summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2024-09-29 14:39:56 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2024-09-29 23:24:09 +0200
commitccd7055b5d94f60eb6a8974f530b526fa2405e0b (patch)
treeebf1550ddd540e9478c3e1b570e0a0fee59bf526
parent9f44f4dd6cdaeb44addf5cc8f2d15a581f1c3e89 (diff)
downloadIshtar-ccd7055b5d94f60eb6a8974f530b526fa2405e0b.tar.bz2
Ishtar-ccd7055b5d94f60eb6a8974f530b526fa2405e0b.zip
✨ more robust templates (don't crash on missing values) - add fields for file templates - number_to_words and float_format filters for templates
-rw-r--r--archaeological_files/models.py86
-rw-r--r--ishtar_common/models.py5
-rw-r--r--ishtar_common/utils_secretary.py30
-rw-r--r--requirements.txt2
4 files changed, 108 insertions, 15 deletions
diff --git a/archaeological_files/models.py b/archaeological_files/models.py
index 04a4ccc2c..9507a5e0b 100644
--- a/archaeological_files/models.py
+++ b/archaeological_files/models.py
@@ -130,6 +130,10 @@ class Job(GeneralType):
lbl += " ({})".format(_("fixed-term contract"))
return lbl
+ @property
+ def has_parents(self):
+ return bool(self.parents.count())
+
@classmethod
def get_choices(cls, current_value, price_agreement_id=None):
q = cls.objects.filter(
@@ -1059,17 +1063,42 @@ class File(
values[prefix + key_qt_planned] = 0
key_qt_worked = slug + "_equipment_sum_worked"
values[prefix + key_qt_worked] = 0
+ key_costs = prefix + slug + "_equipment_costs"
+ key_costs_dct = {}
for cost in q.all():
- values[prefix + key_qt_planned] += (cost.quantity_by_day_planned or 0)
- values[prefix + key_qt_worked] += (cost.quantity_by_day_worked or 0)
+ quantity_by_day_planned = (cost.quantity_by_day_planned or 0)
+ values[prefix + key_qt_planned] += quantity_by_day_planned
+ quantity_by_day_worked = (cost.quantity_by_day_worked or 0)
+ values[prefix + key_qt_worked] += quantity_by_day_worked
value = ES_UNITS_DAYS[cost.equipment_service_cost.unit] * (
cost.days_planned or 0)
if value > values[prefix + key_time_planned]:
values[prefix + key_time_planned] = value
+ cost_name = cost.equipment_service_cost.equipment_service_type.label
+ if cost_name not in key_costs_dct:
+ key_costs_dct[cost_name] = {
+ "quantity_by_day_planned": 0,
+ "days_planned": 0,
+ "cost_planned": 0,
+ "quantity_by_day_worked": 0,
+ "days_worked": 0,
+ "cost_worked": 0
+ }
+ key_costs_dct[cost_name]["quantity_by_day_planned"] += (quantity_by_day_planned or 0)
+ key_costs_dct[cost_name]["days_planned"] += (cost.days_planned or 0)
+ key_costs_dct[cost_name]["cost_planned"] += value
value = ES_UNITS_DAYS[cost.equipment_service_cost.unit] * (
cost.days_worked or 0)
if value > values[prefix + key_time_planned]:
values[prefix + key_time_worked] = value
+ key_costs_dct[cost_name]["quantity_by_day_worked"] += (quantity_by_day_worked or 0)
+ key_costs_dct[cost_name]["days_worked"] += (cost.days_worked or 0)
+ key_costs_dct[cost_name]["cost_worked"] += value
+ values[key_costs] = []
+ for cost_name in key_costs_dct:
+ cost_detail = {"name": cost_name}
+ cost_detail.update(key_costs_dct[cost_name])
+ values[key_costs].append(InlineClass(cost_detail))
return values
@property
@@ -1094,8 +1123,11 @@ class File(
jobs = {}
total_ground_cost_planned = 0
total_ground_cost_worked = 0
+ max_ground_day_planned = 0
for job in self.ground_jobs.filter(job__permanent_contract=permanent).all():
name = job.job.label
+ has_parents = job.job.has_parents
+ max_ground_day_planned = max(max_ground_day_planned, job.days_planned or 0)
ground_cost_total = (
(job.man_by_day_planned or 0) * (job.days_planned or 0) *
(job.cost_planned or 0)
@@ -1115,13 +1147,25 @@ class File(
"ground_days_worked": job.days_worked,
"ground_cost_worked": job.cost_worked,
"ground_cost_total_worked": ground_cost_total_worked or "",
+ "has_parents": has_parents,
+ "man_by_day_planned": "",
+ "days_planned": "",
+ "cost_planned": "",
+ "cost_total_planned": "",
+ "man_by_day_worked": "",
+ "days_worked": "",
+ "cost_worked": "",
+ "cost_total_worked": "",
}
total_cost_planned = 0
total_cost_worked = 0
+ max_day_worked = 0
for job in self.jobs.filter(job__permanent_contract=permanent).all():
name = job.job.label
+ max_day_worked = max(max_day_worked, job.days_worked or 0)
if name not in jobs:
- jobs[name] = {}
+ has_parents = job.job.has_parents
+ jobs[name] = {"has_parents": has_parents}
cost_total_planned = (
(job.man_by_day_planned or 0) * (job.days_planned or 0) *
(job.cost_planned or 0)
@@ -1147,21 +1191,37 @@ class File(
dct = jobs[k]
dct["name"] = k
result.append(InlineClass(dct))
- return result, total_ground_cost_planned, total_ground_cost_worked, \
- total_cost_planned, total_cost_worked
+ dct = {
+ "jobs_list": result,
+ "ground_cost_planned": total_ground_cost_planned,
+ "ground_cost_worked": total_ground_cost_worked,
+ "cost_planned": total_cost_planned,
+ "cost_worked": total_cost_worked,
+ "max_day_worked": max_day_worked,
+ "max_ground_day_worked": max_ground_day_planned,
+ }
+ return dct
def get_extra_values(self, prefix="", no_values=False, filtr=None, **kwargs):
values = get_values_town_related(self, prefix, {}, filtr=filtr)
values = self.get_dynamic_values(prefix, values, filtr=filtr)
values["montant_subvention_dotation"] = self.montant_subvention_dotation
- values["jobs_list_permanent"], values["permanent_ground_cost_planned"], \
- values["permanent_ground_cost_worked"], \
- values["permanent_cost_planned"], \
- values["permanent_cost_worked"] = self.jobs_list_permanent
- values["jobs_list_non_permanent"], values["non_permanent_ground_cost_planned"], \
- values["non_permanent_ground_cost_worked"], \
- values["non_permanent_cost_planned"], \
- values["non_permanent_cost_worked"] = self.jobs_list_non_permanent
+
+ dct = self.jobs_list_permanent
+ values["jobs_list"] = dct["jobs_list"][:]
+ values["jobs_list_permanent"] = dct.pop("jobs_list")
+ values["max_day_worked"] = dct.pop("max_day_worked")
+ values["max_ground_day_worked"] = dct.pop("max_ground_day_worked")
+ for k in dct.keys():
+ values["permanent_" + k] = dct[k]
+ dct = self.jobs_list_non_permanent
+ values["jobs_list"] += dct["jobs_list"][:]
+ values["jobs_list_non_permanent"] = dct.pop("jobs_list")
+ values["max_day_worked"] = max(values["max_day_worked"], dct.pop("max_day_worked"))
+ values["max_ground_day_worked"] = max(values["max_ground_day_worked"], dct.pop("max_ground_day_worked"))
+ for k in dct.keys():
+ values["non_permanent_" + k] = dct[k]
+
values["used_equipments"] = self.used_equipments
values["equipments_cost_planned"] = sum([cost for _1, cost, _2, _3, _4 in self.used_equipments])
values["equipments_cost_worked"] = sum([cost for _1, _2, cost, _3, _4 in self.used_equipments])
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index f7512b606..a827fa902 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -2310,6 +2310,11 @@ class DocumentTemplate(models.Model):
ensure_ascii=False,
separators=("", " : "),
).replace(" " * 4, "\t")
+ # initialize missing values
+ if filtr:
+ for k in filtr:
+ if k not in values:
+ values[k] = ""
engine = IshtarSecretaryRenderer()
try:
result = engine.render(self.template, **values)
diff --git a/ishtar_common/utils_secretary.py b/ishtar_common/utils_secretary.py
index e4e6e8e0b..318f50bdc 100644
--- a/ishtar_common/utils_secretary.py
+++ b/ishtar_common/utils_secretary.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from secretary import Renderer
from lxml import etree
@@ -7,10 +7,12 @@ from xml.parsers.expat import ExpatError, ErrorString
from datetime import datetime
import locale
+from num2words import num2words
from PIL import Image
import re
from django.conf import settings
+from django.utils import formats
RE_UNITS = re.compile("([.0-9]+)([a-z]+)")
@@ -25,6 +27,28 @@ def parse_value_unit(value):
return value, unit
+def float_format(value):
+ if value is None or value == "":
+ return ""
+ try:
+ value = float(value)
+ except ValueError:
+ return ""
+ if int(value) == value:
+ return int(value)
+ return formats.localize(value, use_l10n=True)
+
+
+def number_to_words(value):
+ if value is None or value == "":
+ return ""
+ try:
+ value = float(value)
+ except ValueError:
+ return ""
+ return num2words(value, lang=settings.LANGUAGE_CODE.split("-")[0])
+
+
def replace_line_breaks(value):
return (value or "").replace("\r\n", "\n")
@@ -60,7 +84,7 @@ def capitalize_filter(value):
def human_date_filter(value):
try:
value = datetime.strptime(value, "%Y-%m-%d")
- except ValueError:
+ except (ValueError, TypeError) as __:
return ""
language_code = settings.LANGUAGE_CODE.split("-")
language_code = language_code[0] + "_" + language_code[1].upper()
@@ -109,6 +133,8 @@ class IshtarSecretaryRenderer(Renderer):
self.environment.filters["capfirst"] = capfirst_filter
self.environment.filters["lowerfirst"] = lowerfirst_filter
self.environment.filters["capitalize"] = capitalize_filter
+ self.environment.filters["float_format"] = float_format
+ self.environment.filters["number_to_words"] = number_to_words
self.environment.filters["replace_line_breaks"] = replace_line_breaks
self.environment.filters["splitpart"] = splitpart
diff --git a/requirements.txt b/requirements.txt
index ffc44643e..2e666c9de 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -70,3 +70,5 @@ pandas==1.1.5
django-ipware==3.0.0
django-axes==5.4.3
+
+num2words==0.5.9 \ No newline at end of file