Example #1
0
def weeksum (db, drid, format = None) :
    start, end = common.week_from_date (db.daily_record.get (drid, 'date'))
    user       = db.daily_record.get (drid, 'user')
    d   = start
    sum = 0.
    while d <= end :
        dr   = db.daily_record.filter \
            (None, dict (date = common.pretty_range (d, d), user = user))
        if len (dr) == 0 :
            d = d + Interval ('1d')
            continue
        assert (len (dr) == 1)
        dr   = dr [0]
        sum += daysum (db, dr)
        d    = d + Interval ('1d')
    if format :
        return format % sum
    return sum
def check_time_record (db, cl, nodeid, new_values) :
    for i in 'daily_record', :
        if i in new_values :
            raise Reject, _ ("%(attr)s may not be changed") % {'attr' : _ (i)}
    drec     = new_values.get ('daily_record', cl.get (nodeid, 'daily_record'))
    dr       = db.daily_record.getnode (drec)
    date     = dr.date
    user     = dr.user
    wpid     = new_values.get ('wp', cl.get (nodeid, 'wp'))
    wp       = None
    tp       = None
    is_ph    = False
    if (wpid) :
        wp = db.time_wp.getnode (wpid)
        tp = db.time_project.getnode (wp.project)
        is_ph = tp.is_public_holiday
    if  (   frozen (db, user, date)
        and new_values.keys () != ['tr_duration']
        ) :
        uname = db.user.get (user, 'username')
        raise Reject, _ ("Frozen: %(uname)s, %(date)s") % locals ()
    status   = db.daily_record.get (cl.get (nodeid, 'daily_record'), 'status')
    leave    = db.daily_record_status.lookup ('leave')
    allow    = False
    if dr.status == leave :
        du = vacation.leave_duration (db, user, date, is_ph)
        if  (   new_values.keys () == ['duration']
            and new_values ['duration'] == du
            and cl.get (nodeid, 'duration') != du
            ) :
            allow = True
    allow    = allow or db.getuid () == '1'
    if  (   status != db.daily_record_status.lookup ('open')
        and new_values.keys () != ['tr_duration']
        and not allow
        ) :
        raise Reject, _ ('Editing of time records only for status "open"')
    # allow empty duration to delete record
    if 'duration' in new_values and new_values ['duration'] is None :
        keys = dict.fromkeys (new_values.iterkeys ())
        del keys ['duration']
        if len (keys) > 0 :
            raise Reject, \
                ( _ ('%(date)s: No duration means "delete record" but '
                     'you entered new value for %(attr)s'
                    )
                % dict ( date = date.pretty (common.ymd)
                       , attr = ", ".join (['"' + _ (i) + '"' for i in keys])
                       )
                )
        return
    check_generated (new_values)
    start    = new_values.get ('start',        cl.get (nodeid, 'start'))
    end      = new_values.get ('end',          cl.get (nodeid, 'end'))
    duration = new_values.get ('duration',     cl.get (nodeid, 'duration'))
    dist     = new_values.get ('dist',         cl.get (nodeid, 'dist'))
    wp       = new_values.get ('wp',           cl.get (nodeid, 'wp'))
    wl       = 'work_location'
    ta       = 'time_activity'
    location = new_values.get (wl,             cl.get (nodeid, wl))
    activity = new_values.get (ta,             cl.get (nodeid, ta))
    comment  = new_values.get ('comment',      cl.get (nodeid, 'comment'))
    check_start_end_duration \
        (date, start, end, duration, new_values, dist = dist)
    if not location :
        new_values ['work_location'] = '1'
    if dist and not wp :
        raise Reject, _ ("Distribution: WP must be given")
    if dist :
        if dist < duration :
            newrec = dict \
                ( daily_record  = drec
                , duration      = dist
                , wp            = wp
                , time_activity = activity
                , work_location = location
                )
            if comment :
                newrec ['comment'] = comment
            start_generated = new_values.get \
                ('start_generated', cl.get (nodeid, 'start_generated'))
            if (start) :
                newrec     ['start']           = start
                newrec     ['end_generated']   = True
                newrec     ['start_generated'] = start_generated
                new_values ['start_generated'] = True
            cl.create (** newrec)
            for attr in 'wp', 'time_activity', 'work_location', 'comment' :
                if attr in new_values :
                    del new_values [attr]
            wp = cl.get (nodeid, 'wp')
        elif dist == duration :
            # Nothing to do -- just set new wp
            pass
        else :
            dist -= duration
            wstart, wend = common.week_from_date (date)
            dsearch = common.pretty_range (date, wend)
            drs = db.daily_record.filter \
                (None, dict (user = user, date = dsearch))
            trs = db.time_record.filter  \
                (None, {'daily_record' : drs})
            trs = [db.time_record.getnode (t) for t in trs]
            trs = [t for t in trs
                   if  (   not t.wp
                       and t.id != nodeid
                       and (   t.daily_record != drec
                           or (  start and t.start > start
                              or not start
                              )
                           )
                       )
                  ]
            trs = [(db.daily_record.get (tr.daily_record, 'date'), tr.start, tr)
                   for tr in trs
                  ]
            trs.sort ()
            trs = [tr [2] for tr in trs]
            sum = reduce (add, [t.duration for t in trs], 0)
            if sum < dist :
                raise Reject, _ \
                    ("dist must not exceed sum of unassigned times in week")
            for tr in trs :
                if tr.duration <= dist :
                    dist -= tr.duration
                    db.time_record.set \
                        ( tr.id
                        , wp            = wp
                        , time_activity = activity
                        , work_location = location
                        )
                else :
                    param = dict (duration = tr.duration - dist)
                    newrec = dict \
                        ( daily_record  = tr.daily_record
                        , duration      = dist
                        , wp            = wp
                        , time_activity = activity
                        , work_location = location
                        )
                    if tr.start :
                        param ['start_generated'] = True
                        dstart  = Date (tr.start)
                        hours   = int (dist)
                        minutes = (dist - hours) * 60
                        dstart += Interval ('%d:%d' % (hours, minutes))
                        param ['start'] = dstart.pretty (hour_format)
                        newrec ['start']           = tr.start
                        newrec ['end_generated']   = True
                    cl.create          (** newrec)
                    # warning side-effect, calling set will change
                    # values in current tr!
                    db.time_record.set (tr.id, **param)
                    dist = 0
                if not dist :
                    break
            assert (dist == 0)
        del new_values ['dist']
    if wp :
        correct_work_location (db, wp, new_values)
    if 'tr_duration' not in new_values :
        new_values ['tr_duration'] = None
