From ea5093f2c262ab58c56b0003d101657f2370eacd Mon Sep 17 00:00:00 2001 From: ewandor Date: Sun, 4 May 2025 02:26:06 +0200 Subject: [PATCH] Handling mongo indexes in firm --- api/rpk-api/firm/contract/models.py | 26 +++++------------ api/rpk-api/firm/core/models.py | 18 ++++++++---- api/rpk-api/firm/current_firm/__init__.py | 6 +++- api/rpk-api/firm/db.py | 3 -- api/rpk-api/firm/entity/models.py | 29 +++++++------------ api/rpk-api/firm/init_db.py | 31 +++++++++++++++++++++ api/rpk-api/firm/template/models.py | 6 ---- api/rpk-api/main.py | 2 +- gui/rpk-gui/public/locales/frFR/common.json | 2 +- 9 files changed, 67 insertions(+), 56 deletions(-) create mode 100644 api/rpk-api/firm/init_db.py diff --git a/api/rpk-api/firm/contract/models.py b/api/rpk-api/firm/contract/models.py index f135cd1..8fbe51e 100644 --- a/api/rpk-api/firm/contract/models.py +++ b/api/rpk-api/firm/contract/models.py @@ -7,7 +7,8 @@ from beanie import PydanticObjectId from pydantic import BaseModel, Field, ConfigDict from pydantic.json_schema import SkipJsonSchema -from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry, ForeignKey +from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry, ForeignKey, \ + CrudDocumentConfig from firm.core.filter import Filter, FilterSchema from firm.entity.models import Entity @@ -98,15 +99,6 @@ class ContractDraft(CrudDocument): status: ContractDraftStatus = Field(default=ContractDraftStatus.in_progress, title="Statut") todo: List[str] = Field(default=[], title="Reste à faire") - class Settings(CrudDocument.Settings): - fulltext_search = ['name', 'title'] - - bson_encoders = { - datetime.date: lambda dt: dt if hasattr(dt, 'hour') - else datetime.datetime(year=dt.year, month=dt.month, day=dt.day, - hour=0, minute=0, second=0) - } - async def check_is_ready(self, db): if self.status == ContractDraftStatus.published: return @@ -150,6 +142,11 @@ class Contract(CrudDocument): Contrat publié. Les contrats ne peuvent pas être modifiés. Ils peuvent seulement être signés par les parties et imprimés par l'avocat """ + model_config = ConfigDict(title='Contrat') + document_config = CrudDocumentConfig( + indexes=["parties.signature_uuid"], + ) + name: str = Field(title="Nom") title: str = Field(title="Titre") parties: List[Party] = Field(title="Parties") @@ -170,15 +167,6 @@ class Contract(CrudDocument): contract_label = f"{contract_label} - {self.date.strftime('%m/%d/%Y')}" return contract_label - class Settings(CrudDocument.Settings): - fulltext_search = ['name', 'title'] - - bson_encoders = { - datetime.date: lambda dt: dt if hasattr(dt, 'hour') - else datetime.datetime(year=dt.year, month=dt.month, day=dt.day, - hour=0, minute=0, second=0) - } - @classmethod async def find_by_signature_id(cls, db, signature_id: UUID): request = {'parties': {"$elemMatch": {"signature_uuid": str(signature_id) }}} diff --git a/api/rpk-api/firm/core/models.py b/api/rpk-api/firm/core/models.py index f0cf5ff..8a2321c 100644 --- a/api/rpk-api/firm/core/models.py +++ b/api/rpk-api/firm/core/models.py @@ -1,12 +1,18 @@ from datetime import datetime, UTC -from typing import Optional +from typing import Optional, TypedDict, ClassVar from beanie import PydanticObjectId from motor.motor_asyncio import AsyncIOMotorCollection from pydantic import BaseModel, Field, computed_field +class CrudDocumentConfig(TypedDict, total=False): + fulltext_search: list[str] + indexes: list[str] + class CrudDocument(BaseModel): + document_config: ClassVar[CrudDocumentConfig] = CrudDocumentConfig() + id: Optional[PydanticObjectId] = Field(default=None) created_at: datetime = Field(default=datetime.now(UTC), nullable=False, title="Créé le") created_by: Optional[PydanticObjectId] = Field(default=None, title="Créé par") @@ -17,16 +23,13 @@ class CrudDocument(BaseModel): def _id(self): return self.id - @computed_field + @computed_field(title="Label") def label(self) -> str: return self.compute_label() def compute_label(self) -> str: return "" - class Settings: - fulltext_search = [] - @classmethod def _collection_name(cls): return cls.__name__ @@ -35,6 +38,10 @@ class CrudDocument(BaseModel): def _get_collection(cls, db) -> AsyncIOMotorCollection: return db.get_collection(cls._collection_name()) + @classmethod + def create_index(cls, db, index): + cls._get_collection(db).create_index(index) + @classmethod async def create(cls, db, create_schema): model_dict = create_schema.model_dump() | {"created_by": db.partner.id, "updated_by": db.partner.id} @@ -84,7 +91,6 @@ class CrudDocument(BaseModel): await cls._get_collection(db).delete_one({"_id": model.id}) - def text_area(*args, **kwargs): kwargs['widget'] = { "formlyConfig": { diff --git a/api/rpk-api/firm/current_firm/__init__.py b/api/rpk-api/firm/current_firm/__init__.py index c8093b4..79a05de 100644 --- a/api/rpk-api/firm/current_firm/__init__.py +++ b/api/rpk-api/firm/current_firm/__init__.py @@ -3,7 +3,7 @@ from typing import Any from beanie import PydanticObjectId from pydantic import Field -from firm.core.models import CrudDocument +from firm.core.models import CrudDocument, CrudDocumentConfig from firm.core.schemas import Writer, Reader from firm.entity.schemas import EntityIndividualCreate, EntityCorporationCreate, EntityRead @@ -59,6 +59,10 @@ class CurrentFirmSchemaUpdate(Writer): pass class Partner(CrudDocument): + document_config = CrudDocumentConfig( + indexes=["user_id", "entity_id"], + ) + user_id: PydanticObjectId = Field() entity_id: PydanticObjectId = Field() diff --git a/api/rpk-api/firm/db.py b/api/rpk-api/firm/db.py index 7f199c3..9b9b427 100644 --- a/api/rpk-api/firm/db.py +++ b/api/rpk-api/firm/db.py @@ -11,9 +11,6 @@ client = motor.motor_asyncio.AsyncIOMotorClient( DATABASE_URL, uuidRepresentation="standard" ) -async def init_db(): - pass - async def stop_db(): client.close() diff --git a/api/rpk-api/firm/entity/models.py b/api/rpk-api/firm/entity/models.py index 3da0d5a..a9ec774 100644 --- a/api/rpk-api/firm/entity/models.py +++ b/api/rpk-api/firm/entity/models.py @@ -1,8 +1,8 @@ -from datetime import date, datetime +from datetime import date from typing import List, Literal, Optional from pydantic import Field, BaseModel, ConfigDict -from beanie import Indexed, PydanticObjectId +from beanie import PydanticObjectId from firm.core.models import CrudDocument, ForeignKey from firm.core.filter import Filter, FilterSchema @@ -18,10 +18,10 @@ class Individual(EntityType): model_config = ConfigDict(title='Particulier') type: Literal['individual'] = 'individual' - firstname: Indexed(str) = Field(title='Prénom') - middlename: Indexed(str) = Field(default="", title='Autres prénoms') - lastname: Indexed(str) = Field(title='Nom de famille') - surnames: List[Indexed(str)] = Field( + firstname: str = Field(title='Prénom') + middlename: str = Field(default="", title='Autres prénoms') + lastname: str = Field(title='Nom de famille') + surnames: List[str] = Field( default=[], props={"items_per_row": "4", "numbered": True}, title="Surnoms" @@ -32,14 +32,14 @@ class Individual(EntityType): @property def label(self) -> str: # if len(self.surnames) > 0: - # return '{} "{}" {}'.format(self.firstname, self.surnames[0], self.lastname) + # return f"{self.firstname} \"{self.surnames[0]}\" {self.lastname}" return f"{self.firstname} {self.lastname}" class Employee(BaseModel): model_config = ConfigDict(title='Fiche Employé') - position: Indexed(str) = Field(title='Poste') + position: str = Field(title='Poste') entity_id: PydanticObjectId = ForeignKey("entities", "Entity", title='Employé') @@ -47,8 +47,8 @@ class Corporation(EntityType): model_config = ConfigDict(title='Entreprise') type: Literal['corporation'] = 'corporation' - title: Indexed(str) = Field(title='Dénomination sociale') - activity: Indexed(str) = Field(title='Activité') + title: str = Field(title='Dénomination sociale') + activity: str = Field(title='Activité') employees: List[Employee] = Field(default=[], title='Employés') @@ -72,15 +72,6 @@ class Entity(CrudDocument): return "" return self.entity_data.label - class Settings(CrudDocument.Settings): - fulltext_search = ['label'] - - bson_encoders = { - date: lambda dt: dt if hasattr(dt, 'hour') - else datetime(year=dt.year, month=dt.month, day=dt.day, - hour=0, minute=0, second=0) - } - class EntityFilters(FilterSchema): class Constants(Filter.Constants): diff --git a/api/rpk-api/firm/init_db.py b/api/rpk-api/firm/init_db.py new file mode 100644 index 0000000..1ec0b5d --- /dev/null +++ b/api/rpk-api/firm/init_db.py @@ -0,0 +1,31 @@ +import logging + +from firm.contract.models import Contract, ContractDraft +from firm.core.depends import Registry +from firm.current_firm import CurrentFirm, Partner +from firm.db import client +from firm.entity.models import Entity +from firm.template.models import ContractTemplate, ProvisionTemplate + +from hub.firm import Firm + +collections = [CurrentFirm, Entity, Partner, Contract, ContractDraft, ContractTemplate, ProvisionTemplate] + +logger = logging.getLogger('uvicorn.error') + +async def create_documents_indexes(db): + for collection in collections: + if "indexes" in collection.document_config: + for field in collection.document_config["indexes"]: + collection.create_index(db, field) + +async def init_all_db(): + logger.info("[FRM] Creating index for firms") + for firm in await Firm.find({}).to_list(): + reg = Registry(client, firm.instance, firm.firm) + await reg.current_firm + await create_documents_indexes(reg.db) + + +async def init_db(): + await init_all_db() diff --git a/api/rpk-api/firm/template/models.py b/api/rpk-api/firm/template/models.py index d8e131f..dac7702 100644 --- a/api/rpk-api/firm/template/models.py +++ b/api/rpk-api/firm/template/models.py @@ -36,9 +36,6 @@ class ProvisionTemplate(CrudDocument): def compute_label(self) -> str: return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\"" - class Settings(CrudDocument.Settings): - fulltext_search = ['name', 'title', 'body'] - class ProvisionTemplateReference(BaseModel): model_config = ConfigDict(title="Clause") @@ -67,9 +64,6 @@ class ContractTemplate(CrudDocument): def compute_label(self) -> str: return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\"" - class Settings(CrudDocument.Settings): - fulltext_search = ['name', 'title'] - class ContractTemplateFilters(FilterSchema): class Constants(Filter.Constants): diff --git a/api/rpk-api/main.py b/api/rpk-api/main.py index 5d23d25..70f8569 100644 --- a/api/rpk-api/main.py +++ b/api/rpk-api/main.py @@ -5,7 +5,7 @@ from hub import hub_router from hub.db import init_db as hub_init_db from firm import firm_router -from firm.db import init_db as firm_init_db +from firm.init_db import init_db as firm_init_db if __name__ == '__main__': diff --git a/gui/rpk-gui/public/locales/frFR/common.json b/gui/rpk-gui/public/locales/frFR/common.json index c1c9f8f..94456ac 100644 --- a/gui/rpk-gui/public/locales/frFR/common.json +++ b/gui/rpk-gui/public/locales/frFR/common.json @@ -189,7 +189,7 @@ }, "corporation": { "type": "Entreprise", - "title": "Titre", + "title": "Dénomination sociale", "activity": "Activité", "employees": "Employés", "resource_title": "Entreprise"