Handling mongo indexes in firm

This commit is contained in:
2025-05-04 02:26:06 +02:00
parent b542fd40a6
commit ea5093f2c2
9 changed files with 67 additions and 56 deletions

View File

@@ -7,7 +7,8 @@ from beanie import PydanticObjectId
from pydantic import BaseModel, Field, ConfigDict from pydantic import BaseModel, Field, ConfigDict
from pydantic.json_schema import SkipJsonSchema 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.core.filter import Filter, FilterSchema
from firm.entity.models import Entity from firm.entity.models import Entity
@@ -98,15 +99,6 @@ class ContractDraft(CrudDocument):
status: ContractDraftStatus = Field(default=ContractDraftStatus.in_progress, title="Statut") status: ContractDraftStatus = Field(default=ContractDraftStatus.in_progress, title="Statut")
todo: List[str] = Field(default=[], title="Reste à faire") 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): async def check_is_ready(self, db):
if self.status == ContractDraftStatus.published: if self.status == ContractDraftStatus.published:
return return
@@ -150,6 +142,11 @@ class Contract(CrudDocument):
Contrat publié. Les contrats ne peuvent pas être modifiés. 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 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") name: str = Field(title="Nom")
title: str = Field(title="Titre") title: str = Field(title="Titre")
parties: List[Party] = Field(title="Parties") parties: List[Party] = Field(title="Parties")
@@ -170,15 +167,6 @@ class Contract(CrudDocument):
contract_label = f"{contract_label} - {self.date.strftime('%m/%d/%Y')}" contract_label = f"{contract_label} - {self.date.strftime('%m/%d/%Y')}"
return contract_label 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 @classmethod
async def find_by_signature_id(cls, db, signature_id: UUID): async def find_by_signature_id(cls, db, signature_id: UUID):
request = {'parties': {"$elemMatch": {"signature_uuid": str(signature_id) }}} request = {'parties': {"$elemMatch": {"signature_uuid": str(signature_id) }}}

View File