Example #3
0
def approvals_pending (db, request, userlist) :
    try :
        db  = db._db
    except AttributeError :
        pass
    pending   = {}
    submitted = db.daily_record_status.lookup ('submitted')
    spec      = copy (request.filterspec)
    filter    = request.filterspec
    editdict  = {':template' : 'edit', ':filter' : 'user,date'}
    now       = Date ('.')
    for u in userlist :
        find_user   = dict (user = u, status = submitted)
        fdate       = None
        last_frozen = db.daily_record_freeze.filter \
            ( None
            , dict (user = u, date = now.pretty (';%Y-%m-%d'), frozen = True)
            , group = [('-', 'date')]
            )
        if last_frozen :
            fdate = db.daily_record_freeze.get (last_frozen [0], 'date') \
                  + common.day
            find_user ['date'] = fdate.pretty ('%Y-%m-%d;')
        dr_per_user = db.daily_record.filter (None, find_user)
        pending [u] = {}
        if dr_per_user :
            earliest = latest = None
            for p in dr_per_user :
                date = db.daily_record.get (p, 'date')
                week, year = common.weekno_year_from_day (date)
                if not earliest or date < earliest :
                    earliest = date
                if not latest   or date > latest :
                    latest   = date
                start, end = common.week_from_date (date)
                if fdate and start < fdate :
                    start = fdate
                filter ['date'] = common.pretty_range (start, end)
                filter ['user'] = u
                pending [u][(year, week)] = \
                    [ None
                    , request.indexargs_url ('', editdict)
                    , 'todo'
                    ]
            interval = latest - earliest
            for k in pending [u].iterkeys () :
                if interval < Interval ('31d') :
                    filter ['date'] = common.pretty_range (earliest, latest)
                    pending [u][k][0] = request.indexargs_url ('', editdict)
                else :
                    pending [u][k][0] = pending [u][k][1]
        else :
            dyn = user_dynamic.last_user_dynamic (db, u)
            if dyn and (not dyn.valid_to or not fdate or dyn.valid_to > fdate) :
                date = now
                if dyn.valid_to and dyn.valid_to < date :
                    date = dyn.valid_to
                week, year = common.weekno_year_from_day (date)
                start, end = common.week_from_date (date)
                if fdate and start < fdate :
                    start = fdate
                if dyn.valid_to and dyn.valid_to < end :
                    end   = dyn.valid_to
                filter ['date'] = common.pretty_range (start, end)
                filter ['user'] = u
                url = request.indexargs_url ('', editdict)
                pending [u][(year, week)] = [url, url, 'done']
    request.filterspec = spec
    return pending
