예제 #1
0
파일: Proposal.py 프로젝트: mmccarty/nell
 def remainingTime(self):
     "From this proposal's project's time accounting."
     if self.dss_project is not None:
         ta = TimeAccounting()
         return ta.getTimeLeft(self.dss_project)
     else:
         return None
예제 #2
0
파일: Proposal.py 프로젝트: mmccarty/nell
 def getTime(self, type):
     "Leverage time accounting for this proposal's project."
     if self.dss_project is not None:
         ta = TimeAccounting()
         return ta.getTime(type, self.dss_project)
     else:
         return None
예제 #3
0
파일: Session.py 프로젝트: nrao/nell
 def remainingTime(self):
     "From this session's dss sessions's time accounting."
     if self.dss_session is not None:
         ta = TimeAccounting()
         return ta.getTimeLeft(self.dss_session)
     else:
         return None
예제 #4
0
파일: Proposal.py 프로젝트: mmccarty/nell
 def dssAllocatedTime(self):
     "How much was the corresponding DSS project allocated?"
     if self.dss_project is not None:
         ta = TimeAccounting()
         return ta.getProjectTotalTime(self.dss_project)
     else:
         return None
예제 #5
0
    def setUp(self):
        super(TestProposal, self).setUp()

        # this project has no allotments!
        self.project = DSSProject.objects.order_by('pcode').all()[0]

        # setup some periods
        self.start = datetime(2000, 1, 1, 0)
        self.end   = self.start + timedelta(hours = 12)
        times = [(datetime(2000, 1, 1, 0), 5.0, "one")
               , (datetime(2000, 1, 1, 5), 3.0, "two")
               , (datetime(2000, 1, 1, 8), 4.0, "three")
               ]
        self.ps = []
        state = DSSPeriod_State.objects.get(abbreviation = 'P')
        for start, dur, name in times:
            # each session has grade 4, time = 3 
            s = create_sesshun()
            s.name = name
            s.save()
            pa = DSSPeriod_Accounting(scheduled = dur)
            pa.save()
            p = DSSPeriod( session    = s
                      , start      = start
                      , duration   = dur
                      , state      = state
                      , accounting = pa
                      )
            p.save()
            self.ps.append(p)


        # Okay, now set up the corresponding proposal
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        sqlResult = { 'PROP_ID' : self.project.pcode
                    , 'PROPOSAL_TYPE' : 'Regular'
                    , 'STATUS' : 'Draft'
                    , 'SUBMITTED_DATE' : now 
                    , 'CREATED_DATE' : now 
                    , 'MODIFIED_DATE' : now 
                    , 'TITLE' : 'Lynrd Sknyrd'
                    , 'ABSTRACT' : 'What song do you wanna hear?'
                    , 'proposal_id' : 0
                    , 'JOINT_PROPOSAL_TYPE' : 'Not a joint Proposal'
                    }
        proposal = Proposal.createFromSqlResult(sqlResult)
        proposal.dss_project = self.project
        proposal.setSemester(self.project.semester.semester)
        proposal.save()
        self.proposal = proposal

        self.ta = TimeAccounting()
