#!/usr/bin/env python3
"""Generate an A4 French/Arabic vehicle rental contract as a print-ready PDF.
Usage:
python generate_rental_contract.py
Edit the CONTRACT dictionary below, or import generate_contract() in your app.
Requires: weasyprint (pip install weasyprint)
"""
from __future__ import annotations
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Any
from html import escape
from weasyprint import HTML
OUTPUT_DIR = Path(__file__).resolve().parent
CONTRACT: dict[str, Any] = {
"company_name": "CAR LOCATION",
"company_address": "Adresse de la société, Midelt, Maroc",
"delivery_place": "Midelt",
"return_place": "Midelt",
"issued_at": "Midelt le 06/06/2021 11:35",
"driver": {
"first_name": "Prénom",
"last_name": "Nom",
"birth_date": "01/01/1990",
"nationality": "Marocain",
"address": "Adresse du client",
"phone": "+212 6 00 00 00 00",
"cin": "AB123456",
"passport": "",
"license": "P123456",
"license_issued": "09/03/2004",
},
"second_driver": None,
"vehicle": {
"make_model": "Dacia Dokker",
"registration": "12345-A-6",
"departure": "06/06/2021 11:35",
"return": "08/06/2021 11:35",
"duration": "2 jour(s)",
"fuel_type": "Diesel",
"extension": False,
"extension_return": "",
"spare_wheel": True,
"radio_cd": False,
"fuel_level": "1/4", # 0, 1/4, 1/2, 3/4 or 1
},
"notes": "",
}
def h(value: Any) -> str:
return escape("" if value is None else str(value))
def check(value: bool) -> str:
return "☒" if value else "☐"
def field(label_fr: str, label_ar: str, value: Any) -> str:
return (
'
'
f'{h(label_fr)}'
f'{h(value)}'
f'{h(label_ar)}'
'
'
)
def driver_block(driver: dict[str, Any] | None, title: str) -> str:
if not driver:
return f'''
{h(title)}
Prénom: ***** AUCUN ***** لا أحد
Nom: *****
Date de naissance: *****
N° de C.I.N.: *****
N° de Permis: *****
Délivrée le: *****
'''
return f'''
{h(title)}
{field("Prénom:", "الاسم", driver.get("first_name"))}
{field("Nom:", "النسب", driver.get("last_name"))}
{field("Date de naissance:", "تاريخ الازدياد", driver.get("birth_date"))}
{field("Nationalité:", "الجنسية", driver.get("nationality"))}
{field("Adresse:", "العنوان", driver.get("address"))}
{field("N° téléphone:", "رقم الهاتف", driver.get("phone"))}
{field("N° de C.I.N.:", "رقم البطاقة الوطنية", driver.get("cin"))}
{field("N° de Passeport:", "رقم جواز السفر", driver.get("passport"))}
{field("N° de Permis:", "رقم رخصة السياقة", driver.get("license"))}
{field("Délivrée le:", "سلمت بتاريخ", driver.get("license_issued"))}
'''
def vehicle_diagram() -> str:
return '''
'''
def render_html(data: dict[str, Any]) -> str:
v = data["vehicle"]
levels = ["0", "1/4", "1/2", "3/4", "1"]
fuel_marks = "".join(
f'{x}{"X" if v.get("fuel_level") == x else ""}
'
for x in levels
)
extension_yes = bool(v.get("extension"))
return f'''
Contrat de location
| Lieu de livraison : {h(data['delivery_place'])} |
Lieu de reprise : {h(data['return_place'])} |
{driver_block(data.get('driver'), 'Conducteur I')}
{driver_block(data.get('second_driver'), 'Conducteur II')}
Le client est seul responsable des délits, contraventions et infractions au code de la route.
Véhicule
Marque :{h(v.get('make_model'))}
Matricule :{h(v.get('registration'))}
Date de départ :{h(v.get('departure'))}
Date de retour :{h(v.get('return'))}
Durée de location :{h(v.get('duration'))}
Carburant :{h(v.get('fuel_type'))}
Prolongation :{check(extension_yes)} OUI {check(not extension_yes)} NON
Retour en cas de prolongation :{h(v.get('extension_return'))}
Roue de secours
{check(bool(v.get('spare_wheel')))} OUI {check(not bool(v.get('spare_wheel')))} NON
Poste radio et CD
{check(bool(v.get('radio_cd')))} OUI {check(not bool(v.get('radio_cd')))} NON
Niveau carburant
{fuel_marks}
État général à la réception du client
{vehicle_diagram()}
Signature du client : lu et approuvé des conditions
Cachet et signature de la société
'''
def generate_contract(data: dict[str, Any], output_pdf: str | Path, output_html: str | Path | None = None) -> Path:
"""Render contract data to a print-ready PDF and optionally save the HTML preview."""
output_pdf = Path(output_pdf)
output_pdf.parent.mkdir(parents=True, exist_ok=True)
html = render_html(data)
if output_html:
output_html = Path(output_html)
output_html.write_text(html, encoding="utf-8")
HTML(string=html, base_url=str(OUTPUT_DIR)).write_pdf(str(output_pdf))
return output_pdf
if __name__ == "__main__":
pdf = generate_contract(
CONTRACT,
OUTPUT_DIR / "rental_contract.pdf",
OUTPUT_DIR / "rental_contract_preview.html",
)
print(f"Generated: {pdf}")