Coverage for src/paperap/const.py: 92%
193 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-18 12:26 -0400
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-18 12:26 -0400
1"""
2----------------------------------------------------------------------------
4 METADATA:
6 File: const.py
7 Project: paperap
8 Created: 2025-03-04
9 Version: 0.0.7
10 Author: Jess Mann
11 Email: jess@jmann.me
12 Copyright (c) 2025 Jess Mann
14----------------------------------------------------------------------------
16 LAST MODIFIED:
18 2025-03-04 By Jess Mann
20"""
22from __future__ import annotations
24import logging
25from datetime import datetime
26from enum import Enum, IntEnum, StrEnum
27from string import Template
28from typing import Any, Literal, NotRequired, Required, Self, TypedDict, override
30import pydantic
31from pydantic import ConfigDict, Field
33logger = logging.getLogger(__name__)
36class ConstModel(pydantic.BaseModel):
37 model_config = ConfigDict(
38 from_attributes=True,
39 extra="forbid",
40 use_enum_values=True,
41 validate_default=True,
42 validate_assignment=True,
43 )
45 @override
46 def __eq__(self, other: Any) -> bool:
47 if isinstance(other, dict):
48 # Ensure the dictionary keys match the model fields
49 expected_keys = set(self.model_fields.keys())
50 if set(other.keys()) != expected_keys:
51 return False
52 return all(getattr(self, key) == other.get(key) for key in expected_keys)
54 # This check probably isn't necessary before calling super (TODO?)
55 if isinstance(other, self.__class__):
56 # Compare all fields of the model
57 return self.model_dump() == other.model_dump()
59 return super().__eq__(other)
62class URLS:
63 index: Template = Template("/api/")
64 token: Template = Template("/api/token/")
65 list: Template = Template("/api/${resource}/")
66 detail: Template = Template("/api/${resource}/${pk}/")
67 create: Template = Template("/api/${resource}/")
68 update: Template = Template("/api/${resource}/${pk}/")
69 delete: Template = Template("/api/${resource}/${pk}/")
70 download: Template = Template("/api/document/${pk}/download/")
71 meta: Template = Template("/api/document/${pk}/metadata/")
72 next_asn: Template = Template("/api/document/next_asn/")
73 notes: Template = Template("/api/document/${pk}/notes/")
74 preview: Template = Template("/api/document/${pk}/preview/")
75 thumbnail: Template = Template("/api/document/${pk}/thumb/")
76 post: Template = Template("/api/document/post_document/")
77 single: Template = Template("/api/document/${pk}/")
78 suggestions: Template = Template("/api/document/${pk}/suggestions/")
81class Endpoints(TypedDict, total=False):
82 list: Required[Template]
83 detail: NotRequired[Template]
84 create: NotRequired[Template]
85 update: NotRequired[Template]
86 delete: NotRequired[Template]
89class FilteringStrategies(StrEnum):
90 WHITELIST = "whitelist"
91 BLACKLIST = "blacklist"
92 ALLOW_ALL = "allow_all"
93 ALLOW_NONE = "allow_none"
96class ModelStatus(StrEnum):
97 INITIALIZING = "initializing"
98 UPDATING = "updating"
99 SAVING = "saving"
100 READY = "ready"
101 ERROR = "error"
104class CustomFieldTypes(StrEnum):
105 STRING = "string"
106 BOOLEAN = "boolean"
107 INTEGER = "integer"
108 FLOAT = "float"
109 MONETARY = "monetary"
110 DATE = "date"
111 URL = "url"
112 DOCUMENT_LINK = "documentlink"
113 UNKNOWN = "unknown"
115 @override
116 @classmethod
117 def _missing_(cls, value: object) -> "Literal[CustomFieldTypes.UNKNOWN]":
118 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
119 return cls.UNKNOWN
122class CustomFieldValues(ConstModel):
123 field: int
124 value: Any
127class CustomFieldTypedDict(TypedDict):
128 field: int
129 value: Any
132class DocumentMetadataType(ConstModel):
133 namespace: str | None = None
134 prefix: str | None = None
135 key: str | None = None
136 value: str | None = None
139class DocumentSearchHitType(ConstModel):
140 score: float | None = None
141 highlights: str | None = None
142 note_highlights: str | None = None
143 rank: int | None = None
146class MatchingAlgorithmType(IntEnum):
147 NONE = 0
148 ANY = 1
149 ALL = 2
150 LITERAL = 3
151 REGEX = 4
152 FUZZY = 5
153 AUTO = 6
154 UNKNOWN = -1
156 @override
157 @classmethod
158 def _missing_(cls, value: object) -> "Literal[MatchingAlgorithmType.UNKNOWN]":
159 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
160 return cls.UNKNOWN
163class PermissionSetType(ConstModel):
164 users: list[int] = Field(default_factory=list)
165 groups: list[int] = Field(default_factory=list)
168class PermissionTableType(ConstModel):
169 view: PermissionSetType = Field(default_factory=PermissionSetType)
170 change: PermissionSetType = Field(default_factory=PermissionSetType)
173class RetrieveFileMode(StrEnum):
174 DOWNLOAD = "download"
175 PREVIEW = "preview"
176 THUMBNAIL = "thumb"
179class SavedViewFilterRuleType(ConstModel):
180 rule_type: int | None = None
181 value: str | None = None
184class ShareLinkFileVersionType(StrEnum):
185 ARCHIVE = "archive"
186 ORIGINAL = "original"
187 UNKNOWN = "unknown"
189 @override
190 @classmethod
191 def _missing_(cls, value: object) -> "Literal[ShareLinkFileVersionType.UNKNOWN]":
192 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
193 return cls.UNKNOWN
196class StatusType(StrEnum):
197 OK = "OK"
198 ERROR = "ERROR"
199 UNKNOWN = "UNKNOWN"
201 @override
202 @classmethod
203 def _missing_(cls, value: object) -> "Literal[StatusType.UNKNOWN]":
204 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
205 return cls.UNKNOWN
208class StatusDatabaseMigrationStatusType(ConstModel):
209 latest_migration: str | None = None
210 unapplied_migrations: list[str] = Field(default_factory=list)
213class StatusDatabaseType(ConstModel):
214 type: str | None = None
215 url: str | None = None
216 status: StatusType | None = None
217 error: str | None = None
218 migration_status: StatusDatabaseMigrationStatusType | None = None
221class StatusStorageType(ConstModel):
222 total: int | None = None
223 available: int | None = None
226class StatusTasksType(ConstModel):
227 redis_url: str | None = None
228 redis_status: StatusType | None = None
229 redis_error: str | None = None
230 celery_status: StatusType | None = None
231 index_status: StatusType | None = None
232 index_last_modified: datetime | None = None
233 index_error: str | None = None
234 classifier_status: StatusType | None = None
235 classifier_last_trained: datetime | None = None
236 classifier_error: str | None = None
239class TaskStatusType(StrEnum):
240 PENDING = "PENDING"
241 STARTED = "STARTED"
242 SUCCESS = "SUCCESS"
243 FAILURE = "FAILURE"
244 UNKNOWN = "UNKNOWN"
246 @override
247 @classmethod
248 def _missing_(cls, value: object) -> "Literal[TaskStatusType.UNKNOWN]":
249 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
250 return cls.UNKNOWN
253class WorkflowActionType(IntEnum):
254 ASSIGNMENT = 1
255 UNKNOWN = -1
257 @override
258 @classmethod
259 def _missing_(cls, value: object) -> "Literal[WorkflowActionType.UNKNOWN]":
260 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
261 return cls.UNKNOWN
264class WorkflowTriggerType(IntEnum):
265 CONSUMPTION = 1
266 DOCUMENT_ADDED = 2
267 DOCUMENT_UPDATED = 3
268 UNKNOWN = -1
270 @override
271 @classmethod
272 def _missing_(cls, value: object) -> "Literal[WorkflowTriggerType.UNKNOWN]":
273 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
274 return cls.UNKNOWN
277class WorkflowTriggerSourceType(IntEnum):
278 CONSUME_FOLDER = 1
279 API_UPLOAD = 2
280 MAIL_FETCH = 3
281 UNKNOWN = -1
283 @override
284 @classmethod
285 def _missing_(cls, value: object) -> "Literal[WorkflowTriggerSourceType.UNKNOWN]":
286 logger.debug("Handling unknown enum value", extra={"enum_class": cls.__name__, "value": value})
287 return cls.UNKNOWN