Example #4
0
    def __init__ \
        ( self
        , db
        , user
        , start
        , end
        , period
        , start_balance
        , overtime_corrections = {}
        ) :
        use_additional        = not period.weekly
        overtime              = 0.0
        overtadd              = 0.0
        required              = 0.0
        worked                = 0.0
        over_per              = 0.0
        over_per_2            = 0.0
        days                  = 0.0
        self.achieved_supp    = 0.0
        self.overtime_balance = 0.0
        self.start_balance    = start_balance
        self.period           = period
        date                  = common.start_of_period (start, self.period)
        eop                   = common.end_of_period   (end,   self.period)
        try :
            s, e, p           = overtime_period (db, user, start, end, period)
        except TypeError :
            s, e, p           = eop + day, eop + day, self.period
        assert (p.id == self.period.id)
        opp = 0
        while date <= eop :
            days     += 1.0
            d         = date
            date     += day
            if d < s or d > e : continue
            dur       = durations (db, user, d)

            if  (   dur.dyn
                and period.required_overtime
                and dur.dyn.overtime_period == period.id
                ) :
                if opp == 0 :
                    opp = dur.req_overtime_pp
                elif opp != dur.req_overtime_pp :
                    #print >> sys.stderr, "Oops:", opp, dur.req_overtime_pp
                    opp = None
            over_per += \
                (   period.months
                and dur.dyn
                and dur.dyn.overtime_period == self.period.id
                and dur.supp_per_period
                ) or 0
        assert (days)
        if period.required_overtime :
            self.overtime_per_period = opp
        else :
            self.overtime_per_period = over_per / days
        date = start
        while date <= end :
            dur       = durations (db, user, date)
            work      = dur.tr_duration
            req       = dur.day_work_hours
            over      = dur.supp_weekly_hours
            do_over   = use_work_hours (db, dur.dyn, period)
            oc        = overtime_corrections.get (date.pretty (ymd), [])
            for o in oc :
                self.overtime_balance += o.value or 0
            if use_additional :
                over  = dur.additional_hours
            if period.required_overtime :
                over  = req + dur.required_overtime
            overtime += over * do_over
            if period.months and date <= end :
                overtadd += dur.additional_hours * do_over
            required += req  * do_over
            worked   += work * do_over
            eow       = common.week_from_date (date) [1]
            if  (   date == eow
                and period.months
                and (period.weekly or period.required_overtime)
                ) :
                if period.required_overtime :
                    self.overtime_balance += worked - overtime
                else :
                    if worked > overtadd :
                        self.achieved_supp += min (worked, overtime) - overtadd
                    if worked > overtime :
                        self.overtime_balance += worked - overtime
                    elif worked < required :
                        self.overtime_balance += worked - required
                overtadd = overtime = worked = required = 0.0
                self._consolidate ()
            # increment at end (!)
            date += day

        if not period.weekly and not period.required_overtime :
            overtime += self.overtime_per_period
        if worked > overtadd and period.months :
            if period.weekly :
                self.achieved_supp += min (worked, overtime) - overtadd
            elif not period.required_overtime :
                self.achieved_supp += worked - overtadd
        if worked > overtime :
            self.overtime_balance += worked - overtime
        elif period.required_overtime :
            if worked < overtime :
                self.overtime_balance += worked - overtime
        else :
            if worked < required :
                self.overtime_balance += worked - required
        self._consolidate  ()
        self.achieved_supp = min (self.achieved_supp, self.overtime_per_period)