예제 #6
0
def GenerateReport():

    ta = TimeAccounting()
    ui = UserInfoTools()

    outfile = open("./DssDbHealthReport.txt",'w')

    projects = sorted(Project.objects.all(), lambda x, y: cmp(x.pcode, y.pcode))
    sessions = sorted(Sesshun.objects.all(), lambda x, y: cmp(x.name, y.name))
    periods  = Period.objects.exclude(state__name = 'Deleted').order_by('start')
    rcvrs    = Receiver.objects.order_by("freq_low")
    deleted  = Period_State.get_state('D')

    outfile.write("Projects without sessions:")
    values = [p.pcode for p in projects if len(p.sesshun_set.all()) == 0]
    print_values(outfile, values)

    outfile.write("\n\nSessions without a project:")
    values = [s.name for s in sessions if not s.project]
    print_values(outfile, values)

    outfile.write("\n\nOpen sessions (not completed) with alloted time < min duration:")
    values = [s.name for s in sessions \
              if s.session_type.type == "open" and \
                 not s.status.complete and \
                 s.allotment.total_time < s.min_duration]
    print_values(outfile, values)

    outfile.write("\n\nOpen sessions (not completed) with time left < min duration:")
    values = [s.name for s in sessions \
              if s.session_type.type == "open" and \
                 not s.status.complete and \
                 ta.getTimeLeft(s) < s.min_duration]
    print_values(outfile, values)

    outfile.write("\n\nOpen sessions with max duration < min duration:")
    values = [s.name for s in sessions \
              if s.session_type.type == "open" and \
                 s.min_duration > s.max_duration]
    print_values(outfile, values)

    outfile.write("\n\nSessions with min duration too small:")
    ss1 = [s for s in sessions if s.min_duration <= 0.25]
    ss = sorted(ss1, lambda x, y: cmp(x.min_duration, y.min_duration))
    values = ["%s, %f" % (s.name, s.min_duration) for s in ss]
    print_values(outfile, values)

    outfile.write("\n\nSessions with negative observed time:")
    values = ["%s; obs: %s, total: %s" % \
        (s.name, str(ta.getTime("observed", s)), str(s.allotment.total_time)) \
        for s in sessions if ta.getTime("observed", s) < 0.0]
    print_values(outfile, values)

    outfile.write("\n\nSessions without receivers:")
    values = [s.name for s in sessions \
              if len(s.receiver_list()) == 0 and \
                 s.project.pcode not in ("Maintenance", "Shutdown")]
    print_values(outfile, values)

    outfile.write("\n\nOpen sessions with default frequency 0:")
    values = [s.name for s in sessions \
              if s.session_type.type == "open" and s.frequency == 0.0]
    print_values(outfile, values)

    outfile.write("\n\nSessions with invalid LST Exclusion/Inclusion parameters:")
    values = invalidLSTParameters(sessions)
    print_values(outfile, values)

    outfile.write("\n\nSessions with repeated observing parameters:")
    values = repeatedObservingParameters(sessions)
    print_values(outfile, values)

    #outfile.write("\n\nSessions with unmatched observer parameter pairs:")
    #values = [s.name for s in missing_observer_parameter_pairs(sessions)]
    #print_values(outfile, values)

    outfile.write("\n\nSessions with RA and Dec equal to zero:")
    values = [s.name for s in sessions \
                     if s.getTarget() is not None and s.target.vertical == 0.0 \
                        and s.target.horizontal == 0.0]
    print_values(outfile, values)

    outfile.write("\n\nSessions with frequency (GHz) out of all Rcvr bands")
    values = []
    for s in sessions:
        out_of_band = False
        # freq. must be out of band for ALL rcvrs to be reported
        rcvrs = [Receiver.objects.get(abbreviation = rname) \
            for rname in s.rcvrs_specified()]
        in_bands = [r for r in rcvrs if r.in_band(s.frequency)]
        # don't report sessions w/ out rcvrs: we do that above
        if len(in_bands) == 0 and len(rcvrs) != 0:
            values.append("%s %s %5.4f" % (s.name
                                         , s.receiver_list_simple()
                                         , s.frequency))
    print_values(outfile, values)

    outfile.write("\n\nUpcoming Windowed, Elective, and Fixed Sessions that are not enabled:")
    sa = SessionInActiveAlerts()
    print_values(outfile
               , ["%s session %s which runs %s" % (u.session.session_type.type
                                                 , u.session.id
                                                 , sa.getRange(u))
                   for u in sa.findDisabledSessionAlerts()]
                   )

    outfile.write("\n\nUpcoming Windowed, Elective, and Fixed Sessions that are not authorized:")
    sa = SessionInActiveAlerts()
    print_values(outfile
               , ["%s session %s which runs %s" % (u.session.session_type.type
                                                 , u.session.id
                                                 , sa.getRange(u))
                   for u in sa.findUnauthorizedSessionAlerts()]
                   )

    outfile.write("\n\nProjects without a friend:")
    values = [p.pcode for p in projects if not p.complete and len(p.friend_set.all()) == 0]
    print_values(outfile, values)

    outfile.write("\n\nProjects without any schedulable sessions:")
    values = [p.pcode for p in projects \
              if not p.complete and \
              not any([s.schedulable() for s in p.sesshun_set.all()])]
    print_values(outfile, values)

    outfile.write("\n\nProjects without any observers:")
    values = [p.pcode for p in projects \
              if not p.complete and len(p.get_observers()) == 0]
    print_values(outfile, values)

    outfile.write("\n\nProjects whose project type is inconsistent with its sessions' observing types:")
    values = [p.pcode for p in projects \
                      if p.is_science() and \
                         not all([s.isScience() for s in p.sesshun_set.all()])]
    print_values(outfile, values)

    outfile.write("\n\nProjects whose category is inconsistent with its sessions' categories:")
    badCats = []
    for p in projects:
        cats = list(set([s.getCategory() for s in p.sesshun_set.all()]))
        if len(cats) > 1:
            badCats.append(p.pcode)
        elif len(cats) == 1 and cats[0] != p.get_category():
            badCats.append(p.pcode)
    print_values(outfile, badCats)
    
    outfile.write("\n\nReceiver changes happening on days other than maintenance days:")
    values = [str(s) for s in check_maintenance_and_rcvrs()]
    print_values(outfile, values)

    outfile.write("\n\nPeriods longer then 24 hours:")
    values = Period.objects.filter(duration__gt = 24.0)
    print_values(outfile, values)

    outfile.write("\n\nPeriods in the future whose receivers are different then their sessions.")
    fps = Period.objects.filter(start__gt = datetime.now()).order_by('start')
    # Here we compare what the period currently has, with the function that
    # the period was initialized with 
    bps = [p for p in fps if p.receiver_list_simple() != ", ".join(p.session.rcvrs_specified())] 
    values = ["%s at %s; %s vs. %s" % (p.session.name
                                     , p.start
                                     , p.receiver_list_simple()
                                     , ", ".join(p.session.rcvrs_specified())) \
               for p in bps]
    print_values(outfile, values)

    outfile.write("\n\nPeriods which have been observed when none of their receivers were up:")
    start = datetime(2009, 10, 10) # don't bother looking before this
    end = datetime.utcnow() - timedelta(days=1) # leave a day buffer
    obs_ps = [p for p in periods if p.start > start and p.start < end \
        and p.session.name not in ['Shutdown', 'Maintenance'] \
        and p.state != deleted]
    bad_ps = [p for p in obs_ps if not p.has_observed_rcvrs_in_schedule()]
    values = ["%s, %s" % (p.__str__(), p.receiver_list()) for p in bad_ps]
    print_values(outfile, values)

    outfile.write("\n\nSessions for which periods are scheduled when none of their receivers are up:")
    now = datetime.utcnow()
    future_ps = [p for p in periods if p.start > now \
        and p.session.project.pcode not in ['Shutdown', 'Maintenance']]
    bad_ps = [p for p in future_ps if not p.has_required_receivers()]
    values = ["%s, %s" % (p.__str__(), p.session.receiver_list_simple()) \
        for p in bad_ps]
    print_values(outfile, values)

    # note that here we use the session's rcvrs, *not* the periods'
    # rcvrs because we are looking into the future.
    outfile.write("\n\nPeriods in the future that are using retired hardware:")
    values = [p for p in periods if p.state != deleted and p.start > now and p.session.usesDeletedReceiver()] 
    print_values(outfile, values)

    outfile.write("\n\nProjects that contain non-unique session names:")
    names  = [(p.pcode, [s.name for s in p.sesshun_set.all()])
                                    for p in projects]
    values = [p for p, n in names if len(set(n)) != len(n)]
    print_values(outfile, values)

    outfile.write("\n\nUsers with duplicate accounts by name:")
    values = ui.findUsersWithSameNames()
    print_values(outfile, values)

    outfile.write("\n\nUsers with shared PST accounts:\n")
    sharedPstIds = ui.findUsersWithSamePstId(quiet = True)
    # print out useful format
    if len(sharedPstIds) > 0:
        for pst_id, us in sharedPstIds:
            outfile.write("PST ID: %s\n" % pst_id)
            for u in us:
                outfile.write("    %s, %d\n" % (u, u.id))
                outfile.write("    Projects: %s\n" % u.getProjects())
                outfile.write("    Blackouts: %s\n" % u.blackout_set.all())
    else:
        outfile.write("    None\n")

    outfile.write("\n\nUsers with no PST ID:")
    users  = list(User.objects.order_by("last_name"))
    values = [u for u in users if u.pst_id is None]
    print_values(outfile, values)

    outfile.write("\n\nPeriods Scheduled on blackout dates:")
    values = []
    for s in sessions:
        for p in [p for p in s.period_set.all() if p.start >= datetime.today() \
                                                   and not p.isDeleted()]:
            blackouts = s.project.get_blackout_times(p.start, p.end())
            if blackouts:
                values.append("%s on %s" % (s.name
                                          , p.start.strftime("%m/%d/%Y %H:%M")))
    print_values(outfile, values)

    outfile.write("\n\nElective and Fixed pending periods scheduled on blackout dates:")
    values = []
    opened = Session_Type.objects.get(type="open")
    windowed = Session_Type.objects.get(type="windowed")
    pending = Period_State.objects.get(name="Pending")
    for p in Period.objects \
                   .exclude(session__session_type=opened) \
                   .exclude(session__session_type=windowed) \
                   .filter(start__gte=now) \
                   .filter(state=pending).order_by("start"):
        blackouts = p.session.project.get_blackout_times(p.start, p.end())
        if blackouts:
            values.append("%s" % p)
    print_values(outfile, values)
        

    outfile.write("\n\nOverlapping periods:")
    values = report_overlaps(periods, now = now)
    print_values(outfile, values)


    outfile.write("\n\nGaps in historical schedule:")
    ps = Period.objects.filter(start__lt = now)\
             .filter(~Q(state__abbreviation = 'D')).order_by("start")
    values = []
    previous = ps[0]
    for p in ps[1:]:
        # periods should be head to tail 
        if p.start != previous.end() and p.start > previous.end():
            values.append("Gap between: %s and %s" % (previous, p))
        previous = p
    print_values(outfile, values)

    outfile.write("\n\nPeriods with time billed exceeding duration:")
    values  = [p.__str__() for p in periods if p.duration < p.accounting.time_billed()]
    print_values(outfile, values)

    outfile.write("\n\nPeriods with non-positive durations:")
    values  = [p for p in periods if p.duration <= 0.]
    print_values(outfile, values)

    outfile.write("\n\nPeriods with negative observed times:")
    values  = [str(p) for p in periods if p.accounting.observed() < 0.]
    print_values(outfile, values)

    outfile.write("\n\nDeleted Periods with positive observed times:")
    ps = [p for p in periods if p.state.abbreviation == 'D']
    values  = [str(p) for p in ps if p.accounting.observed() > 0.]
    print_values(outfile, values)

    outfile.write("\n\nDeleted and Scheduled Periods for non-Windowed Sessions with non-positive scheduled time:")
    ps = [p for p in periods if p.state.abbreviation in ['D', 'S'] and p.session.session_type.type != 'windowed']
    values  = [p for p in ps if p.accounting.scheduled <= 0.0]
    print_values(outfile, values)

    outfile.write("\n\nPending Periods (non-windowed, non-elective):")
    values  = [str(p) for p in periods if p.isPending() and not p.is_windowed() and not p.is_elective()]
    print_values(outfile, values)

    outfile.write("\n\nCompleted projects with non-complete sessions:")
    print_values(outfile, get_closed_projets_with_open_sessions())

    outfile.write("\n\nSessions with wrong number of targets (!= 1):")
    values = sessions_with_null_or_multiple_targets()
    print_values(outfile, values)

    outfile.write("\n\nSessions with frequency == NULL:")
    print_values(outfile, Sesshun.objects.filter(frequency = None))

    outfile.write("\n\nSessions with NULL RA and/or DEC:")
    values = sessions_with_bad_target()
    print_values(outfile, values)

    #    * (new) Incomplete electives with insufficient opportunities:
    #* Electives:
    #      o Elective sessions with no electives:
    #      o Electives with no opportunities:
    #      o Non-elective sessions with electives assigned: 

    outfile.write("\n\nElective Sessions with no Electives:")
    values = elective_sessions_no_electives()
    print_values(outfile, values)
    
    outfile.write("\n\nNon-Elective Sessions with Electives:")
    values = non_elective_sessions_electives()
    print_values(outfile, values)
    
    outfile.write("\n\nElectives with no Opportunities (Periods):")
    values = electives_no_periods()
    print_values(outfile, values)

    outfile.write("\n\nPeriods from Elective Session not in an Elective:")
    ps = periods_no_elective()
    print_values(outfile, [str(p) for p in ps])

    output_windows_report(outfile)
