6 Commits

Author SHA1 Message Date
c7335af514 Replacing reverse proxy from nginx to traefik 2025-03-28 13:41:58 +01:00
ff78f9da54 Migrating to fastapi-pagination 2025-03-17 17:46:04 +01:00
3a14528402 upgrading libraries 2025-03-17 16:58:15 +01:00
5c276faf78 Folding lists and opened variables 2023-03-27 01:29:56 +02:00
95b17947b2 Removing accordion on contract signature page 2023-03-27 01:00:11 +02:00
f20635e10e Spacing printed lis 2023-03-27 00:55:24 +02:00
12 changed files with 98 additions and 90 deletions

View File

@@ -1,4 +1,4 @@
FROM python:3.10 FROM python:3.13
RUN apt update && apt install -y xfonts-base xfonts-75dpi python3-pip python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0 \ RUN apt update && apt install -y xfonts-base xfonts-75dpi python3-pip python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*

View File

@@ -1,5 +1,5 @@
import datetime import datetime
from typing import List, Literal from typing import List, Literal, Optional
from enum import Enum from enum import Enum
from pydantic import BaseModel, Field, validator from pydantic import BaseModel, Field, validator
@@ -52,10 +52,10 @@ class DraftParty(BaseModel):
class Party(BaseModel): class Party(BaseModel):
entity: Entity entity: Entity
part: str part: str
representative: Entity = None representative: Optional[Entity] = None
signature_uuid: str signature_uuid: str
signature_affixed: bool = False signature_affixed: bool = False
signature_png: str = None signature_png: Optional[str] = None
class ProvisionGenuine(BaseModel): class ProvisionGenuine(BaseModel):
@@ -181,7 +181,7 @@ class Contract(CrudDocument):
lawyer: Entity = Field(title="Avocat en charge") lawyer: Entity = Field(title="Avocat en charge")
location: str = Field(title="Lieu") location: str = Field(title="Lieu")
date: datetime.date = Field(title="Date") date: datetime.date = Field(title="Date")
label: str = None label: Optional[str] = None
@validator("label", always=True) @validator("label", always=True)
def generate_label(cls, v, values, **kwargs): def generate_label(cls, v, values, **kwargs):

View File

@@ -117,6 +117,10 @@ p {
text-align: justify; text-align: justify;
} }
li {
margin: 16px 0;
}
.footer { .footer {
margin-top: 30px; margin-top: 30px;
page-break-inside: avoid; page-break-inside: avoid;

View File

@@ -3,8 +3,8 @@ from beanie.odm.operators.find.comparison import In
from beanie.operators import And, RegEx, Eq from beanie.operators import And, RegEx, Eq
from fastapi import APIRouter, HTTPException, Depends from fastapi import APIRouter, HTTPException, Depends
from fastapi_paginate import Page, Params, add_pagination from fastapi_pagination import Page, Params, add_pagination
from fastapi_paginate.ext.motor import paginate from fastapi_pagination.ext.beanie import paginate
from ..user.manager import get_current_user, get_current_superuser from ..user.manager import get_current_user, get_current_superuser
@@ -79,8 +79,7 @@ def get_crud_router(model, model_create, model_read, model_update):
sort = parse_sort(sort_by) sort = parse_sort(sort_by)
query = parse_query(query, model_read) query = parse_query(query, model_read)
collection = model.get_motor_collection() items = paginate(model.find(query), Params(**{'size': size, 'page': page}))
items = paginate(collection, query, Params(**{'size': size, 'page': page}), sort=sort)
return await items return await items
@router.put("/{id}", response_description="{} record updated".format(model.__name__)) @router.put("/{id}", response_description="{} record updated".format(model.__name__))

View File

@@ -23,8 +23,8 @@ class Individual(EntityType):
props={"items-per-row": "4", "numbered": True}, props={"items-per-row": "4", "numbered": True},
title="Surnoms" title="Surnoms"
) )
day_of_birth: date = Field(default=None, title='Date de naissance') day_of_birth: Optional[date] = Field(default=None, title='Date de naissance')
place_of_birth: str = Field(default="", title='Lieu de naissance') place_of_birth: Optional[str] = Field(default="", title='Lieu de naissance')
@property @property
def label(self) -> str: def label(self) -> str:

View File

