def pretty_planned_hours(self): return _( "%(planned_hours)s in %(weeks)s weeks (%(per_week)s per week)") % { "planned_hours": hours(self.planned_hours), "weeks": len(self.weeks), "per_week": hours(self.planned_hours / len(self.weeks)), }
def timer(request): return render( request, "timer.html", { "hours": { "today": hours(request.user.hours["today"]), "week": hours(request.user.hours["week"]), }, }, )
def list_timestamps(request): form = SignedEmailUserForm(request.GET, request=request) if not form.is_valid(): return JsonResponse({"errors": form.errors.as_json()}, status=400) user = form.cleaned_data["user"] slices = Timestamp.objects.slices(user) daily_hours = sum( (slice["logged_hours"].hours for slice in slices if slice.get("logged_hours")), Z1, ) return JsonResponse({ "success": True, "user": str(user), "hours": daily_hours, "timestamps": [{ "timestamp": "{:>5} - {:>5} {:^7} {}".format( local_date_format(slice.get("starts_at"), fmt="H:i") or "? ", local_date_format(slice.get("ends_at"), fmt="H:i") or "? ", "({})".format(hours(slice.elapsed_hours, plus_sign=True)) if slice.elapsed_hours is not None else "?", slice["description"] or "-", ), "elapsed": slice.elapsed_hours, "comment": slice.get("comment", ""), } for slice in slices], })
def amount(row): if row["service"].effort_rate is not None: return currency(row["not_archived"]) elif row["logged_hours"]: return format_html( '{} <small class="bg-warning px-1">{}</small>', currency(row["not_archived"]), _("%s logged but no hourly rate defined.") % hours(row["logged_hours"]), ) return currency(row["logged_cost"])
def classify_logging_delay(delay): explanation = _("Average logging time is %s after noon.") % hours(delay) if delay < 3: return _("Promptly"), "success", explanation elif delay < 8: return _("Same day"), "light", explanation elif delay < 30: return _("Next day"), "caveat", explanation explanation = _("Average logging delay is %s.") % days(delay / 24) return _("Late"), "danger", explanation
def test_formats_hours(self): """Number formatting""" for value, result in [ (Decimal("42.22"), "42.2h"), (Decimal("42.27"), "42.3h"), (Decimal("3"), "3.0h"), (Decimal("-123"), "-123.0h"), (Decimal("0.003"), "0.0h"), (Decimal("-0.003"), "0.0h"), (Decimal("12345"), "12’345.0h"), (None, "0.0h"), ]: with self.subTest(value=value, result=result): self.assertEqual(formats.hours(value), result)
def services_row_with_details(self, service): is_optional = getattr(service, "is_optional", False) return [ ( MarkupParagraph( "<b>%s</b> %s<br/>%s" % ( sanitize(service.title), _("(optional)") if is_optional else "", sanitize(service.description), ), self.style.normal, ), "", "", ), ( MarkupParagraph( ", ".join( filter( None, [ ("%s %s à %s/h" % ( hours(service.effort_hours), service.effort_type, currency(service.effort_rate), )) if service.effort_hours and service.effort_rate else "", ("{} {}".format(currency(service.cost), _("fixed costs"))) if service.cost else "", ], )), self.style.normalWithExtraLeading, ), MarkupParagraph( "<i>%s</i>" % currency(service.service_cost.quantize(Z)), self.style.right, ) if is_optional else "", "" if is_optional else currency( service.service_cost.quantize(Z)), ), ]
def user_stats_pdf(data): with io.BytesIO() as buf: pdf = PDFDocument(buf, font_size=7) pdf.init_report() awt_columns = [9.5 * mm for i in range(12)] awt_columns.append(12 * mm) awt_columns.insert(0, pdf.bounds.E - pdf.bounds.W - sum(awt_columns)) awt_table_style = pdf.style.tableHead + (("TOPPADDING", (0, 1), (-1, -1), 3), ) pdf.h1(data["user"]) pdf.spacer(1 * mm) pdf.p(_("annual working time")) pdf.spacer(3 * mm) table = [] table.append( [data["months"]["year"]] + [date_format(day, "M") for day in data["months"]["months"]] + [_("Total")]) table.append([_("target days for full time employment")] + data["months"]["target_days"] + [data["totals"]["target_days"]]) table.append([_("pensum")] + ["%.0f%%" % p for p in data["months"]["percentage"]] + ["%.0f%%" % data["totals"]["percentage"]]) table.append([_("vacation days available")] + [ days(value) for value in data["months"]["available_vacation_days"] ] + [days(data["totals"]["available_vacation_days"])]) table.append([ "%s (%s)" % ( _("target time"), data["months"]["year"].pretty_working_time_per_day, ) ] + [hours(value) for value in data["months"]["target"]] + [hours(data["totals"]["target"])]) table.append([_("vacation days taken")] + [ days(value) if value else "" for value in data["months"]["absence_vacation"] ] + [days(data["totals"]["absence_vacation"])]) table.append([_("sickness days")] + [ days(value) if value else "" for value in data["months"]["absence_sickness"] ] + [days(data["totals"]["absence_sickness"])]) if data["totals"]["absence_paid"]: table.append([_("Paid leave")] + [ days(value) if value else "" for value in data["months"]["absence_paid"] ] + [days(data["totals"]["absence_paid"])]) if data["totals"]["absence_correction"]: table.append([_("Working time correction")] + [ days(value) if value else "" for value in data["months"]["absence_correction"] ] + [days(data["totals"]["absence_correction"])]) if data["totals"]["vacation_days_correction"]: table.append([_("vacation days correction")] + [ days(value) if value else "" for value in data["months"]["vacation_days_correction"] ] + [days(data["totals"]["vacation_days_correction"])]) table.append( [_("countable absence hours")] + [hours(value) if value else "" for value in data["absences_time"]] + [hours(data["totals"]["absences_time"])]) table.append([_("logged hours")] + [ hours(value) if value else "" for value in data["months"]["hours"] ] + [hours(data["totals"]["hours"])]) table.append( [_("working time")] + [hours(value) if value else "" for value in data["working_time"]] + [hours(data["totals"]["working_time"])]) table.append([_("net work hours per month")] + [hours(value) for value in data["monthly_sums"]] + [""]) pdf.table(table, awt_columns, awt_table_style) pdf.spacer(1 * mm) table = [] table.append([_("running net work hours")] + [hours(value) for value in data["running_sums"]] + [hours(data["totals"]["running_sum"])]) if data["totals"]["vacation_days_credit"]: table.append([_("vacation days credit")] + [""] * 12 + [days(data["totals"]["vacation_days_credit"])]) table.append([_("balance")] + [""] * 12 + [hours(data["totals"]["balance"])]) pdf.table( table, awt_columns, awt_table_style + (("FONT", (0, 0), (-1, 0), "Rep", pdf.style.fontSize), ), ) pdf.spacer() table = [] for employment in data["employments"]: table.append([ employment, employment.percentage, "%.0f" % employment.vacation_weeks, employment.notes, ]) if table: pdf.table( [[ _("employment"), _("percentage"), _("vacation weeks"), _("notes") ]] + table, pdf.table_columns((35 * mm, 15 * mm, 20 * mm, None)), awt_table_style + (("ALIGN", (0, 0), (-1, -1), "LEFT"), ), ) pdf.spacer() for key, reason in [ ("absence_vacation", _("vacation days")), ("absence_sickness", _("sickness days")), ("absence_paid", _("Paid leave")), ("absence_correction", _("Working time correction")), ]: table = [[ absence.pretty_period, days(absence.days), absence.description ] for absence in data["absences"][key]] if table: pdf.table( [[reason, "", ""]] + table, pdf.table_columns((35 * mm, 15 * mm, None)), awt_table_style + (("ALIGN", (0, 0), (-1, -1), "LEFT"), ), ) pdf.spacer() pdf.p( _("Generated on %(day)s") % {"day": local_date_format(dt.date.today())}) pdf.generate() return buf.getvalue()
def pretty_working_time_per_day(self): return "%s/%s" % (hours(self.working_time_per_day), _("day"))
def pretty_working_time_per_day(self): return "{}/{}".format(hours(self.working_time_per_day), _("day"))
def __str__(self): u = self.user.get_short_name() h = hours(self.planned_hours) return f"{self.title} ({u}, {h}, {self.pretty_from_until})"
def __str__(self): return "{} ({})".format(self.title, hours(self.planned_hours))