2024-07-01 07:56:04 +00:00
|
|
|
from jinja2 import Environment, FileSystemLoader, TemplateNotFound
|
2023-12-31 12:59:13 +00:00
|
|
|
import json
|
|
|
|
import pathlib
|
2024-05-29 13:04:57 +00:00
|
|
|
import datetime
|
2024-07-01 07:56:04 +00:00
|
|
|
import shutil
|
2023-12-31 12:59:13 +00:00
|
|
|
|
2024-04-17 08:45:28 +00:00
|
|
|
from argparse import ArgumentParser
|
|
|
|
|
2024-05-29 13:04:57 +00:00
|
|
|
from helpers.finances import (
|
|
|
|
generate_transparency_table,
|
|
|
|
get_transparency_data,
|
|
|
|
get_latest_month,
|
|
|
|
)
|
2024-05-29 12:50:52 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
# Configure Jinja2 environment
|
2024-07-01 08:24:24 +00:00
|
|
|
env = Environment(loader=FileSystemLoader("templates"))
|
2024-04-17 08:45:28 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
# Set up the output directory for static files
|
2024-07-01 08:24:24 +00:00
|
|
|
output_dir = pathlib.Path("build")
|
2024-07-01 07:56:04 +00:00
|
|
|
output_dir.mkdir(exist_ok=True, parents=True)
|
2023-12-31 12:59:13 +00:00
|
|
|
|
2024-07-01 08:24:24 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
# Define the icon filter
|
|
|
|
def icon(icon_name):
|
2024-07-01 08:24:24 +00:00
|
|
|
icon_path = pathlib.Path("assets") / f"dist/icons/{icon_name}.svg"
|
2023-12-31 12:59:13 +00:00
|
|
|
try:
|
2024-07-01 08:24:24 +00:00
|
|
|
with open(icon_path, "r", encoding="utf-8") as file:
|
2024-07-01 07:56:04 +00:00
|
|
|
file_content = file.read()
|
|
|
|
except FileNotFoundError:
|
2024-07-01 08:24:24 +00:00
|
|
|
file_content = ""
|
2024-07-01 07:56:04 +00:00
|
|
|
return file_content
|
2024-05-27 15:37:30 +00:00
|
|
|
|
2024-07-01 08:24:24 +00:00
|
|
|
|
|
|
|
env.filters["icon"] = icon
|
|
|
|
|
2024-05-27 15:37:30 +00:00
|
|
|
|
2024-07-01 10:06:30 +00:00
|
|
|
# Filter for rendering a month name from a number
|
|
|
|
def month_name(month_number):
|
|
|
|
return datetime.date(1900, int(month_number), 1).strftime("%B")
|
|
|
|
|
|
|
|
|
|
|
|
env.filters["month_name"] = month_name
|
|
|
|
|
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
def render_template_to_file(template_name, output_name, **kwargs):
|
|
|
|
try:
|
|
|
|
template = env.get_template(template_name)
|
|
|
|
output_path = output_dir / output_name
|
2024-07-01 08:24:24 +00:00
|
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
2024-07-01 07:56:04 +00:00
|
|
|
f.write(template.render(**kwargs))
|
|
|
|
except TemplateNotFound:
|
|
|
|
print(f"Template {template_name} not found.")
|
|
|
|
|
2024-07-01 08:24:24 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
def generate_static_site(development_mode=False):
|
|
|
|
# Common context
|
|
|
|
kwargs = {}
|
|
|
|
if development_mode:
|
|
|
|
kwargs.update(
|
|
|
|
{
|
|
|
|
"warning": env.get_template("prod-warning.html").render(),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
# Load services data
|
|
|
|
services = json.loads(
|
|
|
|
(pathlib.Path(__file__).parent / "data" / "services.json").read_text()
|
|
|
|
)
|
2024-05-29 13:04:57 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
# Load finances data
|
|
|
|
finances = json.loads(
|
|
|
|
(pathlib.Path(__file__).parent / "data" / "finances.json").read_text()
|
|
|
|
)
|
2024-05-29 12:50:52 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
# Iterate over all templates in the templates directory
|
2024-07-01 08:24:24 +00:00
|
|
|
templates_path = pathlib.Path("templates")
|
|
|
|
for template_file in templates_path.glob("*.html"):
|
2024-07-01 07:56:04 +00:00
|
|
|
template_name = template_file.stem
|
|
|
|
context = kwargs.copy()
|
2024-05-29 12:50:52 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
if template_name in ["index", "simple"]:
|
|
|
|
context.update({"services": services})
|
2024-06-30 17:15:45 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
if template_name == "membership":
|
|
|
|
allow_current = development_mode
|
2024-06-30 17:15:45 +00:00
|
|
|
finances_month, finances_year = get_latest_month(finances, allow_current)
|
2024-05-29 13:04:57 +00:00
|
|
|
finances_period = datetime.date(finances_year, finances_month, 1)
|
|
|
|
finances_period_str = finances_period.strftime("%B %Y")
|
2024-05-29 12:50:52 +00:00
|
|
|
finances_table = generate_transparency_table(
|
2024-07-01 08:24:24 +00:00
|
|
|
get_transparency_data(
|
|
|
|
finances, finances_year, finances_month, allow_current
|
|
|
|
)
|
|
|
|
)
|
|
|
|
context.update(
|
|
|
|
{
|
|
|
|
"finances": finances_table,
|
|
|
|
"finances_period": finances_period_str,
|
|
|
|
}
|
2024-06-03 14:12:21 +00:00
|
|
|
)
|
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
if template_name == "transparency":
|
2024-06-03 14:12:21 +00:00
|
|
|
finance_data = {}
|
|
|
|
for year in sorted(finances.keys(), reverse=True):
|
|
|
|
for month in sorted(finances[year].keys(), reverse=True):
|
|
|
|
if year not in finance_data:
|
|
|
|
finance_data[year] = {}
|
|
|
|
finance_data[year][month] = generate_transparency_table(
|
2024-07-01 09:11:04 +00:00
|
|
|
get_transparency_data(finances, year, month, True)
|
2024-06-03 14:12:21 +00:00
|
|
|
)
|
2024-07-01 07:56:04 +00:00
|
|
|
context.update({"finances": finance_data})
|
2024-06-03 14:12:21 +00:00
|
|
|
|
2024-07-01 08:24:24 +00:00
|
|
|
render_template_to_file(
|
|
|
|
f"{template_name}.html", f"{template_name}.html", **context
|
|
|
|
)
|
2024-06-30 17:15:45 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
# Generate metrics
|
2024-07-01 05:06:19 +00:00
|
|
|
balances = get_transparency_data(finances, allow_current=True)["end_balance"]
|
2024-06-30 17:15:45 +00:00
|
|
|
|
|
|
|
response = (
|
|
|
|
"# HELP privatecoffee_balance The balance of the private.coffee account\n"
|
|
|
|
)
|
|
|
|
response += "# TYPE privatecoffee_balance gauge\n"
|
|
|
|
|
|
|
|
for currency, balance in balances.items():
|
|
|
|
response += f'privatecoffee_balance{{currency="{currency}"}} {balance}\n'
|
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
metrics_path = output_dir / "metrics.txt"
|
2024-07-01 08:24:24 +00:00
|
|
|
with open(metrics_path, "w", encoding="utf-8") as f:
|
2024-07-01 07:56:04 +00:00
|
|
|
f.write(response)
|
2024-06-30 17:15:45 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
# Copy static assets
|
2024-07-01 10:12:39 +00:00
|
|
|
for folder in ["assets", "data"]:
|
|
|
|
src = pathlib.Path(folder)
|
|
|
|
dst = output_dir / folder
|
|
|
|
if dst.exists():
|
|
|
|
shutil.rmtree(dst)
|
|
|
|
shutil.copytree(src, dst)
|
2024-06-30 17:15:45 +00:00
|
|
|
|
2024-07-01 07:56:04 +00:00
|
|
|
print("Static site generated successfully.")
|
2024-05-29 16:57:32 +00:00
|
|
|
|
2024-07-01 08:24:24 +00:00
|
|
|
|
2024-04-17 08:45:28 +00:00
|
|
|
if __name__ == "__main__":
|
2024-07-01 07:56:04 +00:00
|
|
|
parser = ArgumentParser(description="Generate the private.coffee static site.")
|
|
|
|
parser.add_argument("--dev", action="store_true", help="Enable development mode")
|
2024-04-17 08:45:28 +00:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
2024-07-01 08:24:24 +00:00
|
|
|
generate_static_site(development_mode=args.dev)
|