@@ -1,12 +1,18 @@
from datetime import datetime, UTC from datetime import datetime, UTC
from typing import Optional from typing import Optional, TypedDict, ClassVar
from beanie import PydanticObjectId from beanie import PydanticObjectId
from motor.motor_asyncio import AsyncIOMotorCollection from motor.motor_asyncio import AsyncIOMotorCollection
from pydantic import BaseModel, Field, computed_field from pydantic import BaseModel, Field, computed_field
class CrudDocumentConfig(TypedDict, total=False):
fulltext_search: list[str]
indexes: list[str]
class CrudDocument(BaseModel): class CrudDocument(BaseModel):
document_config: ClassVar[CrudDocumentConfig] = CrudDocumentConfig()
id: Optional[PydanticObjectId] = Field(default=None) id: Optional[PydanticObjectId] = Field(default=None)
created_at: datetime = Field(default=datetime.now(UTC), nullable=False, title="Créé le") created_at: datetime = Field(default=datetime.now(UTC), nullable=False, title="Créé le")
created_by: Optional[PydanticObjectId] = Field(default=None, title="Créé par") created_by: Optional[PydanticObjectId] = Field(default=None, title="Créé par")
@@ -17,16 +23,13 @@ class CrudDocument(BaseModel):
def _id(self): def _id(self):
return self.id return self.id
@computed_field @computed_field(title="Label")
def label(self) -> str: def label(self) -> str:
return self.compute_label() return self.compute_label()
def compute_label(self) -> str: def compute_label(self) -> str:
return "" return ""
class Settings:
fulltext_search = []
@classmethod @classmethod
def _collection_name(cls): def _collection_name(cls):
return cls.__name__ return cls.__name__
@@ -35,6 +38,10 @@ class CrudDocument(BaseModel):
def _get_collection(cls, db) -> AsyncIOMotorCollection: def _get_collection(cls, db) -> AsyncIOMotorCollection:
return db.get_collection(cls._collection_name()) return db.get_collection(cls._collection_name())
@classmethod
def create_index(cls, db, index):
cls._get_collection(db).create_index(index)
@classmethod @classmethod
async def create(cls, db, create_schema): async def create(cls, db, create_schema):
model_dict = create_schema.model_dump() | {"created_by": db.partner.id, "updated_by": db.partner.id} 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}) await cls._get_collection(db).delete_one({"_id": model.id})
def text_area(*args, **kwargs): def text_area(*args, **kwargs):
kwargs['widget'] = { kwargs['widget'] = {
"formlyConfig": { "formlyConfig": {

View File

@@ -3,7 +3,7 @@ from typing import Any
from beanie import PydanticObjectId from beanie import PydanticObjectId
from pydantic import Field 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.core.schemas import Writer, Reader
from firm.entity.schemas import EntityIndividualCreate, EntityCorporationCreate, EntityRead from firm.entity.schemas import EntityIndividualCreate, EntityCorporationCreate, EntityRead
@@ -59,6 +59,10 @@ class CurrentFirmSchemaUpdate(Writer):
pass pass
class Partner(CrudDocument): class Partner(CrudDocument):
document_config = CrudDocumentConfig(
indexes=["user_id", "entity_id"],
)
user_id: PydanticObjectId = Field() user_id: PydanticObjectId = Field()
entity_id: PydanticObjectId = Field() entity_id: PydanticObjectId = Field()

View File

@@ -11,9 +11,6 @@ client = motor.motor_asyncio.AsyncIOMotorClient(
DATABASE_URL, uuidRepresentation="standard" DATABASE_URL, uuidRepresentation="standard"
) )
async def init_db():
pass
async def stop_db(): async def stop_db():
client.close() client.close()

View File

@@ -1,8 +1,8 @@
from datetime import date, datetime from datetime import date
from typing import List, Literal, Optional from typing import List, Literal, Optional
from pydantic import Field, BaseModel, ConfigDict 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.models import CrudDocument, ForeignKey
from firm.core.filter import Filter, FilterSchema from firm.core.filter import Filter, FilterSchema
@@ -18,10 +18,10 @@ class Individual(EntityType):
model_config = ConfigDict(title='Particulier') model_config = ConfigDict(title='Particulier')
type: Literal['individual'] = 'individual' type: Literal['individual'] = 'individual'
firstname: Indexed(str) = Field(title='Prénom') firstname: str = Field(title='Prénom')
middlename: Indexed(str) = Field(default="", title='Autres prénoms') middlename: str = Field(default="", title='Autres prénoms')
lastname: Indexed(str) = Field(title='Nom de famille') lastname: str = Field(title='Nom de famille')
surnames: List[Indexed(str)] = Field( surnames: List[str] = Field(
default=[], default=[],
props={"items_per_row": "4", "numbered": True}, props={"items_per_row": "4", "numbered": True},
title="Surnoms" title="Surnoms"
@@ -32,14 +32,14 @@ class Individual(EntityType):
@property @property
def label(self) -> str: def label(self) -> str:
# if len(self.surnames) > 0: # 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}" return f"{self.firstname} {self.lastname}"
class Employee(BaseModel): class Employee(BaseModel):
model_config = ConfigDict(title='Fiche Employé') 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é') entity_id: PydanticObjectId = ForeignKey("entities", "Entity", title='Employé')
@@ -47,8 +47,8 @@ class Corporation(EntityType):
model_config = ConfigDict(title='Entreprise') model_config = ConfigDict(title='Entreprise')
type: Literal['corporation'] = 'corporation' type: Literal['corporation'] = 'corporation'
title: Indexed(str) = Field(title='Dénomination sociale') title: str = Field(title='Dénomination sociale')
activity: Indexed(str) = Field(title='Activité') activity: str = Field(title='Activité')
employees: List[Employee] = Field(default=[], title='Employés') employees: List[Employee] = Field(default=[], title='Employés')
@@ -72,15 +72,6 @@ class Entity(CrudDocument):
return "" return ""
return self.entity_data.label 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 EntityFilters(FilterSchema):
class Constants(Filter.Constants): class Constants(Filter.Constants):

View File

@@ -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()

View File

@@ -36,9 +36,6 @@ class ProvisionTemplate(CrudDocument):
def compute_label(self) -> str: def compute_label(self) -> str:
return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\"" return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\""
class Settings(CrudDocument.Settings):
fulltext_search = ['name', 'title', 'body']
class ProvisionTemplateReference(BaseModel): class ProvisionTemplateReference(BaseModel):
model_config = ConfigDict(title="Clause") model_config = ConfigDict(title="Clause")
@@ -67,9 +64,6 @@ class ContractTemplate(CrudDocument):
def compute_label(self) -> str: def compute_label(self) -> str:
return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\"" return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\""
class Settings(CrudDocument.Settings):
fulltext_search = ['name', 'title']
class ContractTemplateFilters(FilterSchema): class ContractTemplateFilters(FilterSchema):
class Constants(Filter.Constants): class Constants(Filter.Constants):

View File

@@ -5,7 +5,7 @@ from hub import hub_router
from hub.db import init_db as hub_init_db from hub.db import init_db as hub_init_db
from firm import firm_router 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__': if __name__ == '__main__':

View File

@@ -189,7 +189,7 @@
}, },
"corporation": { "corporation": {
"type": "Entreprise", "type": "Entreprise",
"title": "Titre", "title": "Dénomination sociale",
"activity": "Activité", "activity": "Activité",
"employees": "Employés", "employees": "Employés",
"resource_title": "Entreprise" "resource_title": "Entreprise"