예제 #7
0
def get_obs_hours(sessions, typ):
    ta = TimeAccounting()
    return sum([ta.getTime("observed", s) for s in get_sessions(session, typ)])
예제 #8
0
class TestProposal(TestCase):

    fixtures = ['scheduler.json', 'proposal_GBT12A-002']

    def setUp(self):
        super(TestProposal, self).setUp()

        # this project has no allotments!
        self.project = DSSProject.objects.order_by('pcode').all()[0]

        # setup some periods
        self.start = datetime(2000, 1, 1, 0)
        self.end   = self.start + timedelta(hours = 12)
        times = [(datetime(2000, 1, 1, 0), 5.0, "one")
               , (datetime(2000, 1, 1, 5), 3.0, "two")
               , (datetime(2000, 1, 1, 8), 4.0, "three")
               ]
        self.ps = []
        state = DSSPeriod_State.objects.get(abbreviation = 'P')
        for start, dur, name in times:
            # each session has grade 4, time = 3 
            s = create_sesshun()
            s.name = name
            s.save()
            pa = DSSPeriod_Accounting(scheduled = dur)
            pa.save()
            p = DSSPeriod( session    = s
                      , start      = start
                      , duration   = dur
                      , state      = state
                      , accounting = pa
                      )
            p.save()
            self.ps.append(p)


        # Okay, now set up the corresponding proposal
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        sqlResult = { 'PROP_ID' : self.project.pcode
                    , 'PROPOSAL_TYPE' : 'Regular'
                    , 'STATUS' : 'Draft'
                    , 'SUBMITTED_DATE' : now 
                    , 'CREATED_DATE' : now 
                    , 'MODIFIED_DATE' : now 
                    , 'TITLE' : 'Lynrd Sknyrd'
                    , 'ABSTRACT' : 'What song do you wanna hear?'
                    , 'proposal_id' : 0
                    , 'JOINT_PROPOSAL_TYPE' : 'Not a joint Proposal'
                    }
        proposal = Proposal.createFromSqlResult(sqlResult)
        proposal.dss_project = self.project
        proposal.setSemester(self.project.semester.semester)
        proposal.save()
        self.proposal = proposal

        self.ta = TimeAccounting()

    def tearDown(self):
        super(TestProposal, self).tearDown()

        for p in self.ps:
            s = p.session
            p.delete()
            s.delete()

    # TBF: stick this in a utility somewhere        
    def createSession(self, p):
        "Create a new session for the tests"
        return createSession(p)

    def test_requestedTime(self):

        p = Proposal.objects.get(pcode = 'GBT09A-001')
        self.assertEqual(0.0, p.requestedTime())
        # now get some requested time
        self.createSession(p)
        self.assertEqual(7.0, p.requestedTime())
        
    def test_timeAccounting(self):

        self.assertEqual(12.0, self.ta.getTime('time_billed', self.project))
        self.assertEqual(12.0, self.proposal.billedTime())

        self.assertEqual(12.0, self.ta.getTime('scheduled', self.project))
        self.assertEqual(12.0, self.proposal.scheduledTime())

        self.assertEqual(-12.0, self.ta.getTimeRemaining(self.project))
        self.assertEqual(-12.0, self.proposal.remainingTime())

    def test_createFromSqlResult(self):

        sqlResult = {'SUPPORTED': u'\x00', 'FIRST_NAME': u'Carl', 'LAST_NAME': u'Gwinn', 'EMAIL': u'*****@*****.**', 'OLD_PI': u'None', 'TITLE': u'Substructure in the Scattering Disk of Sgr A*', 'RELATED_PROPOSALS': u'', 'ASSIGNMENT': u'None', 'ABSTRACT': u'We propose observations to detect, or set limits on, substructure within the smooth scattering disk of the celebrated galactic center radio source SgrA*.  Such structure is predicted theoretically, and it may have been detected in observations of pulsars with RadioAstron, and in earlier observations of SgrA*. Results will place constraints on the spatial spectrum of the electron-density fluctuations that are responsible for the scattering.', 'CREATED_DATE': u'2013-01-26 18:18:54', 'contact_id': u'33166', 'JOINT_PROPOSAL_TYPE': u'Joint with GBT and VLA', 'OLD_CONTACT': u'None', 'DEADLINE_DATE': u'2013-02-01 22:30:00', 'PROCESSED': u'\x00', 'category2_id': u'None', 'STAFF_SUPPORT': u'Consultation', 'scienceCategory': u'Interstellar Medium', 'comments': u'', 'justificationFile_id': u'7403', 'MODIFIED_DATE': u'2013-02-04 21:57:56', 'user_id': u'2554', 'TELESCOPE': u'VLBA', 'OTHER_AWARDS': u'', 'reviewersConflict': u'\x00', 'allocated_hours': u'0.0', 'objectversion': u'0', 'principal_investigator_id': u'33166', 'RAPID_RESPONSE_TYPE': u'None', 'technicalCategory_id': u'78', 'category3_id': u'None', 'GRADUATION_YEAR': u'-1', 'person_id': u'2552', 'proposal_id': u'7917', 'disposition_letter': u'None', 'public': u'\x00', 'PRESENT': u'no', 'STATUS': u'SUBMITTED', 'trigger_criteria': u'None', 'dissertationPlan_id': u'None', 'PLAN_SUBMITTED': u'no', 'category1_id': u'None', 'SUPPORT_REQUESTER': u'\x00', 'DOMESTIC': u'\x01', 'PROFESSIONAL_STATUS': u'All Others', 'THESIS_OBSERVING': u'\x00', 'LOCK_MILLIS': u'0', 'STORAGE_ORDER': u'0', 'editor_id': u'33166', 'author_id': u'33166', 'SUBMITTED_DATE': u'2013-02-04 21:56:27', 'displayTechnicalReviews': u'\x00', 'DISPLAY_POSITION': u'0', 'LOCK_USER_ID': u'None', 'NEW_USER': u'\x00', 'PREV_PROP_IDS': u'None', 'PROPOSAL_TYPE': u'Regular', 'TELEPHONE': u'805-8932814', 'LOCK_USER_INFO': u'None', 'AFFILIATION': u'California at Santa Barbara, University of', 'BUDGET': u'0.0', 'OBSERVING_TYPE_OTHER': u'None', 'LEGACY_ID': u'BG221', 'ADDRESS': u'', 'PROP_ID': u'VLBA/13B-395', 'OLDAUTHOR_ID': u'2554', 'OLD_EDITOR': u'None'}

        p = Proposal.createFromSqlResult(sqlResult)
        pcode = 'VLBA13B-395'
        self.assertEqual(p.pcode, pcode)

        # now get fresh copy from db
        pdb = Proposal.objects.get(pcode = pcode)
        self.assertEqual(datetime(2013, 2, 4, 21, 56, 27), pdb.submit_date)
        self.assertEqual(datetime(2013, 2, 4, 21, 57, 56), pdb.modify_date)
        self.assertEqual(datetime(2013, 1, 26, 18, 18, 54), pdb.create_date)

        # now, recreate it, but w/ out the submit date
        pdb.delete()
        sqlResult['SUBMITTED_DATE'] = 'None'

        p = Proposal.createFromSqlResult(sqlResult)
        pcode = 'VLBA13B-395'
        self.assertEqual(p.pcode, pcode)

        # now get fresh copy from db
        pdb = Proposal.objects.get(pcode = pcode)
        self.assertEqual(datetime(2013, 2, 4, 21, 57, 56), pdb.modify_date)
        self.assertEqual(datetime(2013, 1, 26, 18, 18, 54), pdb.create_date)

        # the submit date should now be 'now'
        self.assertNotEqual(datetime(2013, 2, 4, 21, 56, 27), pdb.submit_date)
        frmt = '%Y-%m-%d %H:%M'
        now = datetime.now().strftime(frmt)
        self.assertEqual(now, pdb.submit_date.strftime(frmt))

        # demonstrate that as long as it's unicode, we're cool
        pdb.delete()
        a =  u'dog\xe0\xe1cat'
        sqlResult['ABSTRACT'] = a 
        p = Proposal.createFromSqlResult(sqlResult)
        pdb = Proposal.objects.get(pcode = pcode)
        self.assertEqual(a, pdb.abstract)
