#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2025 Étienne Loks # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # See the file COPYING for details. from datetime import datetime from jinja2.environment import Environment from jinja2.filters import environmentfilter import locale from num2words import num2words import re from django.conf import settings def set_locale(): language_code = settings.LANGUAGE_CODE.split("-") language_code = language_code[0] + "_" + language_code[1].upper() for language_suffix in (".utf8", ""): try: locale.setlocale(locale.LC_TIME, language_code + language_suffix) break except locale.Error: pass @environmentfilter def float_format(*args): """ 10350.5 -> 10 350,5 5 -> 5 5.449999 -> 5,45 """ value = args[0] if len(args) == 1 else args[1] # jinja simple filter if value is None or value == "": return "" try: value = float(value) except ValueError: return "" locale.setlocale(locale.LC_ALL, "fr_FR.UTF-8") if int(value) != value: value = float(f"{value:.2f}") return f"{value:n}" @environmentfilter def euro_format(*args): """ 15000 -> 15 000,00 € 5 -> 5,00 € """ value = args[0] if len(args) == 1 else args[1] # jinja simple filter value = float_format(value) if not value: return "" parts = value.split(",") if len(parts) < 2: return value + ",00 €" elif len(parts[1]) == 1: return value + "0 €" return value + " €" @environmentfilter def number_to_words(*args): value = args[0] if len(args) == 1 else args[1] # jinja simple filter if value is None or value == "": return "" try: value = float(value) except ValueError: return "" return num2words(value, lang=settings.LANGUAGE_CODE.split("-")[0]) @environmentfilter def replace_line_breaks(*args): value = args[0] if len(args) == 1 else args[1] # jinja simple filter return (value or "").replace("\r\n", "\n") @environmentfilter def capfirst_filter(*args): value = args[0] if len(args) == 1 else args[1] # jinja simple filter return value[0].upper() + value[1:] if value else value @environmentfilter def lowerfirst_filter(*args): value = args[0] if len(args) == 1 else args[1] # jinja simple filter return value[0].lower() + value[1:] if value else value RE_CAP = re.compile(r"[^-' ]+") SEP = ("un", "une", "le", "la", "les", "lez", "d", "l", "de", "des", "du", "sur", "sous", "en") @environmentfilter def capitalize_filter(*args): value = args[0] if len(args) == 1 else args[1] # jinja simple filter if not value: return "" value = value.lower() res = "" for m in RE_CAP.finditer(value): start = m.start() if start: res += value[start - 1] v = m.group() if v not in SEP: v = v[0].upper() + v[1:] res += v return res @environmentfilter def human_date_filter(*args): value = args[0] if len(args) == 1 else args[1] # jinja simple filter try: value = datetime.strptime(value, "%Y-%m-%d") except (ValueError, TypeError) as __: return "" set_locale() return value.strftime(settings.DATE_FORMAT) @environmentfilter def short_date_filter(*args): value = args[0] if len(args) == 1 else args[1] # jinja simple filter try: value = datetime.strptime(value, "%Y-%m-%d") except (ValueError, TypeError) as __: return "" set_locale() return value.strftime(settings.SHORT_DATE_FORMAT) @environmentfilter def splitpart(*args): """ Split string. First arg is index of first item to get Second arg is last index of items to get not included (not mandatory - default is one item) - 0 to get just one Third arg is character for split - default is "," Last arg is character for join - default is "," {{"9,2,10"|splitpart(1)}} -> 2 {{"chaise;bureau;papier;paragraphe"|splitpart(0,0,";")}} -> chaise {{"182025_C001"|splitpart(1,0,"_")}} -> C001 {{"chaise;bureau;papier;paragraphe"|splitpart(1,3,";")}} -> bureau;papier {{"chaise;bureau;papier;paragraphe"|splitpart(1,4,";","|")}} -> bureau|papier|paragraphe """ # manage delta for environment filter delta = 0 if isinstance(args[0], Environment): delta = 1 value = args[0 + delta] index = args[1 + delta] index_end = args[2 + delta] if len(args) > (2 + delta) else None char = args[3 + delta] if len(args) > (3 + delta) else "," merge_character = args[4 + delta] if len(args) > (4 + delta) else None if index_end: try: index_end = int(index_end) if not merge_character: # merge is assumed merge_character = char except ValueError: # old filter use - manage compatibility merge_character = char char = index_end index_end = None if not value or (not index and index != 0): return "" if merge_character is True: # old filter use merge_character = char splited = value.split(char) if len(splited) <= index: return "" if not merge_character: return splited[index] if index_end: splited = splited[index:index_end] else: splited = splited[index:] return merge_character.join(splited)