@@ -1,7 +1,7 @@
from typing import Optional, TypeVar from typing import Optional, TypeVar
from datetime import datetime from datetime import datetime
from pydantic import Field from pydantic import Field
from beanie import PydanticObjectId from beanie import Document
from fastapi_users.db import BeanieBaseUser, BeanieUserDatabase from fastapi_users.db import BeanieBaseUser, BeanieUserDatabase
from fastapi_users_db_beanie.access_token import BeanieAccessTokenDatabase, BeanieBaseAccessToken from fastapi_users_db_beanie.access_token import BeanieAccessTokenDatabase, BeanieBaseAccessToken
@@ -9,11 +9,11 @@ from fastapi_users_db_beanie.access_token import BeanieAccessTokenDatabase, Bean
from pymongo import IndexModel from pymongo import IndexModel
class AccessToken(BeanieBaseAccessToken[PydanticObjectId]): class AccessToken(BeanieBaseAccessToken, Document):
pass pass
class User(BeanieBaseUser[PydanticObjectId]): class User(BeanieBaseUser, Document):
login: str login: str
entity_id: str entity_id: str
created_at: datetime = Field(default=datetime.utcnow(), nullable=False) created_at: datetime = Field(default=datetime.utcnow(), nullable=False)

View File

@@ -1,4 +1,6 @@
from pydantic import BaseModel from typing import Annotated
from pydantic import BaseModel, Field
from fastapi_users import schemas from fastapi_users import schemas
from .models import User from .models import User
@@ -9,12 +11,8 @@ class UserBase(schemas.CreateUpdateDictModel):
class UserRead(User): class UserRead(User):
class Config: _id: Annotated[str, Field(alias='id')]
fields = { hashed_password: Annotated[str, Field(exclude=True)]
'_id': {'alias': 'id'},
'hashed_password': {'exclude': True}
}
class UserCreate(UserBase): class UserCreate(UserBase):
login: str login: str

View File

@@ -1,8 +1,7 @@
fastapi==0.88.0 fastapi
fastapi_users==10.2.1 fastapi_users
fastapi_users_db_beanie==1.1.2 fastapi_users_db_beanie
motor==3.1.1 fastapi-pagination
fastapi-paginate==0.1.0
uvicorn uvicorn
jinja2 jinja2
weasyprint weasyprint

View File

@@ -1,4 +1,3 @@
version: "3.9"
services: services:
back: back:
build: build:
@@ -10,6 +9,11 @@ services:
volumes: volumes:
- ./back/app:/code/app - ./back/app:/code/app
- ./back/media:/code/media - ./back/media:/code/media
labels:
- "traefik.enable=true"
- "traefik.http.routers.back.entrypoints=web"
- "traefik.http.routers.back.rule=PathPrefix(`/api/v1/`)"
- "traefik.http.services.back.loadbalancer.server.port=8000"
front: front:
build: build:
@@ -21,12 +25,23 @@ services:
volumes: volumes:
- ./front/app/src:/app/src - ./front/app/src:/app/src
- ./front/app/public:/app/public - ./front/app/public:/app/public
labels:
- "traefik.enable=true"
- "traefik.http.routers.front.entrypoints=web"
- "traefik.http.routers.front.rule=PathPrefix(`/`)"
- "traefik.http.services.front.loadbalancer.server.port=4200"
nginx: proxy:
build: image: traefik
context: ./nginx
image: cht-lawfirm-nginx-dev
restart: always restart: always
command:
- --providers.docker
- --providers.docker.watch=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --log.level=DEBUG
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports: ports:
- "80:80" - "80:80"

View File

