import datetime import os import base64 from uuid import UUID from beanie import PydanticObjectId from fastapi import APIRouter, HTTPException, Request, Depends from fastapi.responses import HTMLResponse, FileResponse from fastapi.templating import Jinja2Templates from weasyprint import HTML, CSS from weasyprint.text.fonts import FontConfiguration from pathlib import Path from firm.core.depends import get_tenant_registry from firm.entity.models import Entity from firm.template.models import ProvisionTemplate from firm.contract.models import ContractDraft, Contract, ContractStatus, replace_variables_in_value async def build_model(db, model): parties = [] for p in model.parties: party = { "entity": await Entity.get(db, p.entity_id), "part": p.part } if p.representative_id: party['representative'] = await Entity.get(db, p.representative_id) parties.append(party) provisions = [] for p in model.provisions: if p.provision.type == "template": provision = await ProvisionTemplate.get(db, p.provision.provision_template_id) else: provision = p.provision provision.title = replace_variables_in_value(model.variables, provision.title) provision.body = replace_variables_in_value(model.variables, provision.body) provisions.append(provision) model_dict = model.dict() model_dict['parties'] = parties model_dict['provisions'] = provisions model_dict['location'] = "Los Santos, SA" model_dict['date'] = datetime.date(1970, 1, 1) model_dict['lawyer'] = {'entity_data': { "firstname": "prénom avocat", "lastname": "nom avocat", }} return model_dict BASE_PATH = Path(__file__).resolve().parent templates = Jinja2Templates(directory=str(BASE_PATH / "templates")) async def render_print(root_url, contract): template = templates.get_template("print.html") return template.render({ "contract": contract, "root_url": root_url }) async def render_css(root_url, contract): template = templates.get_template("styles.css") return template.render({ "contract": contract, "root_url": root_url }) def retrieve_signature_png(filepath): with open(filepath, "rb") as f: b_content = f.read() base64_utf8_str = base64.b64encode(b_content).decode('utf-8') ext = filepath.split('.')[-1] return f'data:image/{ext};base64,{base64_utf8_str}' preview_router = APIRouter() @preview_router.get("/draft/{draft_id}", response_class=HTMLResponse, tags=["Contract Draft"]) async def preview_draft(draft_id: PydanticObjectId, reg=Depends(get_tenant_registry)) -> str: record = await ContractDraft.get(reg.db, draft_id) if not record: raise HTTPException( status_code=404, detail=f"Contract Draft record not found!" ) draft = await build_model(reg.db, record) return await render_print('', draft) @preview_router.get("/signature/{signature_id}", response_class=HTMLResponse, tags=["Signature"]) async def preview_contract_by_signature(signature_id: UUID, reg=Depends(get_tenant_registry)) -> str: contract = await Contract.find_by_signature_id(reg.db, signature_id) for p in contract.parties: if p.signature_affixed: p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png') return await render_print('', contract) @preview_router.get("/{contract_id}", response_class=HTMLResponse, tags=["Contract"]) async def preview_contract(contract_id: PydanticObjectId, reg=Depends(get_tenant_registry)) -> str: contract = await Contract.get(reg.db, contract_id) for p in contract.parties: if p.signature_affixed: p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png') return await render_print('', contract) print_router = APIRouter() @print_router.get("/pdf/{contract_id}", response_class=FileResponse, tags=["Contract"]) async def create_pdf(contract_id: PydanticObjectId, reg=Depends(get_tenant_registry)) -> str: contract = await Contract.get(reg.db, contract_id) contract_path = "media/contracts/{}.pdf".format(contract_id) if not os.path.isfile(contract_path): if contract.status != ContractStatus.signed: raise HTTPException(status_code=400, detail="Contract is not in a printable state") for p in contract.parties: signature_path = f'media/signatures/{p.signature_uuid}.png' p.signature_png = retrieve_signature_png(signature_path) # os.remove(signature_path) font_config = FontConfiguration() html = HTML(string=await render_print('http://nginx', contract)) css = CSS(string=await render_css('http://nginx', contract), font_config=font_config) html.write_pdf(contract_path, stylesheets=[css], font_config=font_config) await contract.update_status(reg.db, 'printed') return FileResponse( contract_path, media_type="application/pdf", filename=contract.label) @print_router.get("/opengraph/{signature_id}", response_class=HTMLResponse, tags=["Signature"]) async def get_signature_opengraph(signature_id: UUID, request: Request, reg=Depends(get_tenant_registry)) -> str: contract = await Contract.find_by_signature_id(reg.db, signature_id) signature = contract.get_signature(signature_id) template = templates.get_template("opengraph.html") signatory = signature.representative.label if signature.representative else signature.entity.label return template.render({ "signatory": signatory, "title": contract.label, "origin_url": f"{request.url.scheme}://{request.url.hostname}" })