def immediate_upcoming_event(self, time_delta=15, with_seating_chart=False): # Code for debugging # Turn this boolean to test locally and receive valid event on page load every time test_ev_with_chart = False if test_ev_with_chart: from schedules.models import Event ev = Event.objects.filter(chart__isnull=False)[0] date = ev.date_for_week(3) # calc date from w ev.start_datetime = datetime.combine(date, ev.start) ev.end_datetime = datetime.combine(date, ev.end) return [ev, ] # Actual code starts below schedules = self.active_schedules c_time = datetime.now() delay = timedelta(minutes=time_delta) start_time = c_time + delay end_time = c_time - delay c_term = Term.current_term() weeks = set([int(c_term.term_week_of_date(c_time.date()))]) w_tb = OrderedDict() for schedule in schedules: evs = schedule.events.filter(Q(weekday=c_time.weekday()) | Q(day=c_time.date())).filter(start__lte=start_time, end__gte=end_time) if with_seating_chart: evs = evs.filter(chart__isnull=False) schedule_weeks = set(map(int, schedule.weeks.split(','))) w_tb = EventUtils.compute_prioritized_event_table(w_tb, weeks & schedule_weeks, evs, schedule.priority) # print w_tb return EventUtils.export_event_list_from_table(w_tb)
def events_in_week_list(self, weeks): schedules = self.active_schedules w_tb = OrderedDict() for schedule in schedules: evs = schedule.events.all() w_tb = EventUtils.compute_prioritized_event_table(w_tb, weeks, evs, schedule.priority) # return all the calculated, composite, priority/conflict resolved list of events return EventUtils.export_event_list_from_table(w_tb)
def groupevents_in_week_list(self, weeks): schedules = self.group_schedule w_tb = OrderedDict() # create week table for schedule in schedules: evs = schedule.events.all() weeks = [int(x) for x in schedule.weeks.split(',')] w_tb = EventUtils.compute_prioritized_event_table(w_tb, weeks, evs, schedule.priority) # return all the calculated, composite, priority/conflict resolved list of events return EventUtils.export_event_list_from_table(w_tb)
def events_in_date_range(self, start, end, listOfSchedules=[]): # check for generic group calendar if listOfSchedules: schedules = listOfSchedules else: schedules = self.active_schedules # figure out which weeks are in the date range. c_term = Term.current_term() start_week = c_term.term_week_of_date(start) end_week = c_term.term_week_of_date(end) w_tb = OrderedDict() # for every schedule, filter events to get events in the date range. for schedule in schedules: # create week table for date range that covers more than one week. if end_week - start_week > 0: # covers first week. evs = schedule.events.filter( Q(weekday__gte=start.weekday())).order_by( 'weekday', 'start', 'end') weeks = [start_week] w_tb = EventUtils.compute_prioritized_event_table( w_tb, weeks, evs, schedule.priority) # covers weeks between first and last week. evs = schedule.events.all().order_by('weekday', 'start', 'end') weeks = range(start_week + 1, end_week) w_tb = EventUtils.compute_prioritized_event_table( w_tb, weeks, evs, schedule.priority) # covers last week. evs = schedule.events.filter( Q(weekday__lte=end.weekday())).order_by( 'weekday', 'start', 'end') weeks = [end_week] w_tb = EventUtils.compute_prioritized_event_table( w_tb, weeks, evs, schedule.priority) # create week table for date range that covers only one week. else: evs = schedule.events.filter( weekday__gte=start.weekday(), weekday__lte=end.weekday()).order_by( 'weekday', 'start', 'end') weeks = range(start_week, end_week + 1) w_tb = EventUtils.compute_prioritized_event_table( w_tb, weeks, evs, schedule.priority) # create event list. return EventUtils.export_event_list_from_table(w_tb, start_datetime=start, end_datetime=end)
def form_valid(self, form): new_schedule = form.instance cur_schedule = Schedule.objects.get(id=new_schedule.id) new_set = new_schedule.trainees.all() current_set = cur_schedule.trainees.all() # If the trainee sets are identical, minor schedule update to_add = new_set.exclude(pk__in=current_set) to_delete = current_set.exclude(pk__in=new_set) if not to_add and not to_delete: return super(ScheduleAdminUpdate, self).form_valid(form) for t in to_delete: # trainee cannot be moved off of a schedule if there are rolls for events on that schedule t_events = t.rolls.order_by('event').distinct('event').values_list('event__id', flat=True) if cur_schedule.events.filter(id__in=t_events).exists(): form._errors[NON_FIELD_ERRORS] = ErrorList([u'Trainee(s) cannot be removed from schedule. Split the schedule.']) return super(ScheduleAdminUpdate, self).form_invalid(form) for t in to_add: # trainee cannot be moved onto a schedule if there are rolls for events on a schedule that it will overlap sch_event_set = cur_schedule.events.values('event__id', 'event__start', 'event__end') tr_event_set = t.rolls.order_by('event').distinct('event').values('event__id', 'event__start', 'event__end') for i in tr_event_set: for j in sch_event_set: if EventUtils.time_overlap(i['event__start'], i['event__end'], j['event__start'], j['event__end']): form._errors[NON_FIELD_ERRORS] = ErrorList([u'Trainee(s) cannot be added to schedule. Split the schedule.']) return super(ScheduleAdminUpdate, self).form_invalid(form) return super(ScheduleAdminUpdate, self).form_valid(form)
def get_roll_table_by_type_in_weeks(trainees, monitor, weeks, event_type): ''' Grab all active schedules of trainees and collapse in order of priority. This saves us from recalculated shared schedule common among many trainees, We only need to collapse them once. get_all_schedules_in_weeks_for_trainees(): -------------------------------------------------------- Get all the schedules for all trainees (distinct) in order of inc. priorities collapse_priority_event_trainee_table(): -------------------------------------------------------- Go through all schedules and override conflicting events trainee roster list with trainee list that has higher priority Returns table {ev: set([trainee1, trainee2])} in order of increasing start/end time of ev export_typed_ordered_roll_list(): -------------------------------------------------------- Pull out all remaining events after priority-collapsing of type we are taking roll for Returns {event: Set([trainee1, trainee2]) flip_roll_list(): -------------------------------------------------------- we flip the table Return object {trainee: [Events, ]} ''' t_set = set(trainees) schedules = Schedule.get_all_schedules_in_weeks_for_trainees( weeks, trainees) w_tb = EventUtils.collapse_priority_event_trainee_table( weeks, schedules, t_set) event_trainee_tb = EventUtils.export_typed_ordered_roll_list( w_tb, monitor) if monitor == 'AM': event_trainee_tb = [ ev_ts for ev_ts in event_trainee_tb if ev_ts[0].type == event_type ] return EventUtils.flip_roll_list(event_trainee_tb)
def validate_rolls_to_schedules(schedules, trainee_set, weeks, rolls): roll_ids = [] current_term = Term.current_term() w_tb = EventUtils.collapse_priority_event_trainee_table( weeks, schedules, trainee_set) potential_rolls = rolls.order_by('date', 'event__start') for roll in potential_rolls: key = current_term.reverse_date(roll.date) evs = w_tb[key] if roll.event not in evs or (roll.event in evs and roll.trainee not in evs[roll.event]): roll_ids.append(roll.id) invalid_rolls = Roll.objects.filter(id__in=roll_ids) return invalid_rolls
def get_context_data(self, **kwargs): lJRender = JSONRenderer().render ctx = super(RollsView, self).get_context_data(**kwargs) user = self.request.user trainee = trainee_from_user(user) if self.request.method == 'POST': selected_week = self.request.POST.get('week') event_id = self.request.POST.get('events') event = Event.objects.get(id=event_id) selected_date = event.date_for_week(int(selected_week)) event.date = selected_date event.start_datetime = datetime.combine(event.date, event.start) event.end_datetime = datetime.combine(event.date, event.end) else: selected_date = date.today() selected_week = Event.week_from_date(selected_date) # try; events = trainee.immediate_upcoming_event(with_seating_chart=True) # TODO: - if trainee has no current event load other class that is occuring at the same time if len(events) > 0: event = events[0] else: event = None selected_week = int(selected_week) if event: chart = Chart.objects.filter(event=event).first() if chart: seats = Seat.objects.filter( chart=chart).select_related('trainee') partial = Partial.objects.filter( chart=chart).order_by('section_name') # Get roll with with for current event and today's date roll = Roll.objects.filter(event=event, date=selected_date) # TODO - Add group leave slips individualslips = IndividualSlip.objects.filter(rolls__in=roll, status='A') trainees = Trainee.objects.filter(schedules__events=event) schedules = Schedule.get_all_schedules_in_weeks_for_trainees([ selected_week, ], trainees) w_tb = EventUtils.collapse_priority_event_trainee_table([ selected_week, ], schedules, trainees) t_set = EventUtils.get_trainees_attending_event_in_week( w_tb, event, selected_week) for s in seats: if s.trainee in t_set: s.attending = True else: s.attending = False start_datetime = datetime.combine(selected_date, event.start) end_datetime = datetime.combine(selected_date, event.end) group_slip = GroupSlip.objects.filter( end__gte=start_datetime, start__lte=end_datetime, status='A').prefetch_related('trainees') print group_slip, start_datetime, end_datetime trainee_groupslip = set() for gs in group_slip: trainee_groupslip = trainee_groupslip | set( gs.trainees.all()) ctx['event'] = event ctx['event_bb'] = lJRender(EventWithDateSerializer(event).data) ctx['attendance_bb'] = lJRender( RollSerializer(roll, many=True).data) ctx['individualslips_bb'] = lJRender( IndividualSlipSerializer(individualslips, many=True).data) ctx['trainee_groupslip_bb'] = lJRender( TraineeRollSerializer(trainee_groupslip, many=True).data) ctx['trainees_bb'] = lJRender( TraineeRollSerializer(trainees, many=True).data) ctx['chart'] = chart ctx['chart_bb'] = lJRender( ChartSerializer(chart, many=False).data) ctx['seats'] = seats ctx['seats_bb'] = lJRender( SeatSerializer(seats, many=True).data) ctx['partial'] = partial ctx['partial_bb'] = lJRender( PartialSerializer(partial, many=True).data) ctx['weekdays'] = WEEKDAYS ctx['date'] = selected_date ctx['week'] = selected_week ctx['day'] = selected_date.weekday() # ctx['leaveslips'] = chain(list(IndividualSlip.objects.filter(trainee=self.request.user.trainee).filter(events__term=CURRENT_TERM)), list(GroupSlip.objects.filter(trainee=self.request.user.trainee).filter(start__gte=CURRENT_TERM.start).filter(end__lte=CURRENT_TERM.end))) return ctx
def attendance_report_trainee(request): date_from = datetime.strptime(request.session.get("date_from"), '%m/%d/%Y').date() date_to = datetime.strptime(request.session.get("date_to"), '%m/%d/%Y').date() t_id = int(request.GET["traineeId"]) res = dict() trainee = Trainee.objects.get(pk=t_id) res["trainee_id"] = t_id res["name"] = trainee.lastname + ", " + trainee.firstname res["sending_locality"] = trainee.locality.id res["term"] = trainee.current_term res["team"] = trainee.team.code res["ta"] = trainee.TA.full_name res["gender"] = trainee.gender ct = Term.objects.get(current=True) if date_from < ct.start: date_from = ct.start if date_to > ct.end: date_to = ct.end rolls = Roll.objects.filter( trainee=trainee, date__gte=date_from, date__lte=date_to).exclude(status='P').exclude(event__monitor=None) start_datetime = datetime.combine(date_from, datetime.min.time()) end_datetime = datetime.combine(date_to, datetime.max.time()) group_slips = GroupSlip.objects.filter(status='A', start__gte=start_datetime, end__lte=end_datetime, trainees=trainee).values( 'start', 'end') week_from = ct.reverse_date(date_from)[0] week_to = ct.reverse_date(date_to)[0] weeks = range(week_from, week_to) w_tb = EventUtils.collapse_priority_event_trainee_table( weeks, trainee.active_schedules, [trainee]) count = Counter() for kv in w_tb: for ev, t in w_tb[kv].items(): if ev in count: count[ev] += 1 else: count[ev] = 1 # CALCULATE %TARDY total_possible_rolls_count = max( 1, sum(count[ev] for ev in count if ev.monitor is not None)) tardy_rolls = rolls.exclude( status='A').filter(~(Q(leaveslips__does_not_count=True) & Q(leaveslips__status='A'))) tardy_rolls = rolls_excused_by_groupslips( tardy_rolls, group_slips.filter(does_not_count=True)) res["tardy_percentage"] = str( round(tardy_rolls.count() / float(total_possible_rolls_count) * 100, 2)) + "%" # CALCULATE %CLASSES MISSED possible_class_rolls_count = max( 1, sum(count[ev] for ev in count if ev.monitor == 'AM' and ev.type == 'C')) missed_classes = rolls.filter(event__monitor='AM', event__type='C') # currently counts rolls excused by individual and group slips # comment this part out to not count those rolls # exclude absent rolls excused by individual slips # missed_classes = missed_classes.exclude(leaveslips__status='A') missed_classes = missed_classes.filter(~(Q(leaveslips__does_not_count=True) & Q(leaveslips__status='A'))) # exclude absent rolls excused by group slips missed_classes = rolls_excused_by_groupslips( missed_classes, group_slips.filter(does_not_count=True)) res["classes_missed_percentage"] = str( round(missed_classes.count() / float(possible_class_rolls_count) * 100, 2)) + "%" # CALCULATE %SICKNESS rolls_covered_by_sickness = Roll.objects.filter( trainee=trainee, leaveslips__status='A', leaveslips__type='SICK', date__gte=date_from, date__lte=date_to).distinct() res["sickness_percentage"] = str( round( rolls_covered_by_sickness.count() / float(total_possible_rolls_count) * 100, 2)) + "%" # CALCULATE UNEXCUSED ABSENCES unexcused_absences = rolls.filter(status='A') unexcused_absences = unexcused_absences.exclude(leaveslips__status='A') unexcused_absences = rolls_excused_by_groupslips(unexcused_absences, group_slips) res["unexcused_absences_percentage"] = str( round( unexcused_absences.count() / float(total_possible_rolls_count) * 100, 2)) + "%" stash.append_records(res) return JsonResponse(res)
def get_attendance_record(self, period=None): from leaveslips.models import GroupSlip c_term = Term.current_term() rolls = self.rolls.exclude(status='P').filter( date__gte=c_term.start, date__lte=c_term.end).order_by( 'event', 'date').distinct('event', 'date').prefetch_related('event') ind_slips = self.individualslips.filter(status__in=['A', 'S']) att_record = [] # list of non 'present' events excused_timeframes = [] # list of groupslip time ranges excused_rolls = [] # prevents duplicate rolls def attendance_record(att, start, end, event): return { 'attendance': att, 'start': start, 'end': end, 'event': event, } def reformat(slip): s = str( datetime.combine(slip['rolls__date'], slip['rolls__event__start'])).replace( ' ', 'T') e = str( datetime.combine(slip['rolls__date'], slip['rolls__event__end'])).replace(' ', 'T') return (s, e) group_slips = GroupSlip.objects.filter(trainees=self, status__in=['A', 'S']) rolls = rolls.order_by('event__id', 'date').distinct( 'event__id', 'date') # may not need to order if period is not None: # works without period, but makes calculate_summary really slow p = Period(c_term) start_date = p.start(period) end_date = p.end(period) startdt = datetime.combine(start_date, datetime.min.time()) enddt = datetime.combine(end_date, datetime.max.time()) rolls = rolls.filter( date__gte=start_date, date__lte=end_date) # rolls for current period ind_slips = ind_slips.filter( rolls__in=[d['id'] for d in rolls.values('id')]) group_slips = group_slips.filter(start__lte=enddt, end__gte=startdt) rolls = rolls.values('event__id', 'event__start', 'event__end', 'event__name', 'status', 'date') ind_slips = ind_slips.values('rolls__event__id', 'rolls__event__start', 'rolls__event__end', 'rolls__date', 'rolls__event__name', 'id') excused_timeframes = group_slips.values('start', 'end') # first, individual slips for slip in ind_slips: if slip['rolls__event__id'] is None: continue start, end = reformat(slip) att_record.append( attendance_record('E', start, end, slip['rolls__event__id'])) excused_rolls.append( (slip['rolls__event__id'], slip['rolls__date'])) for roll in rolls: excused = False for excused_roll in excused_rolls: if roll['event__id'] == excused_roll[0] and roll[ 'date'] == excused_roll[ 1]: # Check if roll is excused using the roll's event and the roll's date excused = True break if excused is False: if roll['status'] == 'A': # absent rolls att_record.append( attendance_record( 'A', str(roll['date']) + 'T' + str(roll['event__start']), str(roll['date']) + 'T' + str(roll['event__end']), roll['event__id'])) else: # tardy rolls att_record.append( attendance_record( 'T', str(roll['date']) + 'T' + str(roll['event__start']), str(roll['date']) + 'T' + str(roll['event__end']), roll['event__id'])) # now, group slips for record in att_record: if record['event'] is None: continue if record['attendance'] != 'E': start_dt = parser.parse(record['start']) end_dt = parser.parse(record['end']) for tf in excused_timeframes: if EventUtils.time_overlap(start_dt, end_dt, tf['start'], tf['end']): record['attendance'] = 'E' return att_record