162 lines
5.7 KiB
Python
162 lines
5.7 KiB
Python
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}"
|
|
})
|