예제 #9
0
class TestProposal(TestCase):

    fixtures = ['scheduler.json', 'proposal_GBT12A-002']

    def setUp(self):
        super(TestProposal, self).setUp()

        # this project has no allotments!
        self.project = DSSProject.objects.order_by('pcode').all()[0]

        # setup some periods
        self.start = datetime(2000, 1, 1, 0)
        self.end   = self.start + timedelta(hours = 12)
        times = [(datetime(2000, 1, 1, 0), 5.0, "one")
               , (datetime(2000, 1, 1, 5), 3.0, "two")
               , (datetime(2000, 1, 1, 8), 4.0, "three")
               ]
        self.ps = []
        state = DSSPeriod_State.objects.get(abbreviation = 'P')
        for start, dur, name in times:
            # each session has grade 4, time = 3 
            s = create_sesshun()
            s.name = name
            s.save()
            pa = DSSPeriod_Accounting(scheduled = dur)
            pa.save()
            p = DSSPeriod( session    = s
                      , start      = start
                      , duration   = dur
                      , state      = state
                      , accounting = pa
                      )
            p.save()
            self.ps.append(p)


        # Okay, now set up the corresponding proposal
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        sqlResult = { 'PROP_ID' : self.project.pcode
                    , 'PROPOSAL_TYPE' : 'Regular'
                    , 'STATUS' : 'Draft'
                    , 'SUBMITTED_DATE' : now 
                    , 'CREATED_DATE' : now 
                    , 'MODIFIED_DATE' : now 
                    , 'TITLE' : 'Lynrd Sknyrd'
                    , 'ABSTRACT' : 'What song do you wanna hear?'
                    , 'proposal_id' : 0
                    , 'JOINT_PROPOSAL_TYPE' : 'Not a joint Proposal'
                    }
        proposal = Proposal.createFromSqlResult(sqlResult)
        proposal.dss_project = self.project
        proposal.setSemester(self.project.semester.semester)
        proposal.save()
        self.proposal = proposal

        self.ta = TimeAccounting()

    def tearDown(self):
        super(TestProposal, self).tearDown()

        for p in self.ps:
            s = p.session
            p.delete()
            s.delete()

    # TBF: stick this in a utility somewhere        
    def createSession(self, p):
        "Create a new session for the tests"
        return createSession(p)

    def test_requestedTime(self):

        p = Proposal.objects.get(pcode = 'GBT09A-001')
        self.assertEqual(0.0, p.requestedTime())
        # now get some requested time
        self.createSession(p)
        self.assertEqual(7.0, p.requestedTime())
        
    def test_timeAccounting(self):

        self.assertEqual(12.0, self.ta.getTime('time_billed', self.project))
        self.assertEqual(12.0, self.proposal.billedTime())

        self.assertEqual(12.0, self.ta.getTime('scheduled', self.project))
        self.assertEqual(12.0, self.proposal.scheduledTime())

        self.assertEqual(-12.0, self.ta.getTimeRemaining(self.project))
        self.assertEqual(-12.0, self.proposal.remainingTime())