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

1""" 

2---------------------------------------------------------------------------- 

3 

4 METADATA: 

5 

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 

13 

14---------------------------------------------------------------------------- 

15 

16 LAST MODIFIED: 

17 

18 2025-03-04 By Jess Mann 

19 

20""" 

21 

22from __future__ import annotations 

23 

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 

29 

30import pydantic 

31from pydantic import ConfigDict, Field 

32 

33logger = logging.getLogger(__name__) 

34 

35 

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 ) 

44 

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) 

53 

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

58 

59 return super().__eq__(other) 

60 

61 

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/") 

79 

80 

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] 

87 

88 

89class FilteringStrategies(StrEnum): 

90 WHITELIST = "whitelist" 

91 BLACKLIST = "blacklist" 

92 ALLOW_ALL = "allow_all" 

93 ALLOW_NONE = "allow_none" 

94 

95 

96class ModelStatus(StrEnum): 

97 INITIALIZING = "initializing" 

98 UPDATING = "updating" 

99 SAVING = "saving" 

100 READY = "ready" 

101 ERROR = "error" 

102 

103 

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" 

114 

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 

120 

121 

122class CustomFieldValues(ConstModel): 

123 field: int 

124 value: Any 

125 

126 

127class CustomFieldTypedDict(TypedDict): 

128 field: int 

129 value: Any 

130 

131 

132class DocumentMetadataType(ConstModel): 

133 namespace: str | None = None 

134 prefix: str | None = None 

135 key: str | None = None 

136 value: str | None = None 

137 

138 

139class DocumentSearchHitType(ConstModel): 

140 score: float | None = None 

141 highlights: str | None = None 

142 note_highlights: str | None = None 

143 rank: int | None = None 

144 

145 

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 

155 

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 

161 

162 

163class PermissionSetType(ConstModel): 

164 users: list[int] = Field(default_factory=list) 

165 groups: list[int] = Field(default_factory=list) 

166 

167 

168class PermissionTableType(ConstModel): 

169 view: PermissionSetType = Field(default_factory=PermissionSetType) 

170 change: PermissionSetType = Field(default_factory=PermissionSetType) 

171 

172 

173class RetrieveFileMode(StrEnum): 

174 DOWNLOAD = "download" 

175 PREVIEW = "preview" 

176 THUMBNAIL = "thumb" 

177 

178 

179class SavedViewFilterRuleType(ConstModel): 

180 rule_type: int | None = None 

181 value: str | None = None 

182 

183 

184class ShareLinkFileVersionType(StrEnum): 

185 ARCHIVE = "archive" 

186 ORIGINAL = "original" 

187 UNKNOWN = "unknown" 

188 

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 

194 

195 

196class StatusType(StrEnum): 

197 OK = "OK" 

198 ERROR = "ERROR" 

199 UNKNOWN = "UNKNOWN" 

200 

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 

206 

207 

208class StatusDatabaseMigrationStatusType(ConstModel): 

209 latest_migration: str | None = None 

210 unapplied_migrations: list[str] = Field(default_factory=list) 

211 

212 

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 

219 

220 

221class StatusStorageType(ConstModel): 

222 total: int | None = None 

223 available: int | None = None 

224 

225 

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 

237 

238 

239class TaskStatusType(StrEnum): 

240 PENDING = "PENDING" 

241 STARTED = "STARTED" 

242 SUCCESS = "SUCCESS" 

243 FAILURE = "FAILURE" 

244 UNKNOWN = "UNKNOWN" 

245 

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 

251 

252 

253class WorkflowActionType(IntEnum): 

254 ASSIGNMENT = 1 

255 UNKNOWN = -1 

256 

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 

262 

263 

264class WorkflowTriggerType(IntEnum): 

265 CONSUMPTION = 1 

266 DOCUMENT_ADDED = 2 

267 DOCUMENT_UPDATED = 3 

268 UNKNOWN = -1 

269 

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 

275 

276 

277class WorkflowTriggerSourceType(IntEnum): 

278 CONSUME_FOLDER = 1 

279 API_UPLOAD = 2 

280 MAIL_FETCH = 3 

281 UNKNOWN = -1 

282 

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