def isFree(dt): ''' Returns true when the day of the date is either a holiday, specialday, non workday or not within the calc cycle. ''' dat = datetime.fromtimestamp(dt).date() daystr = datetime.fromtimestamp(dt).strftime("%A").lower() datestr = datetime.fromtimestamp(dt).strftime("%d.%m.%Y") yearstr = datetime.fromtimestamp(dt).strftime("%Y") #holidays from settings s_holidays=settings.get("holidays") #Specialdays are free s_specialdays=[] for specialday in settings.get("specialdays"): s_specialdays.append(specialday+"."+yearstr)#Special days in the settings miss the year #Everything what is not in the calc_cycle is a free day if(not settings.get("calc_cycles").get(dat)):#only dates from a user defined range are there return True if(daystr not in settings.get("calc_cycles").get(dat).get("workdays") or (datestr in s_holidays or datestr in s_specialdays)): return True return False
def _getSingleDay(self, wd): ''' Returns a complete view of the given workday ''' width = settings.get("border_width") - 8 c1 = floor(width / 10 * 3) c2 = floor(width / 10 * 7) startStr = datetime.fromtimestamp(wd.start).strftime( settings.get("time_format")) endStr = datetime.fromtimestamp( time.time()).strftime(settings.get("time_format") + " (ongoing)") if (wd.end): endStr = datetime.fromtimestamp(wd.end).strftime( settings.get("time_format")) wdstats = Utils.getWDStats(wd) info = "" if (Utils.isFree(wd.start)): info += Utils.pb("THIS IS A FREE DAY") info += Utils.pbn() info += Utils.pb( Utils.pf("Day started", c1) + " > " + Utils.pf(startStr, c2)) info += Utils.pb( Utils.pf("Day ended", c1) + " > " + Utils.pf(endStr, c2)) info += Utils.pbn() if (len(wd.breaks) > 0): info += Utils.pb( Utils.pf("breaks", c1) + " > " + Utils.pf(Utils.formatHM(wdstats.get("breaktime")), c2)) for bk in wd.breaks: bkStart = bk.get("start") bkEnd = bk.get("end") bkStartStr = datetime.fromtimestamp(bkStart).strftime("%H:%M") bkEndStr = datetime.fromtimestamp( time.time()).strftime("%H:%M (ongoing)") if (bkEnd): bkEndStr = datetime.fromtimestamp(bkEnd).strftime("%H:%M") info += Utils.pb( Utils.pf("", c1) + " ^ " + Utils.pf(bkStartStr + " - " + bkEndStr, c2)) else: info += Utils.pb( Utils.pf("Breaks", c1) + " > " + Utils.pf("No breaks", c2)) info += Utils.pbn() info += Utils.pb( Utils.pf("Worktime", c1) + " > " + Utils.pf(Utils.formatHM(wdstats.get("worktime")), c2)) return info
def inCalc(ts): ''' Returns true when the day of the date is in the calc cycle. ''' dat=datetime.fromtimestamp(ts).date() if settings.get("calc_cycles").get(dat): return True
def pfl(text="",size=settings.get("border_width")-5): ''' Fills the given string with - up to the given count ''' if(len(text)<size): return text+("-"*(size-len(text))) return text
def pfb(text="",size=settings.get("border_width")-5,symbol="#"): ''' Fills the given string with # up to the given count ''' if(len(text)<size): return text+(symbol*(size-len(text))) return text
def ongoing(self): ''' Print infos about a workday ''' #We want to know the last active workday lastwd = Workday.loadLast(settings.get("history_data")).get("workday") if (lastwd): daystr = datetime.fromtimestamp(lastwd.start).strftime( settings.get("date_format")) info = Utils.head("Active Workday: " + daystr, symbol="~") info += Utils.pbn() info += self._getSingleDay(lastwd) info += Utils.pbn() info += Utils.pbdiv() print(info) return self.l.error("Could not find the last open workday")
def pb(rawstring): ''' Returns text decorated with border ''' out="" if rawstring: while len(rawstring) > 0: out+="| " fill="" end=len(rawstring) if(end > settings.get("border_width")-4): end=settings.get("border_width")-4 elif(end < settings.get("border_width")-4): fill=" "*((settings.get("border_width")-4)-end) out+=rawstring[0:end]+fill+"| \n" rawstring=rawstring[end:len(rawstring)+1] return out
def head(text, underline=settings.get("border_width")-5,symbol="#"): ''' Returns a head ''' str="" str+=Utils.pbn() str+=Utils.pb(text) str+=Utils.pb(Utils.pfb("",size=underline,symbol=symbol)) return str
def day(self, ts): ''' Prints the given day ''' wdo = Workday.loadDay(self.historydir, ts) wd = wdo.get("workday") if (wd): daystr = datetime.fromtimestamp(wd.start).strftime( settings.get("date_format")) info = Utils.head("Day: " + daystr, symbol="~") info += Utils.pbn() info += self._getSingleDay(wd) info += Utils.pbn() info += Utils.pbdiv() print(info) return self.l.error( "Could not find the specified day: " + datetime.fromtimestamp(ts).strftime(settings.get("date_format")))
def getPreviousLastYearDay(ts): ''' Returns the last day of the previous "year" as a timestamp The start of the year is defined by year_swap, so this is one day before year_swap ''' datnow=datetime.fromtimestamp(ts).date() swap=settings.get("year_swap").split(".") yearstart=datetime.strptime(swap[0]+"."+swap[1]+"."+str(datnow.year), "%d.%m.%Y").timestamp() return yearstart-86400
def rangesToArray(): ''' updates the settings by converting all range type like dates into an array of dates ''' #Holidays arrHolidays = [] for holiday in settings.get("holidays"): rng = holiday.split("-") if (len(rng) > 1): sDate = datetime.strptime(rng[0], "%d.%m.%Y").date() eDate = datetime.strptime(rng[1], "%d.%m.%Y").date() dates = Utils.getDatesFromRange(sDate, eDate) for rngDate in dates: dt = datetime.fromtimestamp( rngDate.get("timestamp")).strftime("%d.%m.%Y") arrHolidays.append(dt) else: arrHolidays.append(holiday) settings.update({"holidays": arrHolidays}) tarrClc = {} for cycle in settings.get("calc_cycles"): for workrange in cycle.get("range"): range = workrange.split("-") sDate = datetime.strptime(range[0], "%d.%m.%Y").date() eDate = datetime.strptime(range[1], "%d.%m.%Y").date() for dto in Utils.getDatesFromRange(sDate, eDate): dayname = datetime.fromtimestamp( dto.get("timestamp")).strftime("%A").lower() tarrClc.update({ datetime.fromtimestamp(dto.get("timestamp")).date(): { "minutes_per_day": cycle.get("minutes_per_day"), "day": dayname, "workdays": cycle.get("workdays") } }) settings.update({"calc_cycles": tarrClc})
def getYearDates(ts): ''' Returns an array with all dates within the year of the given timestamp based on the year_swap setting like: [ {"date": <Date>, "timestamp":<utc_seconds>}... ] ''' yeardates=[] dateobj=datetime.fromtimestamp(ts).date() swap=datetime.strptime(settings.get("year_swap")+"."+str(dateobj.year),"%d.%m.%Y").timestamp()-86400 if(ts<swap): ts=swap=datetime.strptime(settings.get("year_swap")+"."+str(dateobj.year-1),"%d.%m.%Y").timestamp()-8640 dateobj=datetime.fromtimestamp(ts).date() dStart=datetime.strptime(settings.get("year_swap")+"."+str(dateobj.year),"%d.%m.%Y").date() lDayTs=datetime.strptime(settings.get("year_swap")+"."+str(dateobj.year),"%d.%m.%Y").timestamp()-86400 lDayDt=datetime.fromtimestamp(lDayTs).date() dEnd=datetime.strptime(str(lDayDt.day)+"."+str(lDayDt.month)+"."+str(dateobj.year+1),"%d.%m.%Y").date() 0 return Utils.getDatesFromRange(dStart,dEnd)
def last(self): ''' Prints the last closed day ''' #dateObjNow=datetime.fromtimestamp(time.time()).date() wd = Workday.loadLastNDay(self.historydir, time.time()) if (wd): daystr = datetime.fromtimestamp(wd.start).strftime( settings.get("date_format")) info = Utils.head("Day: " + daystr, symbol="~") info += Utils.pbn() info += self._getSingleDay(wd) info += Utils.pbn() info += Utils.pbdiv() print(info) return self.l.error("Could not find the last closed workday")
def persist(self, historydir, askoverride=False): ''' persists this workday object ''' #(Re)Create history directory in case it got deleted if(not os.path.isdir(historydir)): os.makedirs(historydir, mode=0o750) #Ensure the destination path dstfile = self._getWDF(historydir) dstdir = os.path.dirname(dstfile) if(not os.path.isdir(dstdir)): os.makedirs(dstdir, mode=0o750) if(os.path.isfile(dstfile) and askoverride): self.l.error("The file for this workday ("+datetime.fromtimestamp(self.start).strftime(settings.get("date_simple_format"))+") already exists") uip = input("Override? [N/y] :") if(uip.lower() != "y"): exit(0) #Serialize as json with open(dstfile,"w") as f: json.dump(self,f,cls=WorkdayJSONEncoder,indent=4) self.l.info("Persisted "+dstfile)
def saldo(self): ''' Print salo todo/done and time account ''' now = time.time() nowd = datetime.fromtimestamp(now) reqw = Utils.getRequiredWork(now) donew = Utils.getDoneWork(self.historydir, now) lastwd = Workday.loadLast(settings.get("history_data")).get("workday") width = settings.get("border_width") - 11 c1w = int(width / 10 * 4) c2w = int(width / 10 * 3) c3w = int(width / 10 * 3) rhYear = Utils.formatHM(reqw.get("year")) rhMonth = Utils.formatHM(reqw.get("month")) rhWeek = Utils.formatHM(reqw.get("week")) rhDay = Utils.formatHM(reqw.get("day")) dhYear = Utils.formatHM(donew.get("year")) dhMonth = Utils.formatHM(donew.get("month")) dhWeek = Utils.formatHM(donew.get("week")) dhDay = Utils.formatHM(donew.get("day")) thAcc = Utils.formatHM(-reqw.get("now") + donew.get("now")) switchDate = settings.get("year_swap") info = info = Utils.head("Saldo") info += Utils.pbn() info += Utils.pb( Utils.pf("Entity", c1w) + " | " + Utils.pf("Required ", c2w) + " | " + Utils.pf("Worked ", c3w)) info += Utils.pb("-" * (settings.get("border_width") - 5)) info += Utils.pb( Utils.pf("Year (until " + switchDate + ")", c1w) + " | " + Utils.pf(rhYear, c2w) + " | " + Utils.pf(dhYear, c3w)) info += Utils.pb( Utils.pf("Month", c1w) + " | " + Utils.pf(rhMonth, c2w) + " | " + Utils.pf(dhMonth, c3w)) info += Utils.pb( Utils.pf("Week", c1w) + " | " + Utils.pf(rhWeek, c2w) + " | " + Utils.pf(dhWeek, c3w)) if (lastwd and not lastwd.end and datetime.fromtimestamp(lastwd.start).date() != datetime.fromtimestamp(time.time()).date()): datestr = datetime.fromtimestamp( lastwd.start).date().strftime("Today (%d.%m - Now)") dhLDay = str(floor(donew.get("day") / 3600)) + "h " + str( floor(Utils.getWDStats(lastwd).get("worktime") % 3600 / 60)) + "m" info += Utils.pb( Utils.pf(datestr, c1w) + " | " + Utils.pf(rhDay, c2w) + " | " + Utils.pf(dhLDay, c3w)) else: info += Utils.pb( Utils.pf("Today", c1w) + " | " + Utils.pf(rhDay, c2w) + " | " + Utils.pf(dhDay, c3w)) info += Utils.pb("-" * (settings.get("border_width") - 5)) info += Utils.pbn() info += Utils.pb("Time account (right now): " + thAcc) info += Utils.pbn() info += Utils.pbdiv() print(info)
def getMinutesPerDay(ts): ''' Returns the required worktime in minutes for the date at the given timestamp ''' return settings.get("calc_cycles").get(datetime.fromtimestamp(ts).date()).get("minutes_per_day")*60
def pbn(): ''' Returns a newline with decorated border ''' return "|"+(" "*(settings.get("border_width")-3))+"|\n"
def pbdiv(): ''' Returns a dividing line ''' return "|"+("-"*(settings.get("border_width")-3))+"|\n"
def _getTbl(self, loadedWDs, saldoBefore=0): ''' Returns a table view of the given loaded workdays ''' info = "" width = settings.get("border_width") - 14 c1 = int(width / 10 * 2.5) c2 = int(width / 10 * 2) c3 = int(width / 10 * 1.25) c4 = int(width / 10 * 1.25) c5 = int(width / 10 * 3) sbstr = Utils.pf("Saldo", 10) + " (" + Utils.formatHM( saldoBefore, posSign=True) + ")" info += Utils.pb( Utils.pf("Day", c1) + " | " + Utils.pf("Range", c2) + " | " + Utils.pf("Required", c3) + " | " + Utils.pf("Worked", c4) + " | " + Utils.pf(sbstr, c5)) ssSaldo = saldoBefore for wdIM in loadedWDs: t_wd = wdIM.get("workday") t_ts = wdIM.get("timestamp") t_date = wdIM.get("date") t_dt = datetime.fromtimestamp(t_ts).strftime( settings.get("date_format")) if (t_date == datetime.fromtimestamp(time.time()).date()): t_dt = ">> " + t_dt if (t_wd and not Utils.isFree(t_ts)): reqd = settings.get("calc_cycles").get( wdIM.get("date")).get("minutes_per_day") * 60 reqdstr = Utils.formatHM(reqd) info += Utils.pb(Utils.pfl()) wdstats = Utils.getWDStats(t_wd) worktime = Utils.formatHM(wdstats.get("worktime")) reqDiff = wdstats.get("worktime") - reqd ssSaldo += reqDiff fSaldoStr = Utils.pf(Utils.formatHM(ssSaldo), 10) + " (" + Utils.formatHM( reqDiff, posSign=True) + ")" w_wds = t_wd.start w_wde = t_wd.end if (not w_wde): #currently open day w_wde = time.time() wdStart = datetime.fromtimestamp(w_wds).strftime("%H:%M") wdEnd = datetime.fromtimestamp(w_wde).strftime("%H:%M") info += Utils.pb( Utils.pf(t_dt, c1) + " | " + Utils.pf(wdStart + " - " + wdEnd, c2) + " | " + Utils.pf(reqdstr, c3) + " | " + Utils.pf(worktime, c4) + " | " + Utils.pf(fSaldoStr, c5)) elif (Utils.isFree(t_ts)): #No need to print free days pass else: reqd = settings.get("calc_cycles").get( wdIM.get("date")).get("minutes_per_day") * 60 reqdstr = Utils.formatHM(reqd) fSaldoStr = "" worktimeStr = "" if (time.time() > t_ts): ssSaldo -= reqd fSaldoStr = Utils.pf(Utils.formatHM(ssSaldo), 10) + " (" + Utils.formatHM( -reqd, posSign=True) + ")" info += Utils.pb(Utils.pfl()) info += Utils.pb( Utils.pf(t_dt, c1) + " | " + Utils.pf(" ", c2) + " | " + Utils.pf(reqdstr, c3) + " | " + Utils.pf("", c4) + " | " + Utils.pf(fSaldoStr, c5)) #info+=Utils.pb(str(wkm.get("date"))+" free") info += Utils.pb(Utils.pfb(symbol="~")) info += Utils.pb( Utils.pf("End saldo:", c1) + Utils.pf("", c2 + c3 + c4 + 9) + " | " + Utils.pf(Utils.formatHM(ssSaldo), c5)) info += Utils.pb(Utils.pfb(symbol="=")) return info
def __init__(self): self.l = logging.getLogger(__name__ + "." + self.__class__.__name__) self.historydir = settings.get("history_data") pass