@@ -78,23 +78,12 @@ export class ContractsCardComponent extends BaseContractsComponent{
@Component({ @Component({
template: ` template: `
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-1"> <div>
<ngb-panel>
<ng-template ngbPanelTitle>
<span i18n>Preview</span>
</ng-template>
<ng-template ngbPanelContent>
<iframe width="100%" <iframe width="100%"
[src]="getPreview()" [src]="getPreview()"
onload='javascript:(function(o){o.style.height=o.contentWindow.document.body.scrollHeight+"px";o.style.width=o.contentWindow.document.body.scrollWidth+"px";}(this));' style="height:200px;width:100%;border:none;overflow:hidden;"></iframe> onload='javascript:(function(o){o.style.height=o.contentWindow.document.body.scrollHeight+"px";o.style.width=o.contentWindow.document.body.scrollWidth+"px";}(this));' style="height:200px;width:100%;border:none;overflow:hidden;">
</ng-template> </iframe>
</ngb-panel> </div>
<ngb-panel>
<ng-template ngbPanelTitle>
<span i18n>Signature</span>
</ng-template>
<ng-template ngbPanelContent>
<ng-container *ngIf="this.affixed"><ng-container i18n>This Contract has already been signed by</ng-container> {{ this.signatory }}</ng-container>
<div class="row" *ngIf="!this.affixed"> <div class="row" *ngIf="!this.affixed">
<signature-drawer class="col-7" <signature-drawer class="col-7"
(signatureDrawn$)="postSignature($event)"></signature-drawer> (signatureDrawn$)="postSignature($event)"></signature-drawer>
@@ -104,11 +93,7 @@ export class ContractsCardComponent extends BaseContractsComponent{
<p>En vous maintenant et/ou en interagissant avec cette page, vous enfreignez l'article L.229 du code pénal de l'Etat de San Andreas pour <strong>usurpation d'identité</strong> et vous vous exposez ainsi à une amende de 20 000$ ainsi qu'à des poursuites civiles.</p> <p>En vous maintenant et/ou en interagissant avec cette page, vous enfreignez l'article L.229 du code pénal de l'Etat de San Andreas pour <strong>usurpation d'identité</strong> et vous vous exposez ainsi à une amende de 20 000$ ainsi qu'à des poursuites civiles.</p>
<p>Le cabinet Cooper, Hillman & Toshi LLC</p> <p>Le cabinet Cooper, Hillman & Toshi LLC</p>
</div> </div>
</div> </div>`
</ng-template>
</ngb-panel>
</ngb-accordion>
`
}) })
export class ContractsSignatureComponent implements OnInit { export class ContractsSignatureComponent implements OnInit {
signature_id: string | null = null; signature_id: string | null = null;

View File

@@ -4,12 +4,17 @@ import { FieldArrayType } from '@ngx-formly/core';
@Component({ @Component({
selector: 'formly-array-type', selector: 'formly-array-type',
template: ` template: `
<div> <div class="mb-3">
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0">
<ngb-panel id="ngb-panel-0">
<ng-template ngbPanelTitle>
<label *ngIf="props.label" class="form-label">{{ props.label }}</label> <label *ngIf="props.label" class="form-label">{{ props.label }}</label>
<p *ngIf="props.description">{{ props.description }}</p> <p *ngIf="props.description">{{ props.description }}</p>
<div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors"> <div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors">
<formly-validation-message [field]="field"></formly-validation-message> <formly-validation-message [field]="field"></formly-validation-message>
</div> </div>
</ng-template>
<ng-template ngbPanelContent>
<div class="row row-cols-1 row-cols-md-{{this.itemsPerRow}} g-1"> <div class="row row-cols-1 row-cols-md-{{this.itemsPerRow}} g-1">
<div *ngFor="let entry of field.fieldGroup; let i = index" class="col"> <div *ngFor="let entry of field.fieldGroup; let i = index" class="col">
<div class="card"> <div class="card">
@@ -29,7 +34,10 @@ import { FieldArrayType } from '@ngx-formly/core';
</div> </div>
</div> </div>
</div> </div>
<button *ngIf="! this.field.props.readonly" class="btn btn-success col-sm-12" type="button" (click)="add()"><i-bs name="plus-square-fill"></i-bs></button> <button *ngIf="! this.field.props.readonly" class="btn btn-success col-sm-12 gap-3" type="button" (click)="add()"><i-bs name="plus-square-fill"></i-bs></button>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div> </div>
`, `,
}) })

View File

@@ -10,7 +10,7 @@ import {FormlyJsonschema} from "@ngx-formly/core/json-schema";
template: ` template: `
<div class="mb-3"> <div class="mb-3">
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0"> <ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0">
<ngb-panel> <ngb-panel id="ngb-panel-0">
<ng-template ngbPanelTitle> <ng-template ngbPanelTitle>
<label *ngIf="props.label && props['hideLabel'] !== true" [attr.for]="id" <label *ngIf="props.label && props['hideLabel'] !== true" [attr.for]="id"
class="form-label">{{ props.label }} class="form-label">{{ props.label }}