def readWindFile(self, file): """ Parsing this file is straight forward: we'll need the timestamp and *scalar* quantities from each row. """ # read cleo forecast (ground) print 'Process cleo forecast data (ground) ...', file f = open(file, 'r') lines = f.readlines() header = lines[0] assert header.strip() == windFileHeader.strip() for line in lines[1:]: row = line.split(' ') timestamp = TimeAgent.hour(TimeAgent.mjd2dt(float(row[0]))) #mjd self.data[timestamp] = {} # what forecast type will this be? self.data[timestamp]['forecast_type_id'] = \ self.getForecastTypeIdFromTimestamp(timestamp) self.readWindFileValues(header, timestamp, row) # Note: we'll stop doing this eventually, but for now: # need to insert a corrected wind speed into the DB. speed_mph = self.data[timestamp]['speed_mph'] self.data[timestamp]['speed_ms'] = \ self.dbimport.correctWindSpeed(timestamp , self.mph2mps(speed_mph)) f.close()
def getHrsInDayTime(self, start, end): "Split up given time range by PTCS day and night time hours." dur = TimeAgent.dtDiffHrs(start, end) startDate = date(start.year, start.month, start.day) #rise, set = self.sun.getRiseSet(date1) # cast a wide net: compute the rise and set times for any days # that might be covered by the given time range days = (end - start).days + 2 dayTimes = [] for day in range(days): dt = startDate + timedelta(days = day) dayTimes.append(self.sun.getPTCSRiseSet(dt)) # where does our given time range intersect with day time? ints = AnalogSet.intersects([dayTimes, [(start, end)]]) if len(ints) > 0: # some day time day = 0.0 for intersection in ints: td = intersection[1] - intersection[0] day += TimeAgent.timedelta2frachours(td) # the rest must be night time night = abs(dur - day) else: # our range is all night time. day = 0.0 night = dur return (day, night)
def create_session(sesshun, project, fdata): "Utility method for creating a Sesshun to test." fsestype = fdata.get("type", "open") fobstype = fdata.get("science", "testing") proj_code = fdata.get("pcode", "Test-Project") st = Session_Type.objects.get(type = fsestype) ot = Observing_Type.objects.get(type = fobstype) sesshun.project = project sesshun.session_type = st sesshun.observing_type = ot sesshun.original_id = 0 sesshun.name = fdata.get("name", None) sesshun.frequency = fdata.get("freq", None) sesshun.max_duration = TimeAgent.rndHr2Qtr(float(fdata.get("req_max", 12.0))) sesshun.min_duration = TimeAgent.rndHr2Qtr(float(fdata.get("req_min", 3.0))) sesshun.time_between = fdata.get("between", None) allot = Allotment(psc_time = fdata.get("PSC_time", 0.0) , total_time = fdata.get("total_time", 0.0) , max_semester_time = fdata.get("sem_time", 0.0) , grade = fdata.get("grade", 4.0) ) allot.save() sesshun.allotment = allot status = Status( enabled = get_field(fdata, "enabled", True, bool) , authorized = get_field(fdata, "authorized", True, bool) , complete = get_field(fdata, "complete", True, bool) , backup = get_field(fdata, "backup", True, bool) ) status.save() sesshun.status = status sesshun.save() create_lst_exclusion(sesshun, fdata) proposition = fdata.get("receiver") save_receivers(sesshun, proposition) systemName = fdata.get("coord_mode", "J2000") system = System.objects.get(name = systemName) v_axis = fdata.get("source_v", 0.0) h_axis = fdata.get("source_h", 0.0) target = Target(session = sesshun , system = system , source = fdata.get("source", None) , vertical = v_axis , horizontal = h_axis ) target.save() sesshun.save() return sesshun
def filterPeriodsByDate(start): "Returns the periods within a given month and year." _, day = calendar.monthrange(start.year, start.month) stop = datetime(start.year, start.month, day, 23, 59, 59) ustart = TimeAgent.est2utc(start) ustop = TimeAgent.est2utc(stop) return [p for p in getPeriods() \ if (p.start >= ustart or p.end() > ustart) and p.start < ustop]
def testGetWindowTimes(self): # test wa = WindowAlerts.WindowAlerts() now = datetime(2009, 1, 1) times = wa.getWindowTimes(now) # expected result sch = [(datetime(2009, 4, 6), self.window.end_datetime()) ] bss = [(datetime(2009, 4, 6), datetime(2009, 4, 7, 13, 0))] schHrs = TimeAgent.timedelta2minutes(sch[0][1] - sch[0][0])/60.0 bssHrs = TimeAgent.timedelta2minutes(bss[0][1] - bss[0][0])/60.0 exp = [(self.window , (schHrs , bssHrs , sch , [bss]))] self.assertEquals(exp[0][0], times[0][0]) self.assertEquals(exp[0][1], times[0][1]) # add some blacked out time - 3 days bsStart = datetime(2009, 4, 8) bsEnd = datetime(2009, 4, 11) blackout = create_blackout(project = self.project, start = bsStart, end = bsEnd, repeat = 'Once') bss.insert(0, (bsStart, bsEnd)) bssHrs += 3*24 times = wa.getWindowTimes(now) exp = [(self.window , (schHrs , bssHrs , sch , [bss]))] self.assertEquals(exp[0][0], times[0][0]) self.assertEquals(exp[0][1], times[0][1]) # move the current time forward now = datetime(2009, 4, 9, 12, 15, 0) times = wa.getWindowTimes(now) sch = [(now, self.window.end_datetime()) ] bss = [(now, bsEnd)] schHrs = TimeAgent.timedelta2minutes(sch[0][1] - sch[0][0])/60.0 bssHrs = TimeAgent.timedelta2minutes(bss[0][1] - bss[0][0])/60.0 exp = [(self.window , (schHrs , bssHrs , sch , [bss]))] self.assertEquals(exp[0][0], times[0][0]) self.assertEquals(exp[0][1], times[0][1])
def filterPeriodsByDate(start): "Returns the periods within a given month and year." _, day = calendar.monthrange(start.year, start.month) stop = datetime(start.year, start.month, day, 23, 59, 59) ustart = TimeAgent.est2utc(start) ustop = TimeAgent.est2utc(stop) periods = [] for p in ALL_PERIODS: if (p.start >= ustart or p.end() > ustart) and p.start < ustop: start, stop = normalizePeriodStartStop(p, start) periods.append([p, start, stop]) return periods
def test_getSunSet(self): sun = Sun() # close to the winter solstice dt = date(2011, 12, 25) set = TimeAgent.quarter(sun.getSunSet(dt)) # 22:00 UT self.assertEqual(datetime(2011, 12, 25, 22, 0), set) # close to the summer solstice dt = date(2012, 6, 25) set = TimeAgent.quarter(sun.getSunSet(dt)) # 00:45 UT ON THE NEXT DAY self.assertEqual(datetime(2012, 6, 26, 0, 45), set)
def test_getSunRise(self): sun = Sun() # close to the winter solstice dt = date(2011, 12, 25) rise = TimeAgent.quarter(sun.getSunRise(dt)) # 12:30 UT self.assertEqual(datetime(2011, 12, 25, 12, 30), rise) # close to the summer solstice dt = date(2012, 6, 25) rise = TimeAgent.quarter(sun.getSunRise(dt)) # 10 UT self.assertEqual(datetime(2012, 6, 25, 10, 0), rise)
def normalizePeriodStartStop(period, dt): """ Returns the start/stop time for a period ensuring it stays within the given month. """ start = TimeAgent.utc2est(period.start) if start.month != dt.month: start = datetime(dt.year, dt.month, 1, 0, 0, 0) stop = TimeAgent.utc2est(period.end()) if stop.month != dt.month: _, day = calendar.monthrange(dt.year, dt.month) stop = datetime(dt.year, dt.month, day, 23, 59, 59) return start, stop
def filterPeriodsByDate(start): "Returns the periods (plus lost time) within a given month and year." _, day = calendar.monthrange(start.year, start.month) stop = datetime(start.year, start.month, day, 23, 59, 59) ustart = TimeAgent.est2utc(start) ustop = TimeAgent.est2utc(stop) periods = [] for p in getPeriods(): if (p.start >= ustart or p.end() > ustart) and p.start < ustop: start, stop = normalizePeriodStartStop(p, start) # normalize lost time too! lostTime = (diffHrs(start, stop)/p.duration) * p.accounting.lost_time() periods.append([p, start, stop, lostTime]) return periods
def SetDate(self, date): if date: weekdays = {"0" : "Sun", "1" : "Mon", "2" : "Tue", "3" : "Wed", "4" : "Thu", "5" : "Fri", "6" : "Sat"} months = {"1" : "Jan", "2" : "Feb", "3" : "Mar", "4" : "Apr", "5" : "May", "6" : "Jun", "7" : "Jul", "8" : "Aug", "9" : "Sep", "10" : "Oct", "11" : "Nov", "12" : "Dec"} self.date = "%s, %s %s %s -0%d00" % (weekdays[date.strftime("%w")], date.strftime("%d"), months[str(int(date.strftime("%m")))], date.strftime("%Y %H:%M:%S"), TimeAgent.utcoffset()) else: self.date = None
def test_lstInRange(self): # ra to lst: rads to hours lst = TimeAgent.rad2hr(self.sesshun.target.horizontal) # create the window range utcStart = datetime(2009, 6, 1) utcEnd = datetime(2009, 6, 2) wr = WindowRange(window = self.w , start_date = utcStart , duration = (utcEnd - utcStart).days) wr.save() # any target should be in range, w/ out a buffer inRange = wr.lstInRange(lst) self.assertEquals(True, inRange) # but with a big enough buffer, no target can be in range buffer = 12.0 inRange = wr.lstInRange(lst, buffer = buffer) self.assertEquals(False, inRange) # make the buffer reasonable enough, and it passes again buffer = 4.5 inRange = wr.lstInRange(lst, buffer = buffer) self.assertEquals(True, inRange)
def test_summary(self): # setup a period for a month before lastMonth = self.start - timedelta(days = 31) lastMonthEst = TimeAgent.utc2est(lastMonth) pa = Period_Accounting(scheduled = self.dur) pa.save() p = Period( session = self.s , start = lastMonth , duration = self.dur , state = Period_State.get_state("S") , accounting = pa ) p.save() # get - sets up the form response = self.get('/schedule/summary') self.failUnlessEqual(response.status_code, 200) startStr = datetime.strftime(lastMonthEst, "%H:%M") self.assertTrue(startStr in response.content) self.assertTrue("mikes awesome project" in response.content) # post - they've requested something # first, the schedule summary report # default to last month response = self.post('/schedule/summary', {'summary' : 'schedule'}) self.failUnlessEqual(response.status_code, 200) self.assertTrue(startStr in response.content) self.assertTrue("mikes awesome project" in response.content) # now do an earlier month, and our period should not show up response = self.post('/schedule/summary' , {'summary' : 'schedule' , 'month' : 'December' , 'year' : '2000'} ) self.failUnlessEqual(response.status_code, 200) self.assertTrue(startStr not in response.content) self.assertTrue("mikes awesome project" not in response.content) # okay, now test the project summary report pcode = self.s.project.pcode response = self.post('/schedule/summary' , {'summary' : 'project' , 'project' : pcode} ) self.failUnlessEqual(response.status_code, 200) self.assertTrue("GBT Project Summary for" in response.content) self.assertTrue(pcode in response.content) self.assertTrue(str(self.dur) in response.content) # make sure we can handle all projects response = self.post('/schedule/summary' , {'summary' : 'project' , 'project' : None} ) self.failUnlessEqual(response.status_code, 200) self.assertTrue("GBT Project Summary for" in response.content) self.assertTrue(pcode in response.content) self.assertTrue(str(self.dur) in response.content)
def test_getPTCSRiseSet(self): sun = Sun() dt = date(2011, 12, 25) rise, set = sun.getPTCSRiseSet(dt) self.assertEqual(datetime(2011, 12, 25, 12,30) , TimeAgent.quarter(rise)) self.assertEqual(datetime(2011, 12, 26, 1, 0) , TimeAgent.quarter(set)) # close to the summer solstice dt = date(2012, 6, 25) rise, set = sun.getPTCSRiseSet(dt) self.assertEqual(datetime(2012, 6, 25, 10, 0) , TimeAgent.quarter(rise)) self.assertEqual(datetime(2012, 6, 26, 3, 45) , TimeAgent.quarter(set))
def getWindowTimeBlackedOut(self): "How many hours in this window have been blacked out?" bs = [] for wr in self.ranges(): bstart = wr.start_datetime() bend = wr.end_datetime() bs.extend(self.session.project.get_blackout_times(bstart, bend)) return sum([TimeAgent.timedelta2minutes(b[1] - b[0])/60.0 \ for b in bs])
def getWindowTimeNotSchedulable(self, blackouts = True): "How many hours in this window are not schedulable?" ns = [] for wr in self.ranges(): ns.extend(self.session.get_time_not_schedulable( \ wr.start_datetime() , wr.end_datetime() , blackouts = blackouts)) return sum([TimeAgent.timedelta2minutes(n[1] - n[0])/60.0 \ for n in ns])
def getSessionTable(self, periods): table = "Start (ET) | UT | LST | (hr) | T | S | PI | Rx | Session\n" table += "--------------------------------------------------------------------------------------\n" for p in periods: if p.session.project.pcode == "Maintenance": pi = "" else: pi = p.session.project.principal_investigator().last_name[:9] if p.session.project.principal_investigator() else "Unknown" table += "%s | %s | %s | %5s | %s | %s | %-9s | %-9s | %s\n" % ( TimeAgent.utc2est(p.start).strftime('%b %d %H:%M') # start (ET) , p.start.strftime('%b %d %H:%M') # start (UT) , TimeAgent.dt2tlst(p.start).strftime('%H:%M') # LST , "%2.2f" % p.duration # dur (Hrs) , p.session.session_type.type[0].upper() # sess type , p.state.abbreviation # state , pi , p.session.receiver_list_simple()[:9] , p.session.name ) return table
def getHrsInGC(self, start, end): "Split up given time range by Galactic Center overlap." dur = TimeAgent.dtDiffHrs(start, end) # convert local time range to LST range lstStart = sla.Absolute2RelativeLST(start) lstEnd = sla.Absolute2RelativeLST(end) # be simplistic about the overalp if lstEnd < lstStart: lstEnd += 24.0 gcHrs, nonGcHrs = self.findOverlap((lstStart, lstEnd), self.gcHrs, dur) return (gcHrs, nonGcHrs)
def getDowntime(periods, month): "This does not use getTime because lost time must be handled carefully" ps = filterPeriods(periods, 'p.session.project.is_science()') ps.sort(key = lambda x: x.start) total = 0.0 for p in ps: start, stop = normalizePeriodStartStop(p, month) hrs = TimeAgent.timedelta2frachours(stop - start) # We must normalize the lost time as well lostTime = (hrs/p.duration) * p.accounting.lost_time() total += lostTime return total
def lstOutOfRange(self): """ Are any of the window ranges just one day, with the LST such that the session can't be scheduled? """ lst = TimeAgent.rad2hr(self.session.target.horizontal) # how close can the lst be to the edges of the range? buffer = (self.session.min_duration + self.session.max_duration) / 2.0 return [wr for wr in self.windowrange_set.all() \ if wr.duration == 1 and \ not wr.lstInRange(lst, buffer = buffer)]
def readAtmoFile(self, file): """ Parsing this file is more complicated, because each row contains *vector* quantities. """ # read cleo forecast (atmosphere) print 'Process cleo forecast data (atmosphere) ... ', file f = open(file, 'r') lines = f.readlines() header = lines[0] assert header.strip() == freqFileHeader.strip() first = lines[0].split(' ') lines = lines[1:] for line in lines: row = line.split(' ') timestamp = TimeAgent.hour(TimeAgent.mjd2dt(float(row[0]))) #mjdt1 if not self.data.has_key(timestamp): self.reportLine("ERROR: No wind data for %s\n" % timestamp) continue # frequencies self.data[timestamp]['freqs'] = [] # OpacityTime<freq>List_avrg self.data[timestamp]['tauCleo'] = [] # TsysTime<freq>List_avrg self.data[timestamp]['tSysCleo'] = [] # TatmTime<freq>List_avrg self.data[timestamp]['tAtmCleo'] = [] num = self.numAtmoFreqs for i in range(num): #print first[ifreq+1], first[ifreq+51], first[ifreq+101] self.data[timestamp]['freqs'].append(self.atmoFreqs[i]) self.data[timestamp]['tauCleo'].append(float(row[i + 1])) self.data[timestamp]['tSysCleo'].append(float(row[i + num + 1])) self.data[timestamp]['tAtmCleo'].append( float(row[i + (num * 2) + 1])) f.close()
def test_getRiseSet(self): sun = Sun() dt = date(2011, 12, 25) rise, set = sun.getRiseSet(dt) # 12:30 UT self.assertEqual(datetime(2011, 12, 25, 12,30) , TimeAgent.quarter(rise)) self.assertEqual(datetime(2011, 12, 25, 22, 0) , TimeAgent.quarter(set)) # close to the summer solstice dt = date(2012, 6, 25) rise, set = sun.getRiseSet(dt) # 00:45 UT ON THE NEXT DAY self.assertEqual(datetime(2012, 6, 25, 10, 0) , TimeAgent.quarter(rise)) self.assertEqual(datetime(2012, 6, 26, 0, 45) , TimeAgent.quarter(set)) # close to the summer solstice dts = (date(2012, 8, 1) , date(2012, 8, 2) , date(2012, 8, 3) , date(2013, 1, 29) , date(2013, 1, 30) ) expected = ((datetime(2012, 8, 1, 10, 21, 31), datetime(2012, 8, 2, 0, 30, 12)) , (datetime(2012, 8, 2, 10, 22, 24), datetime(2012, 8, 3, 0, 29, 12)) , (datetime(2012, 8, 3, 10, 23, 16), datetime(2012, 8, 4, 0, 28, 10)) , (datetime(2013, 1, 29, 12, 26, 38), datetime(2013, 1, 29, 22, 38, 52)) , (datetime(2013, 1, 30, 12, 25, 49), datetime(2013, 1, 30, 22, 40, 0)) ) for dt, (exp_rise, exp_set) in zip(dts, expected): rise, set = sun.getRiseSet(dt) self.assertEqual(exp_rise, rise) self.assertEqual(exp_set, set)
def readAtmoFile(self, file): """ Parsing this file is more complicated, because each row contains *vector* quantities. """ # read cleo forecast (atmosphere) print 'Process cleo forecast data (atmosphere) ... ', file f = open(file, 'r') lines = f.readlines() header = lines[0] assert header.strip() == freqFileHeader.strip() first = lines[0].split(' ') lines = lines[1:] for line in lines: row = line.split(' ') timestamp = TimeAgent.hour(TimeAgent.mjd2dt(float(row[0]))) #mjdt1 if not self.data.has_key(timestamp): self.reportLine("ERROR: No wind data for %s\n" % timestamp) continue # frequencies self.data[timestamp]['freqs'] = [] # OpacityTime<freq>List_avrg self.data[timestamp]['tauCleo'] = [] # TsysTime<freq>List_avrg self.data[timestamp]['tSysCleo'] = [] # TatmTime<freq>List_avrg self.data[timestamp]['tAtmCleo'] = [] num = self.numAtmoFreqs for i in range(num): #print first[ifreq+1], first[ifreq+51], first[ifreq+101] self.data[timestamp]['freqs'].append(self.atmoFreqs[i]) self.data[timestamp]['tauCleo'].append(float(row[i+1])) self.data[timestamp]['tSysCleo'].append(float(row[i+num+1])) self.data[timestamp]['tAtmCleo'].append(float(row[i+(num*2)+1])) f.close()
def getStates(self, periodVersions): "What is the state history: a list of (dates, current state of period)?" states = [] currentState = None for v in periodVersions: state = v.field_dict.get("state", None) if state is not None: currentState = state # the revision library saves things in ET - but we deal in UT if not self.test: dt = TimeAgent.est2utc(v.revision.date_created) else: # why make setting up the tests anymore complicated? dt = v.revision.date_created state = (dt, currentState) if state not in states: states.append(state) return states
def getSessionTable(self, periods): table = "Start (ET) | UT | LST | (hr) | PI | Rx | Session\n" table += "------------------------------------------------------------------------------------\n" for p in periods: if p.session.project.pcode == "Maintenance": pi = "" else: pi = p.session.project.principal_investigator().last_name[:9] if p.session.project.principal_investigator() else "Unknown" table += "%s | %s | %s | %5s | %-9s | %-9s | %s\n" % ( self.utc2estDT(p.start) , p.start.strftime('%b %d %H:%M') , TimeAgent.dt2tlst(p.start).strftime('%H:%M') , "%2.2f" % p.duration , pi , p.session.receiver_list_simple()[:9] , p.session.name ) return table
def test_lstOutOfRange(self): # ra to lst: rads to hours lst = TimeAgent.rad2hr(self.sesshun.target.horizontal) # this first window should not have a problem, since # duration > 1 day self.assertEquals(False, self.w.hasLstOutOfRange()) self.assertEquals(False, self.w.hasNoLstInRange()) # now create a one day window range utcStart = datetime(2009, 6, 1) utcEnd = datetime(2009, 6, 2) wr = WindowRange(window = self.w , start_date = utcStart , duration = (utcEnd - utcStart).days) wr.save() # any target should be in range, w/ out a big buffer self.assertEquals(False, self.w.hasLstOutOfRange()) self.assertEquals(False, self.w.hasNoLstInRange()) # now, increase the buffer: self.sesshun.min_duration = 12.0 self.sesshunmax_duration = 12.0 self.sesshun.save() self.assertEquals(True, self.w.hasLstOutOfRange()) self.assertEquals([wr], self.w.lstOutOfRange()) self.assertEquals(False, self.w.hasNoLstInRange()) # now, shrink the original window range so that it # is too small as well self.wr1.duration = 1 self.wr1.save() self.assertEquals(True, self.w.hasLstOutOfRange()) self.assertEquals(True, self.w.hasNoLstInRange())
def adjustForLstDrift(self, dts): """ Assuming the first datetime is the target LST, adjust all datetimes to be on the same LST (when they are on different dates. Finally, make sure adjusted dates fall on quarter boundaries. """ if len(dts) == 0: return [] # what's the target LST? start = dts[0] lst = sla.Absolute2RelativeLST(start) # make sure each datetime stays on this lst adjusted = [start] for dt in dts[1:]: newDt = sla.RelativeLST2AbsoluteTime(lst, dt) if newDt > dt: dt2 = dt - timedelta(days = 1) newDt = sla.RelativeLST2AbsoluteTime(lst, dt2) adjusted.append(TimeAgent.quarter(newDt)) return adjusted
def gbt_schedule(request, *args, **kws): """ Serves up a GBT schedule page tailored for Operations. Note: This view is in either ET or UTC, database is in UTC. """ def cleanSD(startDate): try: return datetime.strptime(startDate, '%m/%d/%Y') if startDate else datetime.now() except: # Bad input? return datetime.now() timezones = ['ET', 'UTC'] # Note: we probably should have better error handling here, # but since the forms are Date Pickers and drop downs, it seems # difficult for the user to send us malformed params. # Default date, days, and timezone. Loaded from the values # saved below, or from defaults if no values were saved. if request.method == 'POST': startDate, days, timezone = (None, 5, 'ET') else: startDate, days, timezone, _ = _get_calendar_defaults(request) data = request.POST if request.method == 'POST' else request.GET timezone = data.get('tz', timezone) days = int(data.get('days', days)) startDate = cleanSD(data.get('start', startDate)) start = TimeAgent.truncateDt(startDate) end = start + timedelta(days = days) # save these values for use in 'GET' above. _save_calendar_defaults(request, start, days, timezone) requestor = get_requestor(request) supervisor_mode = True if (requestor in get_rescal_supervisors()) else False schedule = get_gbt_schedule_events(start, end, timezone) try: tzutc = Schedule_Notification.objects.latest('date').date.replace(tzinfo=UTC) pubdate = tzutc.astimezone(EST) except: pubdate = None printerFriendly = data.get('printerFriendly', None) template = 'users/schedules/schedule_friendly.html' if printerFriendly == '1' \ else 'users/schedule.html' return render_to_response( template, {'calendar' : schedule, 'day_list' : range(1, 32), 'tz_list' : timezones, 'timezone' : timezone, 'today' : datetime.now(EST), 'start' : start, 'startFmt' : start.strftime('%m/%d/%Y'), 'days' : days, 'rschedule' : Receiver_Schedule.extract_schedule(start, days), 'requestor' : requestor, 'supervisor_mode' : supervisor_mode, 'pubdate' : pubdate, })
def summary(request, *args, **kws): """ Serves up a page that allows Operations to run reconcilation reports. There are two basic reports - schedule and project. Even though it is specifically for Operations, any logged in user may view it. """ now = datetime.now() psummary = [] if request.method == 'POST': summary = request.POST.get('summary', 'schedule') project = project_search(request.POST.get('project', '')) if isinstance(project, list) and len(project) == 1: project = project[0].pcode else: project = '' month = request.POST.get('month', None) year = request.POST.get('year', None) year = int(year) if year else None if month and year: start = datetime(int(year) , [m for m in calendar.month_name].index(month) , 1) else: # Default to this month start = datetime(now.year, now.month, 1) month = calendar.month_name[start.month] year = start.year else: # Default to this month summary = 'schedule' project = '' start = datetime(now.year, now.month, 1) month = calendar.month_name[start.month] year = start.year end = datetime(start.year , start.month , calendar.monthrange(start.year, start.month)[1]) + \ timedelta(days = 1) # View is in ET, database is in UTC. Only use scheduled periods. periods = Period.in_time_range(TimeAgent.est2utc(start) , TimeAgent.est2utc(end)) if project: periods = [p for p in periods if p.isScheduled() and p.session.project.pcode == project] # Handle either schedule or project summaries. if summary == "schedule": schedule = get_gbt_schedule_events(start, end, "ET", True) url = 'users/schedule_summary.html' projects = [] receivers = {} days = {} hours = {} summary = {} else: url = 'users/project_summary.html' projects = list(set([p.session.project for p in periods])) projects.sort(lambda x, y: cmp(x.pcode, y.pcode)) receivers = {} for p in periods: rxs = receivers.get(p.session.project.pcode, []) rxs.extend([r.abbreviation for r in p.receivers.all()]) receivers[p.session.project.pcode] = rxs schedule = {} days = dict([(p.pcode, []) for p in projects]) hours = dict([(p.pcode, 0) for p in projects]) summary = dict([(c, 0) for c in Sesshun.getCategories()]) for p in periods: pstart = TimeAgent.utc2est(p.start) pend = TimeAgent.utc2est(p.end()) # Find the days this period ran within the month. day = pstart.day if pstart >= start else pend.day days[p.session.project.pcode].append(str(day)) if day != pend.day: # For multi-day periods days[p.session.project.pcode].append(str(pend.day)) # Find the duration of this period within the month. duration = min(pend, end) - max(pstart, start) hrs = duration.seconds / 3600. + duration.days * 24. hours[p.session.project.pcode] += hrs # Tally hours for various categories important to Operations. summary[p.session.getCategory()] += hrs # If just for one project, create a more detailed summary. if project: psummary.append((pstart, hrs, p.receiver_list)) return render_to_response( url , {'calendar' : schedule , 'projects' : [(p , sorted(list(set(receivers[p.pcode]))) , sorted(list(set(days[p.pcode])) , lambda x, y: cmp(int(x), int(y))) , hours[p.pcode]) for p in projects] , 'start' : start , 'months' : calendar.month_name , 'month' : month , 'years' : [y for y in xrange(2009, now.year + 2, 1)] , 'year' : year , 'summary' : [(t, summary[t]) for t in sorted(summary)] , 'project' : project , 'psummary' : psummary , 'is_logged_in': request.user.is_authenticated()})
def test_get_time_not_schedulable(self): "Test a number of overlapping bad things" # First bad thing: a receiver schedule that leaves out our rx # Schedule = 4/01/2009: 450, 600, 800 # 4/06/2009: 600, 800, 1070 # 4/11/2009: 800, 1070, 1_2 # 4/16/2009: 1070, 1_2, 2_3 # 4/21/2009: 1_2, 2_3, 4_6 # 4/26/2009: 2_3, 4_6, 8_10 # 5/01/2009: 4_6, 8_10, 12_18 # 5/06/2009: 8_10, 12_18, 18_26 # 5/11/2009: 12_18, 18_26, 26_40 start = datetime(2009, 4, 1, 0) # April 1 end = datetime(2009, 6, 1, 0) # June 1 rcvr_id = 3 for i in range(9): start_date = start + timedelta(5*i) for j in range(1, 4): rcvr_id = rcvr_id + 1 rs = Receiver_Schedule() rs.start_date = start_date rs.receiver = Receiver.objects.get(id = rcvr_id) rs.save() rcvr_id = rcvr_id - 2 # Now add some receivers to this session SessionHttpAdapter(self.sesshun).save_receivers('L | (X & S)') blackouts = self.sesshun.get_time_not_schedulable(start, end) # No available receivers at these times: exp = [(datetime(2009, 4, 1), datetime(2009, 4, 11)) , (datetime(2009, 5, 1), end) ] expected = set(exp) self.assertEquals(exp, blackouts) # Now add a project w/ prescheduled times. otherproject = Project() pdata = {"semester" : "09A" , "type" : "science" , "total_time" : "10.0" , "PSC_time" : "10.0" , "sem_time" : "10.0" , "grade" : "4.0" , "notes" : "notes" , "schd_notes" : "scheduler's notes" } adapter = ProjectHttpAdapter(otherproject) adapter.update_from_post(pdata) othersesshun = create_sesshun() othersesshun.project = otherproject othersesshun.save() fdata = {'session' : othersesshun.id , 'date' : '2009-04-20' , 'time' : '13:00' , 'duration' : 1.0 , 'backup' : False} otherperiod = Period() adapter = PeriodHttpAdapter(otherperiod) adapter.init_from_post(fdata, 'UTC') otherperiod.state = Period_State.objects.filter(abbreviation = 'S')[0] otherperiod.save() #exp.append((datetime(2009, 4, 20, 13, 0), datetime(2009, 4, 20, 14, 0))) exp = [(datetime(2009, 4, 1), datetime(2009, 4, 11)) , (datetime(2009, 4, 20, 13), datetime(2009, 4, 20, 14)) , (datetime(2009, 5, 1), end) ] blackouts = self.sesshun.get_time_not_schedulable(start, end) self.assertEquals(exp, blackouts) # how much time is that? hrsNotSchedulable = sum([TimeAgent.timedelta2minutes(b[1] - b[0])/60.0\ for b in blackouts]) self.assertEquals(985.0, hrsNotSchedulable) # how does this work when limiting the range? newEnd = datetime(2009, 4, 3) blackouts = self.sesshun.get_time_not_schedulable(start, newEnd) self.assertEquals([(start, newEnd)], blackouts) # extend this with a Project Blackout blackout = create_blackout(project = self.sesshun.project, start = datetime(2009, 4, 18, 12), end = datetime(2009, 4, 23, 12), repeat = 'Once') exp = [(datetime(2009, 4, 1), datetime(2009, 4, 11)) , (datetime(2009, 4, 18, 12), datetime(2009, 4, 23, 12)) , (datetime(2009, 5, 1), end) ] blackouts = self.sesshun.get_time_not_schedulable(start, end) self.assertEquals(exp, blackouts) # test the time available blacked out calculations # Calculate the expected result: # it turns out that the project blackout overlaps with all # schedulable time, except for one hour on 4/20 hrsBlackedOut = (TimeAgent.timedelta2minutes(blackout.getEndDate() - blackout.getStartDate()) / 60.0) - 1.0 totalTime = TimeAgent.timedelta2minutes(end - start) / 60.0 hrsSchedulable = totalTime - hrsNotSchedulable s, b, ss, bb = self.sesshun.getBlackedOutSchedulableTime(start, end) self.assertEquals(hrsSchedulable, s) self.assertEquals(hrsBlackedOut, b) # test it some more, but in different ranges start = datetime(2009, 5, 1) s, b, ss, bb = self.sesshun.getBlackedOutSchedulableTime(start, end) self.assertEquals(0.0, b) start = datetime(2009, 4, 22) end = datetime(2009, 4, 26) totalTime = TimeAgent.timedelta2minutes(end - start) / 60.0 s, b, ss, bb = self.sesshun.getBlackedOutSchedulableTime(start, end) self.assertEquals(totalTime, s) self.assertEquals(36.0, b) # cleanup self.sesshun.receiver_group_set.all().delete()
def testGetWindowTimesNonContigious(self): # test now = datetime(2009, 1, 1) wa = WindowAlerts.WindowAlerts() times = wa.getWindowTimes(now) # expected result sch = [(datetime(2009, 4, 6), self.window.end_datetime()) ] bss = [(datetime(2009, 4, 6), datetime(2009, 4, 7, 13, 0))] schHrs = TimeAgent.timedelta2minutes(sch[0][1] - sch[0][0])/60.0 bssHrs = TimeAgent.timedelta2minutes(bss[0][1] - bss[0][0])/60.0 exp = [(self.window , (schHrs , bssHrs , sch , [bss]))] self.assertEquals(exp[0][0], times[0][0]) self.assertEquals(exp[0][1], times[0][1]) # now split up this window into two ranges w/ out changing result # 4/5 -> 4/12 changes to 4/2 - 4/4 and 4/6 - 4/12 wr1 = self.window.windowrange_set.all()[0] wr1.start_date = datetime(2009, 4, 6) wr1.duration = 6 # days wr1.save() wr2 = WindowRange(window = self.window , start_date = datetime(2009, 4, 2) , duration = 2) wr2.save() wa = WindowAlerts.WindowAlerts() times = wa.getWindowTimes(now) # expected result sch = [(datetime(2009, 4, 6), self.window.end_datetime()) ] bss = [(datetime(2009, 4, 6), datetime(2009, 4, 7, 13, 0))] schHrs = TimeAgent.timedelta2minutes(sch[0][1] - sch[0][0])/60.0 bssHrs = TimeAgent.timedelta2minutes(bss[0][1] - bss[0][0])/60.0 exp = [(self.window , (schHrs , bssHrs , sch , [bss]))] self.assertEquals(exp[0][0], times[0][0]) self.assertEquals(exp[0][1], times[0][1]) # now change the window ranges to affect the result - change the # second range so that there is less scheduable time wr1.start_date = datetime(2009, 4, 7) wr1.duration = 5 wr1.save() wa = WindowAlerts.WindowAlerts() times = wa.getWindowTimes(now) # expected result sch = [(datetime(2009, 4, 7), self.window.end_datetime()) ] bss = [(datetime(2009, 4, 7), datetime(2009, 4, 7, 13, 0))] schHrs = TimeAgent.timedelta2minutes(sch[0][1] - sch[0][0])/60.0 bssHrs = TimeAgent.timedelta2minutes(bss[0][1] - bss[0][0])/60.0 exp = [(self.window , (schHrs , bssHrs , sch , [bss]))]