fix pylint errors
Some checks failed
Deploy Amap / deploy (push) Failing after 16s

This commit is contained in:
Julien Aldon
2026-03-03 11:08:08 +01:00
parent 0e48d1bbaa
commit 8352097ffb
60 changed files with 1288 additions and 612 deletions

View File

@@ -1,18 +1,27 @@
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from src.database import get_session
from sqlmodel import Session
from src.contracts.generate_contract import generate_html_contract, generate_recap
from src.auth.auth import get_current_user
import src.models as models
import src.messages as messages
import src.contracts.service as service
import src.forms.service as form_service
"""Router for contract resource"""
import io
import zipfile
import src.contracts.service as service
import src.forms.service as form_service
import src.messages as messages
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from sqlmodel import Session
from src import models
from src.auth.auth import get_current_user
from src.contracts.generate_contract import (generate_html_contract,
generate_recap)
from src.database import get_session
router = APIRouter(prefix='/contracts')
def compute_recurrent_prices(products_quantities: list[dict], nb_shipment: int):
def compute_recurrent_prices(
products_quantities: list[dict],
nb_shipment: int
):
"""Compute price for recurrent products"""
result = 0
for product_quantity in products_quantities:
product = product_quantity['product']
@@ -20,30 +29,50 @@ def compute_recurrent_prices(products_quantities: list[dict], nb_shipment: int):
result += compute_product_price(product, quantity, nb_shipment)
return result
def compute_occasional_prices(occasionals: list[dict]):
"""Compute prices for occassional products"""
result = 0
for occasional in occasionals:
result += occasional['price']
return result
def compute_product_price(product: models.Product, quantity: int, nb_shipment: int = 1):
product_quantity_unit = 1 if product.unit == models.Unit.KILO else 1000
final_quantity = quantity if product.price else quantity / product_quantity_unit
final_price = product.price if product.price else product.price_kg
return final_price * final_quantity * nb_shipment
def compute_product_price(
product: models.Product,
quantity: int,
nb_shipment: int = 1
):
"""Compute price for a product"""
product_quantity_unit = (
1 if product.unit == models.Unit.KILO else 1000
)
final_quantity = (
quantity if product.price else quantity / product_quantity_unit
)
final_price = (
product.price if product.price else product.price_kg
)
return final_price * final_quantity * nb_shipment
def find_dict_in_list(lst, key, value):
"""Find the index of a dictionnary in a list of dictionnaries given a key
and a value.
"""
for i, dic in enumerate(lst):
if dic[key].id == value:
return i
return -1
def create_occasional_dict(contract_products: list[models.ContractProduct]):
"""Create a dictionnary of occasional products"""
result = []
for contract_product in contract_products:
existing_id = find_dict_in_list(
result,
'shipment',
result,
'shipment',
contract_product.shipment.id
)
if existing_id < 0:
@@ -69,18 +98,46 @@ def create_occasional_dict(contract_products: list[models.ContractProduct]):
)
return result
@router.post('')
async def create_contract(
contract: models.ContractCreate,
session: Session = Depends(get_session),
):
"""Create contract route"""
new_contract = service.create_one(session, contract)
occasional_contract_products = list(filter(lambda contract_product: contract_product.product.type == models.ProductType.OCCASIONAL, new_contract.products))
occasional_contract_products = list(
filter(
lambda contract_product: (
contract_product.product.type == models.ProductType.OCCASIONAL
),
new_contract.products
)
)
occasionals = create_occasional_dict(occasional_contract_products)
recurrents = list(map(lambda x: {"product": x.product, "quantity": x.quantity}, filter(lambda contract_product: contract_product.product.type == models.ProductType.RECCURENT, new_contract.products)))
recurrent_price = compute_recurrent_prices(recurrents, len(new_contract.form.shipments))
recurrents = list(
map(
lambda x: {'product': x.product, 'quantity': x.quantity},
filter(
lambda contract_product: (
contract_product.product.type ==
models.ProductType.RECCURENT
),
new_contract.products
)
)
)
recurrent_price = compute_recurrent_prices(
recurrents,
len(new_contract.form.shipments)
)
price = recurrent_price + compute_occasional_prices(occasionals)
cheques = list(map(lambda x: {"name": x.name, "value": x.value}, new_contract.cheques))
cheques = list(
map(
lambda x: {'name': x.name, 'value': x.value},
new_contract.cheques
)
)
try:
pdf_bytes = generate_html_contract(
new_contract,
@@ -91,43 +148,63 @@ async def create_contract(
'{:10.2f}'.format(price)
)
pdf_file = io.BytesIO(pdf_bytes)
contract_id = f'{new_contract.firstname}_{new_contract.lastname}_{new_contract.form.productor.type}_{new_contract.form.season}'
contract_id = (
f'{new_contract.firstname}_'
f'{new_contract.lastname}_'
f'{new_contract.form.productor.type}_'
f'{new_contract.form.season}'
)
service.add_contract_file(session, new_contract.id, pdf_bytes, price)
except Exception:
raise HTTPException(status_code=400, detail=messages.pdferror)
except Exception as error:
raise HTTPException(
status_code=400,
detail=messages.pdferror
) from error
return StreamingResponse(
pdf_file,
media_type='application/pdf',
headers={
'Content-Disposition': f'attachment; filename=contract_{contract_id}.pdf'
'Content-Disposition': (
f'attachment; filename=contract_{contract_id}.pdf'
)
}
)
@router.get('/{form_id}/base')
async def get_base_contract_template(
form_id: int,
session: Session = Depends(get_session),
):
"""Get contract template route"""
form = form_service.get_one(session, form_id)
recurrents = list(map(lambda x: {"product": x, "quantity": None}, filter(lambda product: product.type == models.ProductType.RECCURENT, form.productor.products)))
recurrents = [
{'product': product, 'quantity': None}
for product in form.productor.products
if product.type == models.ProductType.RECCURENT
]
occasionals = [{
'shipment': sh,
'price': None,
'shipment': sh,
'price': None,
'products': [{'product': pr, 'quantity': None} for pr in sh.products]
} for sh in form.shipments]
empty_contract = models.ContractPublic(
firstname="",
firstname='',
form=form,
lastname="",
email="",
phone="",
lastname='',
email='',
phone='',
products=[],
payment_method="cheque",
payment_method='cheque',
cheque_quantity=3,
total_price=0,
id=1
)
cheques = [{"name": None, "value": None}, {"name": None, "value": None}, {"name": None, "value": None}]
cheques = [
{'name': None, 'value': None},
{'name': None, 'value': None},
{'name': None, 'value': None}
]
try:
pdf_bytes = generate_html_contract(
empty_contract,
@@ -136,45 +213,68 @@ async def get_base_contract_template(
recurrents,
)
pdf_file = io.BytesIO(pdf_bytes)
contract_id = f'{empty_contract.form.productor.type}_{empty_contract.form.season}'
except Exception as e:
print(e)
raise HTTPException(status_code=400, detail=messages.pdferror)
contract_id = (
f'{empty_contract.form.productor.type}_'
f'{empty_contract.form.season}'
)
except Exception as error:
raise HTTPException(
status_code=400,
detail=messages.pdferror
) from error
return StreamingResponse(
pdf_file,
media_type='application/pdf',
headers={
'Content-Disposition': f'attachment; filename=contract_{contract_id}.pdf'
'Content-Disposition': (
f'attachment; filename=contract_{contract_id}.pdf'
)
}
)
@router.get('', response_model=list[models.ContractPublic])
def get_contracts(
forms: list[str] = Query([]),
session: Session = Depends(get_session),
user: models.User = Depends(get_current_user)
):
"""Get all contracts route"""
return service.get_all(session, user, forms)
@router.get('/{id}/file')
@router.get('/{_id}/file')
def get_contract_file(
id: int,
_id: int,
session: Session = Depends(get_session),
user: models.User = Depends(get_current_user)
):
if not service.is_allowed(session, user, id):
raise HTTPException(status_code=403, detail=messages.Messages.not_allowed('contract', 'get'))
contract = service.get_one(session, id)
"""Get a contract file (in pdf) route"""
if not service.is_allowed(session, user, _id):
raise HTTPException(
status_code=403,
detail=messages.Messages.not_allowed('contract', 'get')
)
contract = service.get_one(session, _id)
if contract is None:
raise HTTPException(status_code=404, detail=messages.Messages.not_found('contract'))
filename = f'{contract.form.name.replace(' ', '_')}_{contract.form.season}_{contract.firstname}-{contract.lastname}'
raise HTTPException(
status_code=404,
detail=messages.Messages.not_found('contract')
)
filename = (
f'{contract.form.name.replace(' ', '_')}_'
f'{contract.form.season}_'
f'{contract.firstname}_'
f'{contract.lastname}'
)
return StreamingResponse(
io.BytesIO(contract.file),
media_type='application/pdf',
headers={
'Content-Disposition': f'attachment; filename={filename}.pdf'
}
)
)
@router.get('/{form_id}/files')
def get_contract_files(
@@ -182,17 +282,30 @@ def get_contract_files(
session: Session = Depends(get_session),
user: models.User = Depends(get_current_user)
):
"""Get all contract files for a given form"""
if not form_service.is_allowed(session, user, form_id):
raise HTTPException(status_code=403, detail=messages.Messages.not_allowed('contracts', 'get'))
raise HTTPException(
status_code=403,
detail=messages.Messages.not_allowed('contracts', 'get')
)
form = form_service.get_one(session, form_id=form_id)
contracts = service.get_all(session, user, forms=[form.name])
zipped_contracts = io.BytesIO()
with zipfile.ZipFile(zipped_contracts, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
with zipfile.ZipFile(
zipped_contracts,
'a',
zipfile.ZIP_DEFLATED,
False
) as zip_file:
for contract in contracts:
contract_filename = f'{contract.form.name.replace(' ', '_')}_{contract.form.season}_{contract.firstname}-{contract.lastname}.pdf'
contract_filename = (
f'{contract.form.name.replace(' ', '_')}_'
f'{contract.form.season}_'
f'{contract.firstname}_'
f'{contract.lastname}'
)
zip_file.writestr(contract_filename, contract.file)
filename = f'{form.name.replace(" ", "_")}_{form.season}'
filename = f'{form.name.replace(' ', '_')}_{form.season}'
return StreamingResponse(
io.BytesIO(zipped_contracts.getvalue()),
media_type='application/zip',
@@ -201,39 +314,69 @@ def get_contract_files(
}
)
@router.get('/{form_id}/recap')
def get_contract_recap(
form_id: int,
session: Session = Depends(get_session),
user: models.User = Depends(get_current_user)
):
"""Get a contract recap for a given form"""
if not form_service.is_allowed(session, user, form_id):
raise HTTPException(status_code=403, detail=messages.Messages.not_allowed('contract recap', 'get'))
raise HTTPException(
status_code=403,
detail=messages.Messages.not_allowed('contract recap', 'get')
)
form = form_service.get_one(session, form_id=form_id)
contracts = service.get_all(session, user, forms=[form.name])
return StreamingResponse(
io.BytesIO(generate_recap(contracts, form)),
media_type='application/zip',
headers={
'Content-Disposition': f'attachment; filename=filename.ods'
'Content-Disposition': (
'attachment; filename=filename.ods'
)
}
)
@router.get('/{id}', response_model=models.ContractPublic)
def get_contract(id: int, session: Session = Depends(get_session), user: models.User = Depends(get_current_user)):
if not service.is_allowed(session, user, id):
raise HTTPException(status_code=403, detail=messages.Messages.not_allowed('contract', 'get'))
result = service.get_one(session, id)
@router.get('/{_id}', response_model=models.ContractPublic)
def get_contract(
_id: int,
session: Session = Depends(get_session),
user: models.User = Depends(get_current_user)
):
"""Get a contract route"""
if not service.is_allowed(session, user, _id):
raise HTTPException(
status_code=403,
detail=messages.Messages.not_allowed('contract', 'get')
)
result = service.get_one(session, _id)
if result is None:
raise HTTPException(status_code=404, detail=messages.Messages.not_found('contract'))
raise HTTPException(
status_code=404,
detail=messages.Messages.not_found('contract')
)
return result
@router.delete('/{id}', response_model=models.ContractPublic)
def delete_contract(id: int, session: Session = Depends(get_session), user: models.User = Depends(get_current_user)):
if not service.is_allowed(session, user, id):
raise HTTPException(status_code=403, detail=messages.Messages.not_allowed('contract', 'delete'))
result = service.delete_one(session, id)
@router.delete('/{_id}', response_model=models.ContractPublic)
def delete_contract(
_id: int,
session: Session = Depends(get_session),
user: models.User = Depends(get_current_user)
):
"""Delete contract route"""
if not service.is_allowed(session, user, _id):
raise HTTPException(
status_code=403,
detail=messages.Messages.not_allowed('contract', 'delete')
)
result = service.delete_one(session, _id)
if result is None:
raise HTTPException(status_code=404, detail=messages.Messages.not_found('contract'))
raise HTTPException(
status_code=404,
detail=messages.Messages.not_found('contract')
)
return result