def _get_future_maintenance_dates3(): today = TimeAgent.truncateDt(datetime.now()) mp = Period.objects\ .filter(session__observing_type__type = "maintenance")\ .latest("start") last_date = mp.start week = today - timedelta(today.weekday()) # get the date of the Monday of this week. dates = {} # now loop, one week at a time, until that last date, gathering # all the maintenance periods. Each group will be represented as # a day of the week: 'A' = 0 (Monday), 'B' = 1 (Tuesday), etc. # These dates are then entered into the list of possible future # dates. while week < last_date: groups = Maintenance_Activity_Group.get_maintenance_activity_groups(week) for i in groups: d = str(i.get_start().date()) if not dates.has_key(d): dates[d] = [] dates[d].append(str(i.rank)) week += timedelta(7) return dates
def get_due_date(template): if template.repeat_interval == 30: due_dates = get_monthly_due_dates(template) for i in range(0, 7): dday = self.week + timedelta(days = i) if dday in due_dates: return dday if template.repeat_interval == 7: week = TimeAgent.truncateDt(self.week) start_date = TimeAgent.truncateDt(template.get_start()) diff = timedelta(days = (week - start_date).days % \ template.repeat_interval) return week + timedelta(7) - diff return None
def good_fit(template, mag): # Checks to see if this template wouldn't work better # elsewhere. If so, returns False. If not, returns True. # first, take care of simple cases: repeat = 1, or no # published mags this week, the template is due, and this # is the highest U: if template.repeat_interval == 1: return False if better_fit(t, other_groups_today) else True if len(published_groups_this_week) == 0 \ and is_highest_U(mag): return True if is_P(self): # Here we generate a dictionary to weigh the possible dates that a # repeat event can be substantiated in. The lower the number the better. # A slight preference is given to the date that comes before the due date # over one that comes after, but that's really just for tie-breaking purposes. #dm = {-4: 40, -3: 30, -2: 20, -1: 10, 0: 0, 1: 15, 2: 25, 3: 35, 4: 45} dayLen = 30 dm = [(i,10*abs(i)) for i in range(-dayLen,1)] dm.extend([(i,(10*i)+5) for i in range(1,dayLen)]) dm = dict(dm) today = TimeAgent.truncateDt(self.period.start) p = [mag.period for mag in published_groups_this_week] due_date = get_due_date(template) diff = (today - due_date).days if diff: # doesn't fall on this date. Is this the closest # period though? for j in p: if j != self.period: # check only other periods mod = (TimeAgent.truncateDt(j.start) - due_date).days # Test to see if it's a better fit in # another period. and if so, don't # use here. if dm[mod] < dm[diff]: return False return True return False
def _get_fixed_maint_events(mags, day, timezone): """ _get_fixed_maint_events(mags, day, timezone) Takes a set of maintenance activity groups and returns the one for 'day' if there is a fixed one for 'day'. """ evs = [] day = TimeAgent.truncateDt(day) tomorrow = day + timedelta(1) for mag in mags: if mag.period: # fixed if period is set if ( TimeAgent.truncateDt(mag.get_start(timezone)) == day or TimeAgent.truncateDt(mag.get_end(timezone)) == day ): ev = CalEventFixedMaintenance( mag, mag.get_start(timezone) < day, mag.get_end(timezone) >= tomorrow, True, timezone ) evs.append(ev) return evs
def good_fit(template, mag): # Checks to see if this template wouldn't work better # elsewhere. If so, returns False. If not, returns True. # first, take care of simple cases: repeat = 1, or no # published mags this week, the template is due, and this # is the highest U: if template.repeat_interval == 1: return False if better_fit(t, other_groups_today) else True if len(published_groups_this_week) == 0 \ and is_highest_U(mag): return True if is_P(self): dm = {-4: 40, -3: 30, -2: 20, -1: 10, 0: 0, 1: 15, 2: 25, 3: 35, 4: 45} today = TimeAgent.truncateDt(self.period.start) p = [mag.period for mag in published_groups_this_week] due_date = get_due_date(template) diff = (today - due_date).days if diff: # doesn't fall on this date. Is this the closest # period though? for j in p: if j != self.period: # check only other periods mod = (TimeAgent.truncateDt(j.start) - due_date).days # Test to see if it's a better fit in # another period. and if so, don't # use here. if dm[mod] < dm[diff]: return False return True return False
def _get_future_maintenance_dates3(): today = TimeAgent.truncateDt(datetime.now()) mp = Period.objects\ .filter(session__observing_type__type = "maintenance")\ .latest("start") last_date = mp.start week = today - timedelta(today.weekday()) # get the date of the Monday of this week. dates = {} # now loop, one week at a time, until that last date, gathering # all the maintenance periods. Each group will be represented as # a day of the week: 'A' = 0 (Monday), 'B' = 1 (Tuesday), etc. # These dates are then entered into the list of possible future # dates. while week < last_date: groups = Maintenance_Activity_Group.get_maintenance_activity_groups(week) for i in groups: d = str(i.get_start().date()) if not dates.has_key(d): dates[d] = [] # we want either the start time--in format "HH:MM"--if a # period is assigned, or the rank, if still floating. if (i.period): # t will be in format "HH:MM:SS" t = str(i.get_start(tzname = 'ET').time()) # use only "HH:MM" part of time string dates[d].append(t[0:t.rfind(":")]) else: dates[d].append(str(i.rank)) week += timedelta(7) for i in dates: dates[i].sort() return dates
from users.models import Maintenance_Activity, Maintenance_Activity_Group from scheduler.models import Period from datetime import datetime, timedelta from nell.utilities import TimeAgent from scheduler.models import Period periods = Period.objects.filter(session__observing_type__type = "maintenance")\ .exclude(state__name = "Deleted").order_by("start") for p in periods: if p: mag = Maintenance_Activity_Group() mag.save() mag.period = p mag.week = TimeAgent.truncateDt(p.start - timedelta(p.start.weekday())) mag.deleted = True if p.state.name == 'Deleted' else False mag.save() print "Period %i -> Group %i" % (p.id, mag.id) mas = [m for m in p.maintenance_activity_set.all()] for ma in mas: ma.group = mag ma.period = None ma.save() mags = Maintenance_Activity_Group.objects.all() week = set() for mag in mags: week.add(mag.week)
def check_for_conflicting_resources(self, mas): """ Checks other maintenance activities on the same day to see whether there are maintenance activities whose resource requirements conflict. If it finds any, it appends the resource specification (presented in the same way the summary does) to a list. """ start = TimeAgent.truncateDt(self.get_start()) end = start + timedelta(days = 1) rval = [] for i in range(0, len(mas)): if self.id == mas[i].id: continue else: my_start = self.get_start() my_end = my_start + timedelta(hours = self.duration) other_start = mas[i].get_start() other_end = other_start + timedelta(hours = mas[i].duration) #check 'self' for time intersection with mas[i] if not (my_start >= other_end or my_end <= other_start): my_summary = self.get_resource_summary()[1:-1].split(', ') other_summary = mas[i].get_resource_summary()[1:-1]\ .split(', ') for i in my_summary: if 'T=' in i: for j in other_summary: if 'T=' in j: # both have 'T=' tr = Maintenance_Telescope_Resources.objects\ .filter(rc_code=i[2])[0] if j[2] not in tr.compatibility: rval.append(i) elif 'S=' in i: for j in other_summary: if 'S=' in j: # both have 'S=' sr = Maintenance_Software_Resources.objects\ .filter(rc_code=i[2])[0] if j[2] not in sr.compatibility: rval.append(i) elif 'O=' in i: #get resource 'x' in 'O=x' other = Maintenance_Other_Resources.objects\ .filter(rc_code=i[2])[0] for j in other_summary: # 'None' does not conflict with anything if 'N' not in i: if 'O=' in j \ and j[2] not in other.compatibility: rval.append(i) if 'R=' in j \ and 'R' not in other.compatibility: rval.append(i) if 'B=' in j \ and 'B' not in other.compatibility: rval.append(i) else: #everything else: receivers, backends # R, U, D (for Receiver, Up and Down) are # equivalent. Flag conflicts if any of # these match, i.e. U=600 matches R=600. # Also check against 'O=' not N x = re.match('[RUD]=.', i) for j in other_summary: y = re.match('[RUD]=.', j) if x and y: if i[2:] == j[2:]: rval.append(i) elif i == j: if not 'T=' in i \ and not 'S=' in i and not 'O=' in i: rval.append(i) elif 'O=' in j and 'N' not in j: #get resource 'x' in 'O=x' other = Maintenance_Other_Resources.objects\ .filter(rc_code=j[2])[0] if i[0] not in other.compatibility: rval.append(i) return rval
def _process_activity(request, ma, form): """ Does some processing in common between the add and the edit views. """ # process the returned stuff here... ma.subject = form.cleaned_data['subject'] # The date and time entered into the form will be ET. It # must be converted to UTC. The end-time need not be # converted since a duration is computed and stored in the # database instead of the end time. diffs = {} date = form.cleaned_data['date'] start = datetime(date.year, date.month, date.day, hour = int(form.cleaned_data['time_hr']), minute = int(form.cleaned_data['time_min'])) diffs = _record_diffs('start', ma.get_start('ET') if ma._start else start, start, diffs) ma.set_start(start, 'ET') oldval = ma.duration if form.cleaned_data["end_choice"] == "end_time": end_date = form.cleaned_data['date'] end = datetime(year = end_date.year, month = end_date.month, day = end_date.day, hour = int(form.cleaned_data['end_time_hr']), minute = int(form.cleaned_data['end_time_min'])) delta = end - start ma.duration = delta.seconds / 3600.0 # in decimal hours else: ma.duration = float(form.cleaned_data['end_time_hr']) \ + float(form.cleaned_data["end_time_min"]) / 60.0 diffs = _record_diffs('duration', oldval, ma.duration, diffs) oldval = ma.contacts ma.contacts = form.cleaned_data["responsible"] diffs = _record_diffs('contacts', oldval, ma.contacts, diffs) oldval = ma.location ma.location = form.cleaned_data["location"] diffs = _record_diffs('location', oldval, ma.location, diffs) oldval = ma.telescope_resource trid = form.cleaned_data["telescope"] ma.telescope_resource = Maintenance_Telescope_Resources.objects \ .filter(id = trid)[0] diffs = _record_diffs('telescope', oldval, ma.telescope_resource, diffs) oldval = ma.software_resource srid = form.cleaned_data["software"] ma.software_resource = Maintenance_Software_Resources.objects \ .filter(id = srid)[0] diffs = _record_diffs('software', oldval, ma.software_resource, diffs) oldval = [p for p in ma.other_resources.all()] ma.other_resources.clear() for orid in form.cleaned_data["other_resources"]: other_r = Maintenance_Other_Resources.objects.filter(id = orid)[0] ma.other_resources.add(other_r) diffs = _record_m2m_diffs('other', oldval, ma.other_resources.all(), diffs) oldval = [p for p in ma.receivers.all()] ma.receivers.clear() for rid in form.cleaned_data["receivers"]: rcvr = Receiver.objects.filter(id = rid)[0] ma.receivers.add(rcvr) diffs = _record_m2m_diffs('receivers', oldval, ma.receivers.all(), diffs) if form.cleaned_data["change_receiver"] == True: down_rcvr_id = form.cleaned_data["old_receiver"] up_rcvr_id = form.cleaned_data["new_receiver"] # What is needed is a receiver swap entry that contains our # receivers in the correct order (i.e. a for b, not b for a). # To avoid creating duplicate entries (for instance, repeated # swaps of a for b and b for a), search for an existing one # first. If there is none, then create a new swap pair. mrsg = Maintenance_Receivers_Swap.objects.filter( down_receiver = down_rcvr_id).filter(up_receiver = up_rcvr_id) if len(mrsg) == 0: down_rcvr = Receiver.objects \ .filter(id = form.cleaned_data["old_receiver"])[0] up_rcvr = Receiver.objects \ .filter(id = form.cleaned_data["new_receiver"])[0] mrs = Maintenance_Receivers_Swap(down_receiver = down_rcvr, up_receiver = up_rcvr) mrs.save() else: mrs = mrsg[0] ma.receiver_changes.clear() ma.receiver_changes.add(mrs) else: ma.receiver_changes.clear() oldval = [p for p in ma.backends.all()] ma.backends.clear() for bid in form.cleaned_data["backends"]: be = Backend.objects.filter(id = bid)[0] ma.backends.add(be) diffs = _record_m2m_diffs('backends', oldval, ma.backends.all(), diffs) oldval = ma.description ma.description = form.cleaned_data["description"] diffs = _record_diffs('description', oldval, ma.description, diffs) ma.repeat_interval = int(form.cleaned_data["recurrency_interval"]) if ma.repeat_interval > 0: ma.repeat_end = form.cleaned_data["recurrency_until"] # Normally a maintenance activity comes with a group assigned. # But it is possible to add a maintenance activity without a # group. In this case, assign right period for maintenance # activity, if the activity occurs during a scheduled maintenance # group. If no group, this will remain 'None' if not ma.group: start = TimeAgent.truncateDt(ma._start) end = start + timedelta(days = 1) periods = Period.get_periods_by_observing_type(start, end, "maintenance") for p in periods: if p.isScheduled() and ma._start >= p.start and ma._start < p.end(): g = Maintenance_Activity_Group.objects.filter(period = p) if g.count(): ma.group = g[0] # Now add user and timestamp for modification. Earliest mod is # considered creation. u = get_requestor(request) modifying_user = _get_user_name(u) ma.add_modification(modifying_user) ma.save() # If this is a template, modify all subsequent activities based on # it. if ma.is_repeat_template(): template = ma elif ma.is_future_template(): template = ma.repeat_template else: template = None if template: mas = [m for m in Maintenance_Activity.objects\ .filter(repeat_template = template)\ .filter(_start__gte = ma._start)] # times neet to be carried over as ET so that the underlying # UT will compensate for DST. for i in mas: ma_time = ma.get_start('ET') i_time = i.get_start('ET') start = datetime(i_time.year, i_time.month, i_time.day, ma_time.hour, ma_time.minute) i.copy_data(ma) i.set_start(start, 'ET') i.save() return diffs
def comp_mag(mag): return TimeAgent.truncateDt(mag.get_start()) == date and mag.rank.upper() == rank.upper()
def edit_activity(request, activity_id = None): if request.method == 'POST': form = RCAddActivityForm(request.POST) if form.is_valid(): # process the returned stuff here... ma = Maintenance_Activity.objects \ .filter(id = form.cleaned_data["entity_id"])[0] approved = ma.approved # save approval status; _process_activity will clear this. diffs = _process_activity(request, ma, form) if approved: # Notify supervisor if approved activity is modified supervisors = get_rescal_supervisors() view_url = "http://%s/resourcecal_display_activity/%s/" % (request.get_host(), ma.id) rc_notifier.notify(supervisors, "modified", ma.get_start("ET").date(), view_url, changes = diffs) return HttpResponseRedirect('/schedule/') else: u = get_requestor(request) supervisors = get_rescal_supervisors() supervisor_mode = True if (u in supervisors) else False if request.GET['ActionEvent'] == 'Modify': ma = Maintenance_Activity.objects.filter(id = activity_id)[0] form = _modify_activity_form(ma) elif request.GET['ActionEvent'] == 'ModifyFuture': # In this case we want to go back to the template, and set # its 'future_template' to this one. ma = Maintenance_Activity.objects.filter(id = activity_id)[0] ma.set_as_new_template() form = _modify_activity_form(ma) elif request.GET['ActionEvent'] == 'ModifyAll': today = TimeAgent.truncateDt(datetime.now()) ma = Maintenance_Activity.objects.filter(id = activity_id)[0] start_ma = Maintenance_Activity.objects\ .filter(repeat_template = ma.repeat_template)\ .filter(_start__gte = today)\ .order_by('_start')[0] start_ma.set_as_new_template() form = _modify_activity_form(start_ma) elif request.GET['ActionEvent'] == 'Delete': ma = Maintenance_Activity.objects.filter(id = activity_id)[0] ma.deleted = True ma.save() creator = _get_ma_creator(ma) if creator: recipients = supervisors + [creator] else: recipients = supervisors view_url = "http://%s/resourcecal_display_activity/%s/" % (request.get_host(), ma.id) rc_notifier.notify(recipients, "deleted", ma.get_start("ET").date(), view_url) return HttpResponseRedirect('/schedule/') elif request.GET['ActionEvent'] == 'DeleteFuture': ma = Maintenance_Activity.objects.filter(id = activity_id)[0] ma.repeat_template.repeat_end = ma.get_start('ET').date() ma.repeat_template.save() mas = Maintenance_Activity.objects\ .filter(_start__gte = TimeAgent.truncateDt(ma.get_start()))\ .filter(repeat_template = ma.repeat_template) for i in mas: i.deleted = True i.save() return HttpResponseRedirect('/schedule/') elif request.GET['ActionEvent'] == 'DeleteAll': ma = Maintenance_Activity.objects.filter(id = activity_id)[0] ma.repeat_template.deleted = True ma.repeat_template.save() mas = Maintenance_Activity.objects \ .filter(repeat_template = ma.repeat_template) for i in mas: i.deleted = True i.save() return HttpResponseRedirect('/schedule/') elif request.GET['ActionEvent'] == 'Approve': ma = Maintenance_Activity.objects.filter(id = activity_id)[0] u = get_requestor(request) user = _get_user_name(u) ma.add_approval(user) ma.save() # Record any receiver changes in the receiver schedule table. for i in ma.get_receiver_changes(): start = ma.get_start() rsched = datetime(start.year, start.month, start.day, 16) Receiver_Schedule.change_schedule(rsched, [i.up_receiver], [i.down_receiver]) creator = _get_ma_creator(ma) if creator: view_url = "http://%s/resourcecal_display_activity/%s/" % \ (request.get_host(), ma.id) rc_notifier.notify(creator, "approved", ma.get_start("ET").date(), view_url) return HttpResponseRedirect('/schedule/') elif request.GET['ActionEvent'] == 'Move': rank = request.GET['Rank'] ma = Maintenance_Activity.objects.get(id = activity_id) u = get_requestor(request) user = _get_user_name(u) d = request.GET['Destination'].split('-') date = datetime(int(d[0]), int(d[1]), int(d[2])) group = _get_maintenance_activity_group_by_date(date, rank) if group: ma.group = group # assuming here 1 maintenance period ma.approved = False # per day. UI does not support more ma.add_modification(user) # than this. ma.save() return HttpResponseRedirect('/schedule/') elif request.GET['ActionEvent'] == 'Copy': rank = request.GET['Rank'] ma = Maintenance_Activity.objects.get(id = activity_id) u = get_requestor(request) user = _get_user_name(u) d = request.GET['Destination'].split('-') date = datetime(int(d[0]), int(d[1]), int(d[2])) group = _get_maintenance_activity_group_by_date(date, rank) if group: new_ma = ma.clone(group) new_ma.add_modification(user) new_ma.save() return HttpResponseRedirect('/schedule/') return render_to_response('users/rc_add_activity_form.html', {'form': form, 'supervisor_mode': supervisor_mode, 'add_activity' : False })
def _get_elective_mags(utc_day): mags = [] try: delta = timedelta(days = 7) # get maintenance electives for the date span me = Elective.objects\ .filter(session__observing_type__type = 'maintenance')\ .filter(periods__start__gte = utc_day)\ .filter(periods__start__lt = utc_day + delta)\ .distinct() # don't care about elective with all deleted periods. me = [e for e in me if e.periods.count() > e.deletedPeriods().count()] # need as many floating groups as there are elective # maintenance days. First, count up the maintenance days, # then the groups. If not equal, we must either truncate # the groups or add to them. Finally any groups within # the tally must be marked not deleted (in case they were # deleted earlier), and any outside the tally must be # marked deleted. maintenance_periods = len(me) # When obtaining the maintenance activity groups for this # week, ensure that the query will match the mag's 'week' # field by stripping off the time of 'utc_day'. Otherwise # it may not pick it up. Also we include a generous time # range for 'week' just to make sure time issues don't # crop in and exclude any group. dbmags = Maintenance_Activity_Group.objects\ .filter(week__gte = TimeAgent.truncateDt(utc_day))\ .filter(week__lt = TimeAgent.truncateDt(utc_day) + delta)\ .exclude(rank = 'x') \ .order_by("rank") mags = [mag for mag in dbmags] # these are all ordered by rank now, relabel rank in case # it doesn't start with 'A' ('B', 'C', etc.), or there is # a gap ('A', 'B', 'D', etc.) (would occur if manually # deleted from database, etc., and leaving old rank might # confuse users.) for i in range(0, len(mags)): if mags[i].rank != chr(65 + i): mags[i].rank = chr(65 + i) mags[i].save() # either too many or not enough mags. Deal with it # accordingly... if maintenance_periods != len(mags): # too many; mark the excess as deleted. if len(mags) > maintenance_periods: for i in range(maintenance_periods, len(mags)): mag = mags[i] mag.deleted = True mag.period = None mag.save() # too few; create more to make up the numbers. if len(mags) < maintenance_periods: for i in range(len(mags), maintenance_periods): mag = Maintenance_Activity_Group() mag.week = TimeAgent.truncateDt(utc_day) mag.deleted = False mag.rank = chr(65 + i) mag.save() mags.append(mag) # Mark all mags from 0 to number of maintenance periods as # undeleted. This reuses any previously deleted mags. for i in range(0, maintenance_periods): mag = mags[i] if mag.deleted: mag.deleted = False mag.save() # now make sure scheduled electives get assigned the # correct group. sched_periods = [] for i in me: sched_periods += i.periodsByState('S') # sort periods by start, groups by rank. Makes them easy # to match up. Any previous matchups will be undone. sched_periods.sort(key = lambda x: x.start) mags.sort(key = lambda x: x.rank) def in_query_set(item, qset): for i in qset: if item == i: return True return False for i in range(0, len(mags)): mag = mags[i] if i < len(sched_periods): p = sched_periods[i] # update the period if the mag is not the right # one, or if it has more than one mag, to ensure # just one per period. if not in_query_set(mag, p.maintenance_activity_group_set.all()) \ or p.maintenance_activity_group_set.count() > 1: p.maintenance_activity_group_set.clear() p.maintenance_activity_group_set.add(mag) else: if mag.period: mag.period = None mag.save() except: if settings.DEBUG == True: printException(formatExceptionInfo()) return mags
def get_maintenance_activity_set2(self): """ Returns a set of maintenance activities occuring during this group's duration, in time order. """ unscheduled_maintenance = "Unscheduled Maintenance" if not self.period or self.period.session.name == unscheduled_maintenance: mas = self.maintenance_activity_set.all() else: # To handle repeat maintenance activity objects: period = self.period # Get the templates: repeatQ = models.Q(deleted = False) \ & (models.Q(repeat_interval = 1) \ | models.Q(repeat_interval = 7) \ | models.Q(repeat_interval = 30)) \ & (models.Q(_start__lte = period.end()) \ & models.Q(repeat_end__gte = period.end())) # Get the time period start_endQ = models.Q(start__gte = period.start) \ & models.Q(start__lte = period.end()) today = TimeAgent.truncateDt(period.start) # Get other groups today. They will be used below to see # if any of them is a better fit. We must exclude any # possible emergency maintenance periods: other_groups_today = Maintenance_Activity_Group.objects\ .filter(period__start__gte = today)\ .filter(period__start__lt = today + timedelta(1))\ .exclude(id = self.id) \ .exclude(period__session__name = unscheduled_maintenance) groupQ = models.Q(group = self) dbmas = Maintenance_Activity.objects.filter(groupQ) dbrmas = Maintenance_Activity.objects.filter(repeatQ) mas = [i for i in dbmas if not i.is_repeat_template()] rmas = [i for i in dbrmas] # rmas is the list repeating activity templates that may # apply for this period. We need clones of these to # include in mas. If however there are already clones in # mas, we'll want to skip that template. We will also # skip the template if there is a better candidate # maintenance activity group on this day (by better, a # better match in time, defined by the activity's start # time being within the maintenance activity group's time # span). x = [] for i in rmas: for j in mas: if j.repeat_template == i: x.append(i) for g in other_groups_today: if i.get_start().time() >= g.get_start().time() \ and i.get_start().time() < g.get_end().time(): x.append(i) # Weekly repeats have a problem: what if the repeat falls on a # day that is not a maintenance day? Where should we put it? # One strategy is to examine the maintenance periods from 3 # days in the past to 3 days into the future. if none of # those is more suitable, we keep the weekly activity here. If # there is a tie, we favor the earlier date. This is done by # taking the modulo 7 of start - maintenance_activity.start # and mapping it to the values in 'dm'. Lowest value wins. dm = {4: 30, 5: 20, 6: 10, 0: 0, 1: 15, 2: 25, 3: 35} delta = timedelta(days = 3) today = TimeAgent.truncateDt(period.start) p = Period.get_periods_by_observing_type(today - delta, today + delta, "maintenance") for i in rmas: if i.repeat_interval > 1: start_date = TimeAgent.truncateDt(i.get_start()) diff = (today - start_date).days % i.repeat_interval if diff: # doesn't fall on this date. Is this the closest # period though? if diff > 6: # monthly not due x.append(i) else: # weekly or monthly that is due this week for j in p: if j != period: # check only other periods mod = (j.start.date() \ - start_date.date()).days \ % i.repeat_interval # Test to see if it's a better fit in # another period. and if so, don't # use here. if mod < 7 and dm[mod] < dm[diff]: x.append(i) break # Now that we have a list of templates that are not suitable, # cull the template list: for i in x: if i in rmas: rmas.remove(i) # The remaining templates may be used: for i in rmas: ma = i.clone(self) mas.append(ma) # remove all activities marked deleted. This must be done # after all the above to prevent a replacement being generated # for a deleted activity, for repeat activities. mas = [i for i in mas if not i.deleted] mas.sort(cmp = lambda x, y: cmp(x.get_start(), y.get_start())) return mas
def get_maintenance_activity_set(self): """ Returns a set of maintenance activities occuring during this group's duration, in time order. """ floating_maintenance = u'Maintenance day' fixed_maintenance = u'Maintenance' # We need some functions... def is_P(mag): return True if mag.period and \ mag.period.session.name == fixed_maintenance \ else False def is_U(mag): return True if not mag.period else False def is_highest_U(mag): if not is_U(mag): return False week = mag.week mags = Maintenance_Activity_Group.objects.filter(week = week)\ .filter(period = None).order_by("rank") return mag == mags[0] def already_instantiated(template, mag): # the answer is 'True' if it is in this mag... for j in mag.maintenance_activity_set.all(): if j.repeat_template == template: return True # or, if daily template, in another mag today... if template.repeat_interval == 1: for omag in other_groups_today: for j in omag.maintenance_activity_set.all(): if j.repeat_template == template: return True # or, if weekly or monthly, if it is in another mag this week. if template.repeat_interval == 7 or template.repeat_interval == 30: for omag in other_groups_this_week: for j in omag.maintenance_activity_set.all(): if j.repeat_template == template: return True # Not instantiated. return False def get_monthly_due_dates(template): start_date = template._start.date() end = template.repeat_end dates = [] midnight = time(0, 0, 0) months = 0 ddate = start_date while ddate < end: dates.append(datetime.combine(ddate, midnight)) months = months + 1 ddate = TimeAgent.add_months(start_date, months) return dates def get_due_date(template): if template.repeat_interval == 30: due_dates = get_monthly_due_dates(template) for i in range(0, 7): dday = self.week + timedelta(days = i) if dday in due_dates: return dday if template.repeat_interval == 7: week = TimeAgent.truncateDt(self.week) start_date = TimeAgent.truncateDt(template.get_start()) diff = timedelta(days = (week - start_date).days % \ template.repeat_interval) return week + timedelta(7) - diff return None def template_due(template): # daily and weekly will always be due this week. if template.repeat_interval == 1 or template.repeat_interval == 7: return True return False if not get_due_date(template) else True def good_fit(template, mag): # Checks to see if this template wouldn't work better # elsewhere. If so, returns False. If not, returns True. # first, take care of simple cases: repeat = 1, or no # published mags this week, the template is due, and this # is the highest U: if template.repeat_interval == 1: return False if better_fit(t, other_groups_today) else True if len(published_groups_this_week) == 0 \ and is_highest_U(mag): return True if is_P(self): dm = {-4: 40, -3: 30, -2: 20, -1: 10, 0: 0, 1: 15, 2: 25, 3: 35, 4: 45} today = TimeAgent.truncateDt(self.period.start) p = [mag.period for mag in published_groups_this_week] due_date = get_due_date(template) diff = (today - due_date).days if diff: # doesn't fall on this date. Is this the closest # period though? for j in p: if j != self.period: # check only other periods mod = (TimeAgent.truncateDt(j.start) - due_date).days # Test to see if it's a better fit in # another period. and if so, don't # use here. if dm[mod] < dm[diff]: return False return True return False def better_fit(template, other_mags): for g in other_mags: if template.get_start().time() >= g.get_start().time() \ and template.get_start().time() < g.get_end().time(): return True return False def instantiate(template, mag): ma = template.clone(mag) if is_P(self) or is_U(self): # set up all the data we need: if is_P(self): today = TimeAgent.truncateDt(self.period.start) end = self.period.end() else: today = self.week end = self.week + timedelta(days = 7) # Get repeat templates repeatQ = models.Q(deleted = False) \ & (models.Q(repeat_interval = 1) \ | models.Q(repeat_interval = 7) \ | models.Q(repeat_interval = 30)) \ & (models.Q(_start__lte = end) \ & models.Q(repeat_end__gte = today)) dbrmas = Maintenance_Activity.objects.filter(repeatQ) templates = [p for p in dbrmas] allowed_sessionsQ = models.Q(period__session__name = fixed_maintenance) \ | models.Q(period__session__name = floating_maintenance) if is_P(self): today = TimeAgent.truncateDt(self.period.start) # Get other groups today. They will be used below to see # if any of them is a better fit. We must exclude any # possible emergency maintenance periods: other_groups_today = Maintenance_Activity_Group.objects\ .exclude(id = self.id) \ .filter(period__start__gte = today)\ .filter(period__start__lt = today + timedelta(1))\ .filter(allowed_sessionsQ) else: other_groups_today = [] this_week = self.get_week() published_groups_this_week = Maintenance_Activity_Group.objects\ .exclude(id = self.id) \ .filter(period__start__gte = this_week)\ .filter(period__start__lt = today + timedelta(7))\ .filter(allowed_sessionsQ) unpublished_groups_this_week = Maintenance_Activity_Group.objects\ .exclude(id = self.id) \ .filter(week__gte = this_week)\ .filter(week__lt = today + timedelta(7))\ .filter(period = None) other_groups_this_week = [mag for mag in published_groups_this_week] \ + [mag for mag in unpublished_groups_this_week] # Meat and potatoes: For each template, see if we must # instantiate it. We do so if it is due and hasn't been # taken care of yet. for t in templates: if template_due(t): if not already_instantiated(t, self): if good_fit(t, self): instantiate(t, self) groupQ = models.Q(group = self) dbmas = Maintenance_Activity.objects.filter(groupQ) # remove all activities marked deleted. This must be done # after all the above to prevent a replacement being generated # for a deleted activity, for repeat activities. mas = [i for i in dbmas if not i.deleted and not i.is_repeat_template()] mas.sort(cmp = lambda x, y: cmp(x.get_start(), y.get_start())) return mas
def get_week(self): """ Returns the start-of-week date of the week this activity will take place in. """ return TimeAgent.truncateDt(self.week) if self.week else None