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
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
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)