@tstdl/base 0.92.123 → 0.92.125
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/ai-file.service.js +23 -18
- package/ai/ai.service.d.ts +4 -12
- package/ai/ai.service.js +79 -147
- package/ai/functions.d.ts +1 -1
- package/ai/types.d.ts +3 -1
- package/api/server/gateway.js +6 -6
- package/authentication/authentication.api.js +24 -24
- package/authentication/server/authentication.service.js +20 -20
- package/browser/page-controller.js +1 -1
- package/context/context.js +5 -5
- package/document-management/api/document-management.api.d.ts +60 -215
- package/document-management/api/document-management.api.js +32 -68
- package/document-management/models/document-assignment-scope.model.d.ts +11 -0
- package/document-management/models/{document-request-assignment-task-collection.model.js → document-assignment-scope.model.js} +14 -10
- package/document-management/models/document-assignment-task.model.d.ts +13 -0
- package/document-management/models/document-assignment-task.model.js +38 -0
- package/document-management/models/document-category.model.d.ts +2 -0
- package/document-management/models/document-category.model.js +7 -1
- package/document-management/models/{document-collection-document.model.d.ts → document-collection-assignment.model.d.ts} +1 -1
- package/document-management/models/{document-collection-document.model.js → document-collection-assignment.model.js} +7 -7
- package/document-management/models/document-collection.model.d.ts +2 -0
- package/document-management/models/document-collection.model.js +10 -0
- package/document-management/models/document-property-value.model.d.ts +3 -11
- package/document-management/models/document-property-value.model.js +15 -46
- package/document-management/models/document-property.model.d.ts +2 -1
- package/document-management/models/document-property.model.js +2 -2
- package/document-management/models/document-request-submission.model.d.ts +7 -0
- package/document-management/models/document-request-submission.model.js +34 -0
- package/document-management/models/document-request-template.d.ts +2 -4
- package/document-management/models/document-request-template.js +3 -8
- package/document-management/models/document-request.model.d.ts +14 -6
- package/document-management/models/document-request.model.js +22 -12
- package/document-management/models/document-type-validation.model.d.ts +6 -0
- package/document-management/models/document-type-validation.model.js +34 -0
- package/document-management/models/document-type.model.d.ts +0 -1
- package/document-management/models/document-type.model.js +0 -5
- package/document-management/models/document-validation-definition.model.d.ts +10 -0
- package/document-management/models/document-validation-definition.model.js +39 -0
- package/document-management/models/document-validation-execution-related-document.model.d.ts +7 -0
- package/document-management/models/document-validation-execution-related-document.model.js +34 -0
- package/document-management/models/document-validation-execution.model.d.ts +26 -0
- package/document-management/models/document-validation-execution.model.js +72 -0
- package/document-management/models/document-workflow.model.d.ts +35 -0
- package/document-management/models/document-workflow.model.js +70 -0
- package/document-management/models/document.model.d.ts +12 -1
- package/document-management/models/document.model.js +21 -5
- package/document-management/models/index.d.ts +10 -5
- package/document-management/models/index.js +10 -5
- package/document-management/models/service-models/categories-and-types.view-model.d.ts +3 -3
- package/document-management/models/service-models/categories-and-types.view-model.js +2 -21
- package/document-management/models/service-models/document-folders.view-model.d.ts +7 -14
- package/document-management/models/service-models/document-folders.view-model.js +20 -44
- package/document-management/models/service-models/document-management.view-model.d.ts +41 -0
- package/document-management/models/service-models/{document.view-model.js → document-management.view-model.js} +43 -26
- package/document-management/models/service-models/document.service-model.d.ts +51 -81
- package/document-management/models/service-models/document.service-model.js +30 -28
- package/document-management/models/service-models/enriched/enriched-document-assignment.view.d.ts +11 -0
- package/document-management/models/service-models/enriched/enriched-document-assignment.view.js +12 -0
- package/document-management/models/service-models/enriched/enriched-document-category.view.d.ts +13 -0
- package/document-management/models/service-models/enriched/enriched-document-category.view.js +47 -0
- package/document-management/models/service-models/enriched/enriched-document-collection.view.d.ts +22 -0
- package/document-management/models/service-models/enriched/enriched-document-collection.view.js +89 -0
- package/document-management/models/service-models/enriched/enriched-document-file.view.d.ts +12 -0
- package/document-management/models/service-models/enriched/enriched-document-file.view.js +16 -0
- package/document-management/models/service-models/enriched/enriched-document-management-data.view.d.ts +41 -0
- package/document-management/models/service-models/enriched/enriched-document-management-data.view.js +130 -0
- package/document-management/models/service-models/enriched/enriched-document-request.view.d.ts +17 -0
- package/document-management/models/service-models/enriched/enriched-document-request.view.js +52 -0
- package/document-management/models/service-models/enriched/enriched-document-type.view.d.ts +9 -0
- package/document-management/models/service-models/enriched/enriched-document-type.view.js +10 -0
- package/document-management/models/service-models/enriched/enriched-document.view.d.ts +28 -0
- package/document-management/models/service-models/enriched/enriched-document.view.js +77 -0
- package/document-management/models/service-models/enriched/enriched.d.ts +18 -0
- package/document-management/models/service-models/enriched/enriched.js +39 -0
- package/document-management/models/service-models/enriched/index.d.ts +9 -0
- package/document-management/models/service-models/enriched/index.js +9 -0
- package/document-management/models/service-models/index.d.ts +2 -2
- package/document-management/models/service-models/index.js +2 -2
- package/document-management/models/service-models/normalized-requests-template-data.model.d.ts +12 -10
- package/document-management/models/service-models/normalized-requests-template-data.model.js +5 -4
- package/document-management/models/service-models/stats.view-model.d.ts +7 -5
- package/document-management/models/service-models/stats.view-model.js +19 -9
- package/document-management/server/module.d.ts +2 -2
- package/document-management/server/module.js +3 -7
- package/document-management/server/schemas.d.ts +18 -23
- package/document-management/server/schemas.js +18 -23
- package/document-management/server/services/document-category-type.service.d.ts +25 -0
- package/document-management/server/services/document-category-type.service.js +66 -0
- package/document-management/server/services/document-collection.service.d.ts +13 -0
- package/document-management/server/services/document-collection.service.js +41 -0
- package/document-management/server/services/document-file.service.d.ts +17 -0
- package/document-management/server/services/document-file.service.js +204 -0
- package/document-management/server/services/document-management-ai.service.d.ts +22 -0
- package/document-management/server/services/document-management-ai.service.js +340 -0
- package/document-management/server/services/document-management-ancillary.service.d.ts +127 -3
- package/document-management/server/services/document-management-ancillary.service.js +24 -0
- package/document-management/server/services/document-management.service.d.ts +10 -122
- package/document-management/server/services/document-management.service.js +106 -888
- package/document-management/server/services/document-property.service.d.ts +84 -0
- package/document-management/server/services/document-property.service.js +87 -0
- package/document-management/server/services/document-request.service.d.ts +30 -0
- package/document-management/server/services/document-request.service.js +138 -0
- package/document-management/server/services/document-validation.service.d.ts +20 -0
- package/document-management/server/services/document-validation.service.js +145 -0
- package/document-management/server/services/document-workflow.service.d.ts +20 -0
- package/document-management/server/services/document-workflow.service.js +132 -0
- package/document-management/server/services/document.service.d.ts +16 -0
- package/document-management/server/services/document.service.js +81 -0
- package/document-management/server/services/index.d.ts +9 -0
- package/document-management/server/services/index.js +9 -0
- package/document-management/server/validators/ai-validation-executor.d.ts +19 -0
- package/document-management/server/validators/ai-validation-executor.js +51 -0
- package/document-management/server/validators/index.d.ts +2 -0
- package/document-management/server/validators/index.js +2 -0
- package/document-management/server/validators/single-document-validation-executor.d.ts +16 -0
- package/document-management/server/validators/single-document-validation-executor.js +20 -0
- package/document-management/server/validators/validator.d.ts +21 -0
- package/document-management/server/validators/validator.js +2 -0
- package/eslint.config.js +31 -17
- package/examples/document-management/main.d.ts +18 -3
- package/examples/document-management/main.js +28 -12
- package/file/mime-type.js +2 -9
- package/formats.d.ts +5 -2
- package/formats.js +32 -23
- package/http/client/http-client.js +1 -1
- package/injector/injector.js +2 -2
- package/object-storage/s3/s3.object-storage.js +1 -1
- package/orm/data-types/bytea.d.ts +8 -0
- package/orm/data-types/bytea.js +8 -0
- package/orm/data-types/index.d.ts +4 -0
- package/orm/data-types/index.js +4 -0
- package/orm/data-types/numeric-date.d.ts +9 -0
- package/orm/data-types/numeric-date.js +9 -0
- package/orm/data-types/timestamp.d.ts +9 -0
- package/orm/data-types/timestamp.js +9 -0
- package/orm/decorators.d.ts +139 -5
- package/orm/decorators.js +50 -0
- package/orm/entity.d.ts +19 -0
- package/orm/entity.js +19 -0
- package/orm/index.d.ts +5 -0
- package/orm/index.js +5 -0
- package/orm/query.d.ts +51 -0
- package/orm/query.js +6 -0
- package/orm/repository.types.d.ts +54 -2
- package/orm/server/database-schema.d.ts +34 -0
- package/orm/server/database-schema.js +29 -0
- package/orm/server/database.d.ts +19 -1
- package/orm/server/database.js +17 -3
- package/orm/server/drizzle/schema-converter.d.ts +2 -1
- package/orm/server/drizzle/schema-converter.js +12 -1
- package/orm/server/encryption.d.ts +16 -0
- package/orm/server/encryption.js +29 -4
- package/orm/server/index.d.ts +7 -0
- package/orm/server/index.js +7 -0
- package/orm/server/module.d.ts +20 -0
- package/orm/server/module.js +9 -0
- package/orm/server/query-converter.d.ts +17 -0
- package/orm/server/query-converter.js +66 -11
- package/orm/server/repository.d.ts +324 -18
- package/orm/server/repository.js +344 -73
- package/orm/server/transaction.d.ts +5 -5
- package/orm/server/transaction.js +5 -5
- package/orm/server/transactional.d.ts +75 -0
- package/orm/server/transactional.js +134 -0
- package/orm/server/types.d.ts +1 -0
- package/orm/sqls.d.ts +55 -0
- package/orm/sqls.js +60 -0
- package/orm/types.d.ts +67 -4
- package/orm/utils.d.ts +19 -3
- package/orm/utils.js +12 -0
- package/package.json +32 -31
- package/password/password-check-result.model.d.ts +9 -7
- package/password/password-check-result.model.js +8 -8
- package/password/password-check.js +5 -7
- package/password/password-check.localization.js +12 -12
- package/pdf/pdf.service.js +1 -1
- package/pdf/utils.d.ts +9 -0
- package/pdf/utils.js +19 -2
- package/process/spawn.d.ts +11 -4
- package/process/spawn.js +42 -5
- package/queue/postgres/queue.js +5 -5
- package/queue/queue.d.ts +6 -4
- package/queue/queue.js +6 -6
- package/schema/schemas/one-or-many.d.ts +2 -1
- package/schema/schemas/one-or-many.js +1 -1
- package/search-index/elastic/model/index-mapping.d.ts +1 -1
- package/search-index/elastic/model/index-mapping.js +0 -1
- package/search-index/elastic/search-index.d.ts +1 -2
- package/search-index/elastic/search-index.js +3 -3
- package/types.d.ts +1 -0
- package/utils/async-hook/async-hook.d.ts +9 -0
- package/utils/async-hook/async-hook.js +21 -0
- package/utils/async-hook/index.d.ts +1 -0
- package/utils/async-hook/index.js +1 -0
- package/utils/compression.js +1 -1
- package/utils/function/class.d.ts +6 -0
- package/utils/function/class.js +9 -0
- package/utils/function/index.d.ts +1 -0
- package/utils/function/index.js +1 -0
- package/utils/function/memoize.d.ts +18 -0
- package/utils/function/memoize.js +41 -2
- package/utils/jwt.d.ts +1 -1
- package/utils/jwt.js +5 -5
- package/utils/object/forward-ref.d.ts +3 -2
- package/utils/object/forward-ref.js +12 -12
- package/utils/object/lazy-property.js +2 -2
- package/utils/proxy.js +1 -1
- package/utils/stream/{readable-stream-from-promise.d.ts → from-promise.d.ts} +1 -0
- package/utils/stream/from-promise.js +27 -0
- package/utils/stream/index.d.ts +1 -1
- package/utils/stream/index.js +1 -1
- package/utils/stream/stream-reader.js +71 -31
- package/document-management/models/document-request-assignment-task-collection.model.d.ts +0 -7
- package/document-management/models/document-request-assignment-task.model.d.ts +0 -14
- package/document-management/models/document-request-assignment-task.model.js +0 -77
- package/document-management/models/document-request-file.model.d.ts +0 -16
- package/document-management/models/document-request-file.model.js +0 -86
- package/document-management/models/service-models/document.view-model.d.ts +0 -41
- package/document-management/models/service-models/normalized-document-collection-view.model.d.ts +0 -73
- package/document-management/models/service-models/normalized-document-collection-view.model.js +0 -110
- package/utils/stream/readable-stream-from-promise.js +0 -8
|
@@ -1,218 +1,88 @@
|
|
|
1
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
};
|
|
7
|
-
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
8
|
-
if (value !== null && value !== void 0) {
|
|
9
|
-
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
10
|
-
var dispose, inner;
|
|
11
|
-
if (async) {
|
|
12
|
-
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
13
|
-
dispose = value[Symbol.asyncDispose];
|
|
14
|
-
}
|
|
15
|
-
if (dispose === void 0) {
|
|
16
|
-
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
17
|
-
dispose = value[Symbol.dispose];
|
|
18
|
-
if (async) inner = dispose;
|
|
19
|
-
}
|
|
20
|
-
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
21
|
-
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
22
|
-
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
23
|
-
}
|
|
24
|
-
else if (async) {
|
|
25
|
-
env.stack.push({ async: true });
|
|
26
|
-
}
|
|
27
|
-
return value;
|
|
28
|
-
};
|
|
29
|
-
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
30
|
-
return function (env) {
|
|
31
|
-
function fail(e) {
|
|
32
|
-
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
33
|
-
env.hasError = true;
|
|
34
|
-
}
|
|
35
|
-
var r, s = 0;
|
|
36
|
-
function next() {
|
|
37
|
-
while (r = env.stack.pop()) {
|
|
38
|
-
try {
|
|
39
|
-
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
40
|
-
if (r.dispose) {
|
|
41
|
-
var result = r.dispose.call(r.value);
|
|
42
|
-
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
43
|
-
}
|
|
44
|
-
else s |= 1;
|
|
45
|
-
}
|
|
46
|
-
catch (e) {
|
|
47
|
-
fail(e);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
51
|
-
if (env.hasError) throw env.error;
|
|
52
|
-
}
|
|
53
|
-
return next();
|
|
54
|
-
};
|
|
55
|
-
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
56
|
-
var e = new Error(message);
|
|
57
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
58
|
-
});
|
|
59
|
-
var DocumentManagementService_1;
|
|
60
|
-
import { and, eq, inArray, ne, notExists, sql } from 'drizzle-orm';
|
|
61
|
-
import { P, match } from 'ts-pattern';
|
|
62
|
-
import { AiService } from '../../../ai/index.js';
|
|
63
1
|
import { Enumerable } from '../../../enumerable/index.js';
|
|
64
|
-
import {
|
|
65
|
-
import {
|
|
66
|
-
import {
|
|
67
|
-
import {
|
|
68
|
-
import { Logger } from '../../../logger/logger.js';
|
|
69
|
-
import { MessageBus } from '../../../message-bus/message-bus.js';
|
|
70
|
-
import { ObjectStorage } from '../../../object-storage/index.js';
|
|
71
|
-
import { TRANSACTION_TIMESTAMP, arrayAgg, coalesce, getEntityMap, jsonAgg, toJsonb } from '../../../orm/index.js';
|
|
72
|
-
import { DatabaseConfig, getRepository, injectRepository } from '../../../orm/server/index.js';
|
|
73
|
-
import { getPdfPageCount } from '../../../pdf/index.js';
|
|
74
|
-
import { Queue } from '../../../queue/queue.js';
|
|
75
|
-
import { array, boolean, enumeration, integer, nullable, number, object, string } from '../../../schema/index.js';
|
|
76
|
-
import { distinct, toArray } from '../../../utils/array/index.js';
|
|
2
|
+
import { inject } from '../../../injector/index.js';
|
|
3
|
+
import { getEntityMap } from '../../../orm/index.js';
|
|
4
|
+
import { Transactional, injectRepository } from '../../../orm/server/index.js';
|
|
5
|
+
import { distinct } from '../../../utils/array/index.js';
|
|
77
6
|
import { compareByValueSelectionToOrder } from '../../../utils/comparison.js';
|
|
78
|
-
import {
|
|
79
|
-
import {
|
|
80
|
-
import {
|
|
81
|
-
import {
|
|
82
|
-
import {
|
|
83
|
-
import { tryIgnoreLogAsync } from '../../../utils/try-ignore.js';
|
|
84
|
-
import { assertBooleanPass, assertDefined, assertDefinedPass, assertNotNullPass, assertNumberPass, assertStringPass, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, isUndefined } from '../../../utils/type-guards.js';
|
|
85
|
-
import { millisecondsPerMinute } from '../../../utils/units.js';
|
|
86
|
-
import { union } from 'drizzle-orm/pg-core';
|
|
87
|
-
import { debounceTime, filter, map } from 'rxjs';
|
|
88
|
-
import { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
|
|
89
|
-
import { DocumentManagementConfig } from '../module.js';
|
|
90
|
-
import { documentCategory, documentCollectionDocument, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
|
|
7
|
+
import { groupToMap, groupToSingleMap } from '../../../utils/iterable-helpers/index.js';
|
|
8
|
+
import { fromEntries, objectEntries } from '../../../utils/object/index.js';
|
|
9
|
+
import { assertDefinedPass, isNotNull, isNull, isUndefined } from '../../../utils/type-guards.js';
|
|
10
|
+
import { DocumentApproval, DocumentCategory, DocumentCollection, DocumentCollectionAssignment, DocumentFile, DocumentRequest, DocumentRequestCollection, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentValidationExecution, DocumentWorkflowStep } from '../../models/index.js';
|
|
11
|
+
import { DocumentCategoryTypeService, enumTypeKey } from './document-category-type.service.js';
|
|
91
12
|
import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
#
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
fileObjectStorage = inject(ObjectStorage, inject(DocumentManagementConfig).fileObjectStorageModule);
|
|
121
|
-
extractionQueue = inject((Queue), { name: 'DocumentManagement:extraction', processTimeout: 15 * millisecondsPerMinute });
|
|
122
|
-
assignmentQueue = inject((Queue), { name: 'DocumentManagement:assignment', processTimeout: 15 * millisecondsPerMinute });
|
|
123
|
-
collectionChangeMessageBus = inject((MessageBus), 'DocumentManagement:collectionChange');
|
|
124
|
-
logger = inject(Logger, DocumentManagementService_1.name);
|
|
125
|
-
[afterResolve](_, { cancellationSignal }) {
|
|
126
|
-
if (this.isFork) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
this.processQueues(cancellationSignal);
|
|
130
|
-
}
|
|
131
|
-
changes$(collectionIds) {
|
|
132
|
-
const collectionIdsArray = toArray(collectionIds);
|
|
133
|
-
return this.collectionChangeMessageBus.allMessages$.pipe(filter((message) => isUndefined(message) || collectionIdsArray.some((id) => message.includes(id))), debounceTime(250), map(() => undefined));
|
|
134
|
-
}
|
|
135
|
-
processQueues(cancellationSignal) {
|
|
136
|
-
this.extractionQueue.process({ concurrency: 5, cancellationSignal }, async (job) => {
|
|
137
|
-
const [entry] = objectEntries(job.data);
|
|
138
|
-
this.logger.verbose(`Processing extraction job for ${entry?.[0]} ${entry?.[1]}`);
|
|
139
|
-
await match(job.data)
|
|
140
|
-
.with({ documentId: P.string.select() }, async (documentId) => this.enrichDocument(documentId))
|
|
141
|
-
.with({ requestFileId: P.string.select() }, async (requestFileId) => this.enrichDocumentRequestFile(requestFileId))
|
|
142
|
-
.with({ requestAssignmentTaskId: P.string.select() }, async (requestAssignmentTaskId) => this.enrichDocumentRequestAssignmentTask(requestAssignmentTaskId))
|
|
143
|
-
.exhaustive();
|
|
144
|
-
}, this.logger);
|
|
145
|
-
this.assignmentQueue.process({ concurrency: 5, cancellationSignal }, async (job) => {
|
|
146
|
-
this.logger.verbose(`Processing assignment job "${job.data.requestAssignmentTaskId}"`);
|
|
147
|
-
await this.assignDocumentRequest(job.data.requestAssignmentTaskId);
|
|
148
|
-
}, this.logger);
|
|
149
|
-
}
|
|
150
|
-
async resolveNames(...collectionsOrIds) {
|
|
151
|
-
if (collectionsOrIds.length == 0) {
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
154
|
-
const loadIds = collectionsOrIds.filter((collection) => isString(collection));
|
|
155
|
-
if (loadIds.length == 0) {
|
|
156
|
-
return this.ancillaryService.resolveNames(collectionsOrIds);
|
|
157
|
-
}
|
|
158
|
-
const loadedCollections = await this.loadManyByQuery({ id: { $in: loadIds } });
|
|
159
|
-
const collections = collectionsOrIds.map((collectionOrId) => isString(collectionOrId)
|
|
160
|
-
? assertDefinedPass(loadedCollections.find((collection) => collection.id == collectionOrId), `Could not load collection "${collectionOrId}".`)
|
|
161
|
-
: collectionOrId);
|
|
162
|
-
return this.ancillaryService.resolveNames(collections);
|
|
163
|
-
}
|
|
164
|
-
async resolveNamesMap(...collectionsOrIds) {
|
|
165
|
-
const names = await this.resolveNames(...collectionsOrIds);
|
|
166
|
-
const entries = collectionsOrIds.map((collectionOrId, index) => [isString(collectionOrId) ? collectionOrId : collectionOrId.id, names[index]]);
|
|
167
|
-
return fromEntries(entries);
|
|
168
|
-
}
|
|
169
|
-
async loadData(collectionIds, collectionsMetadata) {
|
|
170
|
-
return this.transaction(async (_, transaction) => {
|
|
171
|
-
const [collections, collectionDocuments, requestCollections, categories, types] = await Promise.all([
|
|
172
|
-
this.withTransaction(transaction).loadMany(collectionIds),
|
|
173
|
-
this.documentCollectionDocumentService.withTransaction(transaction).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
174
|
-
this.documentRequestCollectionService.withTransaction(transaction).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
175
|
-
this.documentCategoryService.withTransaction(transaction).loadManyByQuery({}, { order: 'label' }),
|
|
176
|
-
this.documentTypeService.withTransaction(transaction).loadManyByQuery({}, { order: 'label' })
|
|
13
|
+
import { DocumentPropertyService } from './document-property.service.js';
|
|
14
|
+
import { DocumentWorkflowService } from './document-workflow.service.js';
|
|
15
|
+
import { DocumentService } from './document.service.js';
|
|
16
|
+
export class DocumentManagementService extends Transactional {
|
|
17
|
+
#ancillaryService = inject(DocumentManagementAncillaryService);
|
|
18
|
+
#documentCategoryTypeService = inject(DocumentCategoryTypeService);
|
|
19
|
+
#documentCollectionRepository = injectRepository(DocumentCollection);
|
|
20
|
+
#documentCategoryRepository = injectRepository(DocumentCategory);
|
|
21
|
+
#documentCollectionAssignmentRepository = injectRepository(DocumentCollectionAssignment);
|
|
22
|
+
#documentFileRepository = injectRepository(DocumentFile);
|
|
23
|
+
#documentService = inject(DocumentService);
|
|
24
|
+
#documentPropertyService = inject(DocumentPropertyService);
|
|
25
|
+
#documentRequestCollectionRepository = injectRepository(DocumentRequestCollection);
|
|
26
|
+
#documentRequestRepository = injectRepository(DocumentRequest);
|
|
27
|
+
#documentRequestsTemplateRepository = injectRepository(DocumentRequestsTemplate);
|
|
28
|
+
#documentRequestTemplateRepository = injectRepository(DocumentRequestTemplate);
|
|
29
|
+
#documentTypeRepository = injectRepository(DocumentType);
|
|
30
|
+
#documentWorkflowService = inject(DocumentWorkflowService);
|
|
31
|
+
#documentValidationExecutionRepository = injectRepository(DocumentValidationExecution);
|
|
32
|
+
async loadData(collectionIds) {
|
|
33
|
+
return this.transaction(async (tx) => {
|
|
34
|
+
const [collections, collectionsMetadataMap, collectionAssignments, requestCollections, categories, types] = await Promise.all([
|
|
35
|
+
this.#documentCollectionRepository.withTransaction(tx).loadMany(collectionIds),
|
|
36
|
+
this.#ancillaryService.resolveMetadataMap(...collectionIds),
|
|
37
|
+
this.#documentCollectionAssignmentRepository.withTransaction(tx).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
38
|
+
this.#documentRequestCollectionRepository.withTransaction(tx).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
39
|
+
this.#documentCategoryRepository.withTransaction(tx).loadManyByQuery({}, { order: 'label' }),
|
|
40
|
+
this.#documentTypeRepository.withTransaction(tx).loadManyByQuery({}, { order: 'label' }),
|
|
177
41
|
]);
|
|
178
|
-
const documentIds = collectionDocuments.map((document) => document.documentId);
|
|
179
42
|
const requestIds = requestCollections.map((requestCollection) => requestCollection.requestId);
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
this.
|
|
43
|
+
const requests = await this.#documentRequestRepository.withTransaction(tx).loadManyByQuery({ id: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } });
|
|
44
|
+
const collectionDocumentIds = collectionAssignments.map((document) => document.documentId);
|
|
45
|
+
const requestDocumentIds = requests.map((request) => request.documentId).filter(isNotNull);
|
|
46
|
+
const documentIds = distinct([...collectionDocumentIds, ...requestDocumentIds]);
|
|
47
|
+
const [documents, propertyValues] = await Promise.all([
|
|
48
|
+
this.#documentService.withTransaction(tx).repository.loadManyByQuery({ id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
49
|
+
this.#documentPropertyService.withTransaction(tx).loadDocumentProperties(documentIds),
|
|
186
50
|
]);
|
|
187
51
|
const documentFileIds = documents.map((document) => document.fileId);
|
|
188
|
-
const
|
|
189
|
-
const files = await
|
|
190
|
-
|
|
52
|
+
const workflowRelevantDocumentIds = documents.map((document) => (document.approval == DocumentApproval.Pending) ? document.id : null).filter(isNotNull);
|
|
53
|
+
const [files, currentWorkflows] = await Promise.all([
|
|
54
|
+
this.#documentFileRepository.withTransaction(tx).loadManyByQuery({ id: { $in: documentFileIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
55
|
+
this.#documentWorkflowService.loadLatestWorkflows(workflowRelevantDocumentIds),
|
|
56
|
+
]);
|
|
57
|
+
const documentIdMap = getEntityMap(documents);
|
|
58
|
+
const documentWorkflowMap = groupToSingleMap(currentWorkflows, (workflow) => workflow.documentId);
|
|
191
59
|
const valuesMap = Enumerable.from(propertyValues).groupToMap((value) => value.documentId);
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
60
|
+
const validationWorkflowIds = currentWorkflows.map((workflow) => (workflow.step == DocumentWorkflowStep.Validation) ? workflow.id : null).filter(isNotNull);
|
|
61
|
+
const validations = await this.#documentValidationExecutionRepository.loadManyByQuery({ workflowId: { $in: validationWorkflowIds } });
|
|
62
|
+
const workflowValidationMap = groupToMap(validations, (validation) => validation.workflowId);
|
|
63
|
+
const collectionViews = collections.toSorted(compareByValueSelectionToOrder(collectionIds, (collection) => collection.id)).map((collection) => {
|
|
64
|
+
const metadata = assertDefinedPass(collectionsMetadataMap[collection.id]);
|
|
65
|
+
return ({
|
|
66
|
+
...collection,
|
|
67
|
+
name: metadata.name,
|
|
68
|
+
group: metadata.group,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
197
71
|
const documentViews = documents.map((document) => {
|
|
198
|
-
const
|
|
72
|
+
const currentWorkflow = documentWorkflowMap.get(document.id) ?? null;
|
|
73
|
+
const validations = (isNotNull(currentWorkflow) && (currentWorkflow.step == DocumentWorkflowStep.Validation)) ? workflowValidationMap.get(currentWorkflow.id) ?? [] : null;
|
|
199
74
|
return {
|
|
200
75
|
...document,
|
|
201
|
-
collectionAssignments:
|
|
76
|
+
collectionAssignments: collectionAssignments.filter((collectionDocument) => collectionDocument.documentId == document.id),
|
|
202
77
|
properties: valuesMap.get(document.id) ?? [],
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
: isNull(job.lastDequeueTimestamp)
|
|
206
|
-
? 'pending'
|
|
207
|
-
: (job.tries >= this.extractionQueue.maxTries) && (job.lastDequeueTimestamp >= (currentTimestamp() + this.extractionQueue.processTimeout))
|
|
208
|
-
? 'error'
|
|
209
|
-
: 'extracting'
|
|
78
|
+
currentWorkflow,
|
|
79
|
+
validations,
|
|
210
80
|
};
|
|
211
81
|
});
|
|
212
82
|
const requestViews = requests.map((request) => ({
|
|
213
83
|
...request,
|
|
214
84
|
collectionIds: requestCollections.filter((requestCollection) => requestCollection.requestId == request.id).map((requestCollection) => requestCollection.collectionId),
|
|
215
|
-
|
|
85
|
+
document: isNull(request.documentId) ? null : documentIdMap.get(request.documentId) ?? null,
|
|
216
86
|
}));
|
|
217
87
|
return {
|
|
218
88
|
collections: collectionViews,
|
|
@@ -220,710 +90,58 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
220
90
|
requests: requestViews,
|
|
221
91
|
files,
|
|
222
92
|
categories,
|
|
223
|
-
types
|
|
93
|
+
types,
|
|
224
94
|
};
|
|
225
95
|
});
|
|
226
96
|
}
|
|
227
97
|
async loadDocumentRequestsTemplateData() {
|
|
228
98
|
const [requestsTemplates, requestTemplates] = await Promise.all([
|
|
229
|
-
this.
|
|
230
|
-
this.
|
|
99
|
+
this.#documentRequestsTemplateRepository.loadManyByQuery({}, { order: 'label' }),
|
|
100
|
+
this.#documentRequestTemplateRepository.loadManyByQuery({}),
|
|
231
101
|
]);
|
|
232
102
|
const templates = requestsTemplates.map((requestsTemplate) => ({
|
|
233
103
|
...requestsTemplate,
|
|
234
|
-
requestTemplates: requestTemplates.filter((requestTemplate) => requestTemplate.requestsTemplateId == requestsTemplate.id)
|
|
104
|
+
requestTemplates: requestTemplates.filter((requestTemplate) => requestTemplate.requestsTemplateId == requestsTemplate.id),
|
|
235
105
|
}));
|
|
236
106
|
return { templates };
|
|
237
107
|
}
|
|
238
|
-
async
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const key = getDocumentFileKey(fileId);
|
|
256
|
-
return this.fileObjectStorage.getContent(key);
|
|
257
|
-
}
|
|
258
|
-
getFileContentStream(fileId) {
|
|
259
|
-
const key = getDocumentFileKey(fileId);
|
|
260
|
-
return this.fileObjectStorage.getContentStream(key);
|
|
261
|
-
}
|
|
262
|
-
async getFileContentUrl(fileId, title, download = false) {
|
|
263
|
-
const file = await this.documentFileService.load(fileId);
|
|
264
|
-
return this.getDocumentFileContentObjectUrl(title ?? fileId, file, download);
|
|
265
|
-
}
|
|
266
|
-
async createCategory(parameters) {
|
|
267
|
-
return this.documentCategoryService.insert(parameters);
|
|
268
|
-
}
|
|
269
|
-
async createType(parameters) {
|
|
270
|
-
return this.documentTypeService.insert(parameters);
|
|
271
|
-
}
|
|
272
|
-
async createCollection(parameters) {
|
|
273
|
-
return this.insert(parameters ?? {});
|
|
274
|
-
}
|
|
275
|
-
async collectionHasDocumentByFilter(collectionId, filter) {
|
|
276
|
-
const collectionDocuments = await this.documentCollectionDocumentService.loadManyByQuery({ collectionId });
|
|
277
|
-
const documentIds = collectionDocuments.map((cd) => cd.documentId);
|
|
278
|
-
return this.documentService.hasByQuery({ $and: [{ id: { $in: documentIds } }, filter] });
|
|
279
|
-
}
|
|
280
|
-
async getRequestFilesStats(collectionIds) {
|
|
281
|
-
const collectionRequests = await this.documentRequestCollectionService.loadManyByQuery({ collectionId: { $in: toArray(collectionIds) } });
|
|
282
|
-
const requestIds = collectionRequests.map((collectionRequest) => collectionRequest.requestId);
|
|
283
|
-
const filteredRequests = await this.documentRequestService.loadManyByQuery({ id: { $in: requestIds }, completed: false });
|
|
284
|
-
const filteredRequestIds = filteredRequests.map((request) => request.id);
|
|
285
|
-
const requiredFilesCount = filteredRequests.reduce((sum, request) => sum + request.requiredFilesCount, 0);
|
|
286
|
-
const [pendingFilesCount, approvedFilesCount] = await Promise.all([
|
|
287
|
-
this.documentRequestFileService.countByQuery({ requestId: { $in: filteredRequestIds }, approval: null }),
|
|
288
|
-
this.documentRequestFileService.countByQuery({ requestId: { $in: filteredRequestIds }, approval: true })
|
|
289
|
-
]);
|
|
290
|
-
return { requiredFilesCount, requiredFilesLeft: Math.max(0, requiredFilesCount - approvedFilesCount), pendingFilesCount, approvedFilesCount };
|
|
291
|
-
}
|
|
292
|
-
async createDocumentRequestsTemplate(parameters) {
|
|
293
|
-
return this.documentRequestsTemplateService.insert(parameters);
|
|
294
|
-
}
|
|
295
|
-
async updateDocumentRequestsTemplate({ id, label, metadata }) {
|
|
296
|
-
return this.documentRequestsTemplateService.update(id, { label, metadata });
|
|
297
|
-
}
|
|
298
|
-
async applyDocumentRequestsTemplate({ id, collectionIds, metadata }) {
|
|
299
|
-
const requestTemplates = await this.documentRequestTemplateService.loadManyByQuery({ requestsTemplateId: id });
|
|
300
|
-
await this.documentRequestService.transaction(async (_, transaction) => {
|
|
301
|
-
for (const { typeId, requiredFilesCount, comment } of requestTemplates) {
|
|
302
|
-
await this.createDocumentRequest({ typeId, requiredFilesCount, comment, collectionIds, metadata }, transaction);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
async deleteDocumentRequestsTemplate(parameters) {
|
|
307
|
-
return this.documentRequestsTemplateService.delete(parameters.id);
|
|
308
|
-
}
|
|
309
|
-
async createDocumentRequestTemplate(parameters) {
|
|
310
|
-
return this.documentRequestTemplateService.insert(parameters);
|
|
311
|
-
}
|
|
312
|
-
async updateDocumentRequestTemplate({ id, typeId, requiredFilesCount, comment, metadata }) {
|
|
313
|
-
return this.documentRequestTemplateService.update(id, { typeId, requiredFilesCount, comment, metadata });
|
|
314
|
-
}
|
|
315
|
-
async deleteDocumentRequestTemplate(parameters) {
|
|
316
|
-
return this.documentRequestTemplateService.delete(parameters.id);
|
|
317
|
-
}
|
|
318
|
-
async createDocument({ typeId, title, subtitle, pages, date, summary, tags, validated, originalFileName, collectionIds, properties, metadata }, content) {
|
|
319
|
-
const collectionIdsArray = toArray(collectionIds);
|
|
320
|
-
const document = await this.documentService.transaction(async (_, transaction) => {
|
|
321
|
-
const documentFile = await this.createDocumentFile(content, originalFileName, transaction);
|
|
322
|
-
const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata, validated });
|
|
323
|
-
for (const collectionId of collectionIdsArray) {
|
|
324
|
-
await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
|
|
325
|
-
}
|
|
326
|
-
if (isDefined(properties)) {
|
|
327
|
-
await this.setPropertyValues({ documentId: document.id, properties }, transaction);
|
|
328
|
-
}
|
|
329
|
-
return document;
|
|
330
|
-
});
|
|
331
|
-
void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ documentId: document.id }, { tag: document.id }));
|
|
332
|
-
this.publishChange({ collectionIds: collectionIdsArray });
|
|
333
|
-
return document;
|
|
334
|
-
}
|
|
335
|
-
async approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }) {
|
|
336
|
-
return this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
337
|
-
const requestFile = await this.documentRequestFileService.withTransaction(transaction).load(id);
|
|
338
|
-
const requestCollections = await this.documentRequestCollectionService.withTransaction(transaction).loadManyByQuery({ requestId: requestFile.requestId });
|
|
339
|
-
if (requestFile.approval == true) {
|
|
340
|
-
throw new BadRequestError('Document request file already accepted.');
|
|
341
|
-
}
|
|
342
|
-
const request = await this.documentRequestService.withTransaction(transaction).load(requestFile.requestId);
|
|
343
|
-
const document = await this.documentService.withTransaction(transaction).insert({
|
|
344
|
-
fileId: requestFile.fileId,
|
|
345
|
-
typeId: request.typeId,
|
|
346
|
-
title: requestFile.title,
|
|
347
|
-
subtitle: requestFile.subtitle,
|
|
348
|
-
pages: requestFile.pages,
|
|
349
|
-
date: requestFile.date,
|
|
350
|
-
summary: requestFile.summary,
|
|
351
|
-
tags: requestFile.tags,
|
|
352
|
-
validated: true,
|
|
353
|
-
metadata: documentMetadata
|
|
354
|
-
});
|
|
355
|
-
for (const { collectionId } of requestCollections) {
|
|
356
|
-
await this.addDocumentToCollection({ collectionId, documentId: document.id }, transaction);
|
|
357
|
-
}
|
|
358
|
-
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: true, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, createdDocumentId: document.id, metadata: requestFileMetadata });
|
|
359
|
-
return document;
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
async rejectDocumentRequestFile({ id, approvalComment, metadata }) {
|
|
363
|
-
const requestFile = await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
364
|
-
const requestFile = await this.documentRequestFileService.withTransaction(transaction).load(id);
|
|
365
|
-
if (requestFile.approval == true) {
|
|
366
|
-
throw new BadRequestError('Document request file already accepted.');
|
|
367
|
-
}
|
|
368
|
-
return this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
369
|
-
});
|
|
370
|
-
this.publishChange({ requestIds: [requestFile.requestId] });
|
|
371
|
-
}
|
|
372
|
-
async updateDocumentRequestFile({ id, title, approvalComment, metadata }) {
|
|
373
|
-
const result = await this.documentRequestFileService.update(id, { title, approvalComment, metadata });
|
|
374
|
-
this.publishChange({ requestIds: [result.requestId] });
|
|
375
|
-
return result;
|
|
376
|
-
}
|
|
377
|
-
async deleteDocumentRequestFile({ id, metadata }) {
|
|
378
|
-
const requestFile = await this.documentRequestFileService.load(id);
|
|
379
|
-
if (isNotNull(requestFile.approval)) {
|
|
380
|
-
throw new BadRequestError('Only pending request files can be deleted.');
|
|
381
|
-
}
|
|
382
|
-
await this.documentRequestFileService.delete(id, metadata);
|
|
383
|
-
this.publishChange({ requestIds: [requestFile.requestId] });
|
|
384
|
-
}
|
|
385
|
-
async createDocumentFile(content, originalFileName, transaction) {
|
|
386
|
-
const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
|
|
387
|
-
const hash = await digest('SHA-256', contentAsUint8Array).toHex();
|
|
388
|
-
const mimeType = await getMimeType(contentAsUint8Array);
|
|
389
|
-
return this.documentFileService.useTransaction(transaction, async (_, transaction) => {
|
|
390
|
-
const documentFile = await this.documentFileService
|
|
391
|
-
.withTransaction(transaction)
|
|
392
|
-
.insert({
|
|
393
|
-
originalFileName,
|
|
394
|
-
mimeType,
|
|
395
|
-
hash,
|
|
396
|
-
size: contentAsUint8Array.length
|
|
397
|
-
});
|
|
398
|
-
const key = getDocumentFileKey(documentFile.id);
|
|
399
|
-
await this.fileObjectStorage.uploadObject(key, contentAsUint8Array);
|
|
400
|
-
return documentFile;
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
async createDocumentRequestFile({ requestId, title, subtitle, date, summary, tags, originalFileName, metadata }, content) {
|
|
404
|
-
const documentRequestFile = await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
405
|
-
const [request, existingRequestFiles] = await Promise.all([
|
|
406
|
-
this.documentRequestService.withTransaction(transaction).load(requestId),
|
|
407
|
-
this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId })
|
|
408
|
-
]);
|
|
409
|
-
const filesCountLeft = (request.completed ? 0 : request.requiredFilesCount) - existingRequestFiles.reduce((sum, requestFile) => sum + ((requestFile.approval != false) ? 1 : 0), 0);
|
|
410
|
-
if (filesCountLeft <= 0) {
|
|
411
|
-
throw new BadRequestError('Maximum amount of allowed files reached.');
|
|
412
|
-
}
|
|
413
|
-
const file = await this.createDocumentFile(content, originalFileName, transaction);
|
|
414
|
-
const result = await this.documentRequestFileService
|
|
415
|
-
.withTransaction(transaction)
|
|
416
|
-
.insert({
|
|
417
|
-
requestId,
|
|
418
|
-
fileId: file.id,
|
|
419
|
-
title,
|
|
420
|
-
subtitle,
|
|
421
|
-
pages: null,
|
|
422
|
-
date,
|
|
423
|
-
summary,
|
|
424
|
-
tags,
|
|
425
|
-
createdDocumentId: null,
|
|
426
|
-
approval: null,
|
|
427
|
-
approvalComment: null,
|
|
428
|
-
approvalTimestamp: null,
|
|
429
|
-
metadata
|
|
430
|
-
});
|
|
431
|
-
return result;
|
|
432
|
-
});
|
|
433
|
-
void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id }));
|
|
434
|
-
this.publishChange({ requestIds: [documentRequestFile.requestId] });
|
|
435
|
-
return documentRequestFile;
|
|
436
|
-
}
|
|
437
|
-
async createDocumentRequest(parameters, transaction) {
|
|
438
|
-
if (parameters.collectionIds.length == 0) {
|
|
439
|
-
throw new BadRequestError('No target collectionId specified.');
|
|
440
|
-
}
|
|
441
|
-
const result = await this.documentRequestService.useTransaction(transaction, async (_, tx) => {
|
|
442
|
-
const request = await this.documentRequestService.withTransaction(tx).insert({ ...parameters, completed: false });
|
|
443
|
-
const newDocumentRequestCollections = parameters.collectionIds.map((collectionId) => ({ requestId: request.id, collectionId }));
|
|
444
|
-
await this.documentRequestCollectionService.withTransaction(tx).insertMany(newDocumentRequestCollections);
|
|
445
|
-
return request;
|
|
446
|
-
});
|
|
447
|
-
this.publishChange({ collectionIds: parameters.collectionIds });
|
|
448
|
-
return result;
|
|
449
|
-
}
|
|
450
|
-
async createDocumentRequestAssignmentTask({ originalFileName, collectionIds }, content) {
|
|
451
|
-
if (collectionIds.length == 0) {
|
|
452
|
-
throw new BadRequestError('No target collectionId specified.');
|
|
453
|
-
}
|
|
454
|
-
const file = await this.createDocumentFile(content, originalFileName);
|
|
455
|
-
return this.documentRequestAssignmentTaskService.transaction(async (_, transaction) => {
|
|
456
|
-
const task = await this.documentRequestAssignmentTaskService.withTransaction(transaction).insert({
|
|
457
|
-
fileId: file.id,
|
|
458
|
-
assignedRequestFileId: null,
|
|
459
|
-
typeId: null,
|
|
460
|
-
title: null,
|
|
461
|
-
subtitle: null,
|
|
462
|
-
pages: null,
|
|
463
|
-
date: null,
|
|
464
|
-
summary: null,
|
|
465
|
-
tags: null,
|
|
466
|
-
assignmentTries: 0
|
|
467
|
-
});
|
|
468
|
-
const newTaksCollections = collectionIds.map((collectionId) => ({ requestAssignmentTaskId: task.id, collectionId }));
|
|
469
|
-
await this.documentRequestAssignmentTaskCollectionService.withTransaction(transaction).insertMany(newTaksCollections);
|
|
470
|
-
void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id }));
|
|
471
|
-
this.publishChange({ collectionIds });
|
|
472
|
-
return task;
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
async updateDocument(parameters, transaction) {
|
|
476
|
-
const { id: documentId, properties: propertyUpdates, ...update } = parameters;
|
|
477
|
-
await this.documentService.useTransaction(transaction, async (repository, tx) => {
|
|
478
|
-
await repository.update(documentId, update);
|
|
479
|
-
if (isDefined(propertyUpdates)) {
|
|
480
|
-
await this.setPropertyValues({ documentId, properties: propertyUpdates }, tx);
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
this.publishChange({ documentIds: [documentId] });
|
|
484
|
-
}
|
|
485
|
-
async updateDocumentRequest(parameters, transaction) {
|
|
486
|
-
const { id: documentRequestId, ...update } = parameters;
|
|
487
|
-
const requestFiles = await this.documentRequestFileService.withOptionalTransaction(transaction).loadManyByQuery({ requestId: parameters.id });
|
|
488
|
-
if (parameters.completed == true) {
|
|
489
|
-
const hasCreatedDocument = requestFiles.some((requestFile) => isNotNull(requestFile.createdDocumentId));
|
|
490
|
-
if (!hasCreatedDocument) {
|
|
491
|
-
throw new BadRequestError('Cannot complete requests which has no approved files.');
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
await this.documentRequestService.withOptionalTransaction(transaction).update(documentRequestId, update);
|
|
495
|
-
this.publishChange({ requestIds: [documentRequestId] });
|
|
496
|
-
}
|
|
497
|
-
async deleteDocumentRequest({ id, metadata }, transaction) {
|
|
498
|
-
const requestFiles = await this.documentRequestFileService.loadManyByQuery({ requestId: id });
|
|
499
|
-
const hasCreatedDocument = requestFiles.some((requestFile) => isNotNull(requestFile.createdDocumentId));
|
|
500
|
-
if (hasCreatedDocument) {
|
|
501
|
-
throw new BadRequestError('Cannot delete requests which has approved files.');
|
|
502
|
-
}
|
|
503
|
-
await this.documentRequestService.withOptionalTransaction(transaction).delete(id, metadata);
|
|
504
|
-
this.publishChange({ requestIds: [id] });
|
|
505
|
-
}
|
|
506
|
-
async setPropertyValues({ documentId, requestFileId, requestAssignmentTaskId, properties: items }, transaction) {
|
|
507
|
-
if ((items.length == 0)) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
if ((Number(isDefined(documentId)) + Number(isDefined(requestFileId)) + Number(isDefined(requestAssignmentTaskId))) != 1) {
|
|
511
|
-
throw new BadRequestError('Exactly one of documentId, requestFileId or requestAssignmentTaskId must be specified.');
|
|
512
|
-
}
|
|
513
|
-
await this.documentPropertyService.useTransaction(transaction, async (_, tx) => {
|
|
514
|
-
const propertyIds = items.map((property) => property.propertyId);
|
|
515
|
-
const properties = await this.documentPropertyService.withTransaction(tx).loadManyByQuery({ id: { $in: propertyIds } });
|
|
516
|
-
const propertiesMap = getEntityMap(properties);
|
|
517
|
-
const propertyItems = items.map(({ propertyId, value, metadata }) => {
|
|
518
|
-
const property = assertDefinedPass(propertiesMap.get(propertyId));
|
|
519
|
-
validatePropertyValue(propertyId, property.dataType, value);
|
|
520
|
-
return {
|
|
521
|
-
propertyId,
|
|
522
|
-
text: (property.dataType == 'text') ? assertStringPass(value) : null,
|
|
523
|
-
integer: (property.dataType == 'integer') ? assertNumberPass(value) : null,
|
|
524
|
-
decimal: (property.dataType == 'decimal') ? assertNumberPass(value) : null,
|
|
525
|
-
boolean: (property.dataType == 'boolean') ? assertBooleanPass(value) : null,
|
|
526
|
-
date: (property.dataType == 'date') ? assertNumberPass(value) : null,
|
|
527
|
-
metadata
|
|
528
|
-
};
|
|
529
|
-
});
|
|
530
|
-
if (isDefined(documentId)) {
|
|
531
|
-
await this.documentPropertyValueService.withTransaction(tx)
|
|
532
|
-
.upsertMany(['documentId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, documentId })));
|
|
533
|
-
}
|
|
534
|
-
else if (isDefined(requestFileId)) {
|
|
535
|
-
await this.documentRequestFilePropertyValueService.withTransaction(tx)
|
|
536
|
-
.upsertMany(['requestFileId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestFileId })));
|
|
108
|
+
async initializeCategoriesAndTypes(categoryNames, categoryParents, typeNames, typeCategories) {
|
|
109
|
+
const categoryEntries = objectEntries(categoryNames);
|
|
110
|
+
const typeEntries = objectEntries(typeNames);
|
|
111
|
+
const { categoryMap, typeMap } = await this.transaction(async (tx) => {
|
|
112
|
+
const { categories, types } = await this.#documentCategoryTypeService.loadCategoriesAndTypes();
|
|
113
|
+
const enumKeyCategoryMap = groupToSingleMap(categories, (category) => category.metadata.attributes[enumTypeKey]);
|
|
114
|
+
const enumKeyTypeMap = groupToSingleMap(types, (type) => type.metadata.attributes[enumTypeKey]);
|
|
115
|
+
for (const [key, label] of categoryEntries) {
|
|
116
|
+
const category = enumKeyCategoryMap.get(key);
|
|
117
|
+
const parentId = assertDefinedPass(categoryParents[key], `Parent category not defined for ${key}`);
|
|
118
|
+
if (isUndefined(category)) {
|
|
119
|
+
const category = await this.#documentCategoryTypeService.withTransaction(tx).createCategory(label, parentId, key);
|
|
120
|
+
categories.push(category);
|
|
121
|
+
}
|
|
122
|
+
else if ((category.label != label) || (category.parentId != parentId)) {
|
|
123
|
+
await this.#documentCategoryTypeService.withTransaction(tx).updateCategory(category.id, { label, parentId });
|
|
124
|
+
}
|
|
537
125
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
126
|
+
for (const [key, label] of typeEntries) {
|
|
127
|
+
const type = enumKeyTypeMap.get(key);
|
|
128
|
+
const enumCategory = typeCategories[key];
|
|
129
|
+
const category = assertDefinedPass(enumKeyCategoryMap.get(enumCategory));
|
|
130
|
+
if (isUndefined(type)) {
|
|
131
|
+
const type = await this.#documentCategoryTypeService.createType(label, category.id, key);
|
|
132
|
+
types.push(type);
|
|
133
|
+
}
|
|
134
|
+
else if ((type.categoryId != category.id) || (type.label != label)) {
|
|
135
|
+
await this.#documentCategoryTypeService.withTransaction(tx).updateType(type.id, { categoryId: category.id, label: label });
|
|
136
|
+
}
|
|
541
137
|
}
|
|
138
|
+
return { categoryMap: enumKeyCategoryMap, typeMap: enumKeyTypeMap };
|
|
542
139
|
});
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
async addDocumentToCollection(parameters, transaction) {
|
|
550
|
-
await this.documentCollectionDocumentService.withOptionalTransaction(transaction).upsert(['collectionId', 'documentId'], { ...parameters, archiveTimestamp: null });
|
|
551
|
-
this.publishChange({ documentIds: [parameters.documentId] });
|
|
552
|
-
}
|
|
553
|
-
async archiveDocument({ collectionId, documentId, metadata }) {
|
|
554
|
-
await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
555
|
-
this.publishChange({ documentIds: [documentId] });
|
|
556
|
-
}
|
|
557
|
-
async createProperty(parameters) {
|
|
558
|
-
return this.documentPropertyService.insert(parameters);
|
|
559
|
-
}
|
|
560
|
-
async assignPropertyToType(parameters) {
|
|
561
|
-
await this.documentTypePropertyService.insert(parameters);
|
|
562
|
-
}
|
|
563
|
-
async enrichDocument(documentId) {
|
|
564
|
-
const document = await this.documentService.load(documentId);
|
|
565
|
-
const { properties, ...extractionResult } = await this.extractFileInformation(document.fileId, document.typeId);
|
|
566
|
-
this.logger.trace(`Applying extraction to document ${document.id}`);
|
|
567
|
-
await this.documentService.transaction(async (documentService, transaction) => {
|
|
568
|
-
await documentService.update(document.id, { ...extractionResult, validated: false });
|
|
569
|
-
await this.setPropertyValues({ documentId, properties: properties }, transaction);
|
|
570
|
-
});
|
|
571
|
-
this.publishChange({ documentIds: [documentId] });
|
|
572
|
-
}
|
|
573
|
-
async enrichDocumentRequestFile(requestFileId) {
|
|
574
|
-
const requestFile = await this.documentRequestFileService.load(requestFileId);
|
|
575
|
-
const request = await this.documentRequestService.load(requestFile.requestId);
|
|
576
|
-
const { properties, typeId: _, ...extractionResult } = await this.extractFileInformation(requestFile.fileId, request.typeId);
|
|
577
|
-
await this.documentRequestFileService.transaction(async (documentRequestFileService, transaction) => {
|
|
578
|
-
await documentRequestFileService.update(requestFile.id, extractionResult);
|
|
579
|
-
await this.setPropertyValues({ requestFileId, properties: properties }, transaction);
|
|
580
|
-
});
|
|
581
|
-
this.publishChange({ requestIds: [request.id] });
|
|
582
|
-
}
|
|
583
|
-
async enrichDocumentRequestAssignmentTask(requestAssignmentTaskId) {
|
|
584
|
-
const task = await this.documentRequestAssignmentTaskService.load(requestAssignmentTaskId);
|
|
585
|
-
const { properties, ...extractionResult } = await this.extractFileInformation(task.fileId);
|
|
586
|
-
await this.documentRequestAssignmentTaskService.transaction(async (documentRequestAssignmentTaskService, transaction) => {
|
|
587
|
-
await documentRequestAssignmentTaskService.update(task.id, extractionResult);
|
|
588
|
-
await this.setPropertyValues({ requestAssignmentTaskId, properties: properties }, transaction);
|
|
589
|
-
});
|
|
590
|
-
void tryIgnoreLogAsync(this.logger, async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId }));
|
|
591
|
-
this.publishChange({ requestAssignmentTaskIds: [requestAssignmentTaskId] });
|
|
592
|
-
}
|
|
593
|
-
async assignDocumentRequest(requestAssignmentTaskId) {
|
|
594
|
-
const session = this.documentRequestAssignmentTaskCollectionService.session;
|
|
595
|
-
const requestAssignmentTaskProperties = session.$with('requestAssignmentTaskProperty').as(session
|
|
596
|
-
.select({
|
|
597
|
-
requestAssignmentTaskId: documentRequestAssignmentTaskPropertyValue.requestAssignmentTaskId,
|
|
598
|
-
propertyId: documentRequestAssignmentTaskPropertyValue.propertyId,
|
|
599
|
-
label: documentProperty.label,
|
|
600
|
-
value: coalesce(toJsonb(documentRequestAssignmentTaskPropertyValue.text), toJsonb(documentRequestAssignmentTaskPropertyValue.integer), toJsonb(documentRequestAssignmentTaskPropertyValue.decimal), toJsonb(documentRequestAssignmentTaskPropertyValue.boolean), toJsonb(documentRequestAssignmentTaskPropertyValue.date)).as('value')
|
|
601
|
-
})
|
|
602
|
-
.from(documentRequestAssignmentTaskPropertyValue)
|
|
603
|
-
.innerJoin(documentProperty, eq(documentProperty.id, documentRequestAssignmentTaskPropertyValue.propertyId)));
|
|
604
|
-
const [task] = await session
|
|
605
|
-
.with(requestAssignmentTaskProperties)
|
|
606
|
-
.select({
|
|
607
|
-
fileId: documentRequestAssignmentTask.fileId,
|
|
608
|
-
collectionIds: arrayAgg(documentRequestAssignmentTaskCollection.collectionId),
|
|
609
|
-
typeCategory: documentCategory.label,
|
|
610
|
-
typeGroup: documentType.group,
|
|
611
|
-
typeLabel: documentType.label,
|
|
612
|
-
title: documentRequestAssignmentTask.title,
|
|
613
|
-
subtitle: documentRequestAssignmentTask.subtitle,
|
|
614
|
-
pages: documentRequestAssignmentTask.pages,
|
|
615
|
-
date: documentRequestAssignmentTask.date,
|
|
616
|
-
summary: documentRequestAssignmentTask.summary,
|
|
617
|
-
tags: documentRequestAssignmentTask.tags,
|
|
618
|
-
assignmentTries: documentRequestAssignmentTask.assignmentTries,
|
|
619
|
-
properties: sql `coalesce(${jsonAgg(requestAssignmentTaskProperties)} FILTER (WHERE ${requestAssignmentTaskProperties.propertyId} IS NOT NULL), '[]'::json)`
|
|
620
|
-
})
|
|
621
|
-
.from(documentRequestAssignmentTask)
|
|
622
|
-
.innerJoin(documentRequestAssignmentTaskCollection, eq(documentRequestAssignmentTaskCollection.requestAssignmentTaskId, documentRequestAssignmentTask.id))
|
|
623
|
-
.innerJoin(documentType, eq(documentType.id, documentRequestAssignmentTask.typeId))
|
|
624
|
-
.innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
625
|
-
.leftJoin(requestAssignmentTaskProperties, eq(requestAssignmentTaskProperties.requestAssignmentTaskId, documentRequestAssignmentTask.id))
|
|
626
|
-
.where(eq(documentRequestAssignmentTask.id, requestAssignmentTaskId))
|
|
627
|
-
.groupBy(documentRequestAssignmentTask.fileId, requestAssignmentTaskProperties.label, documentCategory.label, documentType.group, documentType.label, documentRequestAssignmentTask.title, documentRequestAssignmentTask.subtitle, documentRequestAssignmentTask.pages, documentRequestAssignmentTask.date, documentRequestAssignmentTask.summary, documentRequestAssignmentTask.tags, documentRequestAssignmentTask.assignmentTries);
|
|
628
|
-
assertDefined(task);
|
|
629
|
-
if (task.assignmentTries >= 3) {
|
|
630
|
-
this.logger.error(`Ignoring assignment task "${requestAssignmentTaskId}" because max tries are reached.`);
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
const requestsWithoutFiles = await session
|
|
634
|
-
.select({
|
|
635
|
-
id: documentRequest.id,
|
|
636
|
-
collectionIds: arrayAgg(documentRequestCollection.collectionId),
|
|
637
|
-
documentCategory: documentCategory.label,
|
|
638
|
-
documentGroup: documentType.group,
|
|
639
|
-
documentType: documentType.label,
|
|
640
|
-
comment: documentRequest.comment
|
|
641
|
-
})
|
|
642
|
-
.from(documentRequest)
|
|
643
|
-
.innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequest.id))
|
|
644
|
-
.innerJoin(documentType, eq(documentType.id, documentRequest.typeId))
|
|
645
|
-
.innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
646
|
-
.where(and(inArray(documentRequestCollection.collectionId, task.collectionIds), notExists(session
|
|
647
|
-
.select()
|
|
648
|
-
.from(documentRequestFile)
|
|
649
|
-
.where(and(eq(documentRequestFile.requestId, documentRequest.id), ne(documentRequestFile.approval, false))))))
|
|
650
|
-
.groupBy(documentRequest.id, documentCategory.label, documentType.group, documentType.label, documentRequest.comment);
|
|
651
|
-
const requestsCollectionIds = distinct(requestsWithoutFiles.flatMap((request) => request.collectionIds));
|
|
652
|
-
const collectionNamesMap = await this.resolveNamesMap(...requestsCollectionIds);
|
|
653
|
-
const requests = requestsWithoutFiles.map((request) => ({
|
|
654
|
-
id: request.id,
|
|
655
|
-
collections: request.collectionIds.map((collectionId) => assertDefinedPass(collectionNamesMap[collectionId])),
|
|
656
|
-
documentCategory: request.documentCategory,
|
|
657
|
-
documentGroup: request.documentGroup ?? undefined,
|
|
658
|
-
documentType: request.documentType,
|
|
659
|
-
comment: request.comment ?? undefined
|
|
660
|
-
}));
|
|
661
|
-
const propertyEntries = task.properties.map((property) => [property.label, assertNotNullPass(property.value)]);
|
|
662
|
-
const context = {
|
|
663
|
-
file: {
|
|
664
|
-
documentCategory: task.typeCategory,
|
|
665
|
-
documentGroup: task.typeGroup ?? undefined,
|
|
666
|
-
documentType: task.typeLabel,
|
|
667
|
-
properties: fromEntries(propertyEntries)
|
|
668
|
-
},
|
|
669
|
-
requests
|
|
140
|
+
const mappedCategories = categoryEntries.map(([key]) => [key, assertDefinedPass(categoryMap.get(key), 'Could not map document category.')]);
|
|
141
|
+
const mappedTypes = typeEntries.map(([key]) => [key, assertDefinedPass(typeMap.get(key), 'Could not map document type.')]);
|
|
142
|
+
return {
|
|
143
|
+
categories: fromEntries(mappedCategories),
|
|
144
|
+
types: fromEntries(mappedTypes),
|
|
670
145
|
};
|
|
671
|
-
const result = await this.#aiService.generate({
|
|
672
|
-
...defaultGenerationOptions,
|
|
673
|
-
generationOptions: {
|
|
674
|
-
maxOutputTokens: 100,
|
|
675
|
-
temperature: 0,
|
|
676
|
-
topP: 0.2
|
|
677
|
-
},
|
|
678
|
-
generationSchema: object({ requestId: nullable(string()) }),
|
|
679
|
-
contents: [{
|
|
680
|
-
role: 'user',
|
|
681
|
-
parts: [
|
|
682
|
-
{
|
|
683
|
-
text: `<context>
|
|
684
|
-
${JSON.stringify(context, null, 2)}
|
|
685
|
-
</context>
|
|
686
|
-
|
|
687
|
-
Ordne die Datei unter "file" der passenden Anforderungen unter "requests" zu. Gib es als JSON im angegebenen Schema aus. Wenn keine Anforderung passt, setze requestId auf null.`
|
|
688
|
-
}
|
|
689
|
-
]
|
|
690
|
-
}]
|
|
691
|
-
});
|
|
692
|
-
await this.documentRequestAssignmentTaskService.update(requestAssignmentTaskId, { assignmentTries: sql `${documentRequestAssignmentTask.assignmentTries} + 1` });
|
|
693
|
-
if (isNull(result.json.requestId)) {
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
697
|
-
const requestFile = await this.documentRequestFileService.withTransaction(transaction).insert({
|
|
698
|
-
requestId: result.json.requestId,
|
|
699
|
-
fileId: task.fileId,
|
|
700
|
-
title: task.title,
|
|
701
|
-
subtitle: task.subtitle,
|
|
702
|
-
pages: task.pages,
|
|
703
|
-
date: task.date,
|
|
704
|
-
summary: task.summary,
|
|
705
|
-
tags: task.tags,
|
|
706
|
-
createdDocumentId: null,
|
|
707
|
-
approval: null,
|
|
708
|
-
approvalComment: null,
|
|
709
|
-
approvalTimestamp: null
|
|
710
|
-
});
|
|
711
|
-
await this.setPropertyValues({ requestFileId: requestFile.id, properties: task.properties }, transaction);
|
|
712
|
-
});
|
|
713
|
-
this.publishChange({ collectionIds: requestsCollectionIds });
|
|
714
|
-
}
|
|
715
|
-
async extractFileInformation(fileId, assumeTypeId) {
|
|
716
|
-
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
717
|
-
try {
|
|
718
|
-
const file = await this.documentFileService.load(fileId);
|
|
719
|
-
const fileContentStream = this.getDocumentFileContentStream(fileId);
|
|
720
|
-
const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
|
|
721
|
-
const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
|
|
722
|
-
const pages = file.mimeType.includes('pdf')
|
|
723
|
-
? await tryIgnoreLogAsync(this.logger, async () => {
|
|
724
|
-
this.logger.trace(`Extracting pdf page count for file ${fileId}`);
|
|
725
|
-
return getPdfPageCount(tmpFile.path);
|
|
726
|
-
}, null)
|
|
727
|
-
: null;
|
|
728
|
-
const types = await this.documentTypeService.session
|
|
729
|
-
.select({
|
|
730
|
-
typeId: documentType.id,
|
|
731
|
-
categoryLabel: documentCategory.label,
|
|
732
|
-
typeGroup: documentType.group,
|
|
733
|
-
typeLabel: documentType.label
|
|
734
|
-
})
|
|
735
|
-
.from(documentType)
|
|
736
|
-
.leftJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
737
|
-
.where(isString(assumeTypeId) ? eq(documentType.id, assumeTypeId) : undefined);
|
|
738
|
-
const typeLabelEntries = types.map((type) => ({ id: type.typeId, label: `${type.categoryLabel} | ${type.typeGroup} | ${type.typeLabel}` }));
|
|
739
|
-
const typeLabels = typeLabelEntries.map(({ label }) => label);
|
|
740
|
-
this.logger.trace(`Classifying document file ${fileId}`);
|
|
741
|
-
const documentTypeGeneration = await this.#aiService.generate({
|
|
742
|
-
...defaultGenerationOptions,
|
|
743
|
-
generationSchema: object({
|
|
744
|
-
documentType: enumeration(typeLabels)
|
|
745
|
-
}),
|
|
746
|
-
contents: [
|
|
747
|
-
{
|
|
748
|
-
role: 'user',
|
|
749
|
-
parts: [
|
|
750
|
-
{ file: filePart.file },
|
|
751
|
-
{ text: `Klassifiziere den Inhalt des Dokuments in das angegebenen JSON Schema.` }
|
|
752
|
-
]
|
|
753
|
-
}
|
|
754
|
-
]
|
|
755
|
-
});
|
|
756
|
-
const typeId = typeLabelEntries.find((entry) => entry.label == documentTypeGeneration.json.documentType)?.id;
|
|
757
|
-
const typeProperties = isDefined(typeId) ? await this.documentTypePropertyService.loadManyByQuery({ typeId }) : undefined;
|
|
758
|
-
const propertyIds = typeProperties?.map((property) => property.propertyId);
|
|
759
|
-
const properties = isDefined(propertyIds) ? await this.documentPropertyService.loadManyByQuery({ id: { $in: propertyIds } }) : undefined;
|
|
760
|
-
const propertiesSchemaEntries = properties?.map((property) => {
|
|
761
|
-
const schema = match(property.dataType)
|
|
762
|
-
.with('text', () => nullable(string()))
|
|
763
|
-
.with('integer', () => nullable(integer()))
|
|
764
|
-
.with('decimal', () => nullable(number()))
|
|
765
|
-
.with('boolean', () => nullable(boolean()))
|
|
766
|
-
.with('date', () => nullable(object({ year: integer(), month: integer(), day: integer() })))
|
|
767
|
-
.exhaustive();
|
|
768
|
-
return [property.label, schema];
|
|
769
|
-
});
|
|
770
|
-
const generationSchema = object({
|
|
771
|
-
documentTitle: string(),
|
|
772
|
-
documentSubtitle: nullable(string()),
|
|
773
|
-
documentSummary: string(),
|
|
774
|
-
documentTags: array(string()),
|
|
775
|
-
documentDate: nullable(object({ year: integer(), month: integer(), day: integer() })),
|
|
776
|
-
...((isUndefined(propertiesSchemaEntries) || (propertiesSchemaEntries.length == 0))
|
|
777
|
-
? {}
|
|
778
|
-
: { documentProperties: object(fromEntries(propertiesSchemaEntries)) })
|
|
779
|
-
});
|
|
780
|
-
this.logger.trace(`Extracting document file ${fileId}`);
|
|
781
|
-
const { json: extraction } = await this.#aiService.generate({
|
|
782
|
-
model: 'gemini-2.0-flash',
|
|
783
|
-
generationOptions: {
|
|
784
|
-
maxOutputTokens: 2048,
|
|
785
|
-
temperature: 0.2,
|
|
786
|
-
topP: 0.2
|
|
787
|
-
},
|
|
788
|
-
generationSchema,
|
|
789
|
-
contents: [
|
|
790
|
-
{
|
|
791
|
-
role: 'user',
|
|
792
|
-
parts: [
|
|
793
|
-
{ file: filePart.file },
|
|
794
|
-
{
|
|
795
|
-
text: `Extrahiere den Inhalt des Dokuments in das angegebenen JSON Schema.
|
|
796
|
-
|
|
797
|
-
Gib in der summary ausführlich an, welche Informationen in dem Dokument vorkommen(ohne konkrete Werte).
|
|
798
|
-
Erstelle bis zu 7 möglichst spezifische Tags.
|
|
799
|
-
Antworte auf deutsch.`
|
|
800
|
-
}
|
|
801
|
-
]
|
|
802
|
-
}
|
|
803
|
-
]
|
|
804
|
-
});
|
|
805
|
-
const filteredDocumentTags = extraction.documentTags.filter((tag) => (tag != extraction.documentTitle) && (tag != extraction.documentSubtitle));
|
|
806
|
-
const date = isNotNull(extraction.documentDate) ? dateObjectToNumericDate(extraction.documentDate) : null;
|
|
807
|
-
const parsedProperties = isUndefined(extraction.documentProperties)
|
|
808
|
-
? []
|
|
809
|
-
: objectEntries(extraction.documentProperties)
|
|
810
|
-
.map(([propertyLabel, rawValue]) => {
|
|
811
|
-
if (isNull(rawValue)) {
|
|
812
|
-
return null;
|
|
813
|
-
}
|
|
814
|
-
const property = assertDefinedPass(properties?.find((property) => property.label == propertyLabel));
|
|
815
|
-
const value = match(rawValue)
|
|
816
|
-
.with({ year: P.number }, (val) => dateObjectToNumericDate(val))
|
|
817
|
-
.otherwise((val) => val);
|
|
818
|
-
return { propertyId: property.id, dataType: property.dataType, value };
|
|
819
|
-
})
|
|
820
|
-
.filter(isNotNull);
|
|
821
|
-
return {
|
|
822
|
-
typeId: typeId ?? null,
|
|
823
|
-
title: extraction.documentTitle,
|
|
824
|
-
subtitle: extraction.documentSubtitle,
|
|
825
|
-
pages,
|
|
826
|
-
date,
|
|
827
|
-
summary: extraction.documentSummary,
|
|
828
|
-
tags: filteredDocumentTags,
|
|
829
|
-
properties: parsedProperties
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
catch (e_1) {
|
|
833
|
-
env_1.error = e_1;
|
|
834
|
-
env_1.hasError = true;
|
|
835
|
-
}
|
|
836
|
-
finally {
|
|
837
|
-
const result_1 = __disposeResources(env_1);
|
|
838
|
-
if (result_1)
|
|
839
|
-
await result_1;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
async getDocumentFileContent(fileId) {
|
|
843
|
-
const key = getDocumentFileKey(fileId);
|
|
844
|
-
return this.fileObjectStorage.getContent(key);
|
|
845
|
-
}
|
|
846
|
-
getDocumentFileContentStream(fileId) {
|
|
847
|
-
const key = getDocumentFileKey(fileId);
|
|
848
|
-
return this.fileObjectStorage.getContentStream(key);
|
|
849
|
-
}
|
|
850
|
-
async getDocumentFileContentObjectUrl(title, file, download) {
|
|
851
|
-
const key = getDocumentFileKey(file.id);
|
|
852
|
-
const fileExtension = getMimeTypeExtensions(file.mimeType)[0] ?? 'bin';
|
|
853
|
-
const disposition = download ? 'attachment' : 'inline';
|
|
854
|
-
const filename = `${title}.${fileExtension}`;
|
|
855
|
-
return this.fileObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
|
|
856
|
-
'Response-Content-Type': file.mimeType,
|
|
857
|
-
'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
publishChange({ collectionIds, documentIds, requestIds, requestFileIds, requestAssignmentTaskIds: requestAssignmentTasksIds }) {
|
|
861
|
-
void tryIgnoreLogAsync(this.logger, async () => {
|
|
862
|
-
const queries = [
|
|
863
|
-
isUndefined(documentIds)
|
|
864
|
-
? undefined
|
|
865
|
-
: this.session
|
|
866
|
-
.selectDistinct({ collectionId: documentCollectionDocument.collectionId })
|
|
867
|
-
.from(documentCollectionDocument)
|
|
868
|
-
.where(inArray(documentCollectionDocument.documentId, documentIds)),
|
|
869
|
-
isUndefined(requestIds)
|
|
870
|
-
? undefined
|
|
871
|
-
: this.session
|
|
872
|
-
.selectDistinct({ collectionId: documentRequestCollection.collectionId })
|
|
873
|
-
.from(documentRequestCollection)
|
|
874
|
-
.where(inArray(documentRequestCollection.requestId, requestIds)),
|
|
875
|
-
isUndefined(requestFileIds)
|
|
876
|
-
? undefined
|
|
877
|
-
: this.session
|
|
878
|
-
.selectDistinct({ collectionId: documentRequestCollection.collectionId })
|
|
879
|
-
.from(documentRequestFile)
|
|
880
|
-
.innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequestFile.requestId))
|
|
881
|
-
.where(inArray(documentRequestFile.requestId, requestFileIds)),
|
|
882
|
-
isUndefined(requestAssignmentTasksIds)
|
|
883
|
-
? undefined
|
|
884
|
-
: this.session
|
|
885
|
-
.selectDistinct({ collectionId: documentRequestAssignmentTaskCollection.collectionId })
|
|
886
|
-
.from(documentRequestAssignmentTaskCollection)
|
|
887
|
-
.where(inArray(documentRequestAssignmentTaskCollection.requestAssignmentTaskId, requestAssignmentTasksIds))
|
|
888
|
-
];
|
|
889
|
-
const filteredQueries = queries.filter(isDefined);
|
|
890
|
-
const mappings = (filteredQueries.length == 1)
|
|
891
|
-
? await filteredQueries[0]
|
|
892
|
-
: (filteredQueries.length > 1)
|
|
893
|
-
? await union(filteredQueries[0], filteredQueries[1], ...filteredQueries.slice(2))
|
|
894
|
-
: [];
|
|
895
|
-
const allCollectionIds = distinct([
|
|
896
|
-
...(collectionIds ?? []),
|
|
897
|
-
...mappings.map((mapping) => mapping.collectionId)
|
|
898
|
-
]);
|
|
899
|
-
if (allCollectionIds.length == 0) {
|
|
900
|
-
return;
|
|
901
|
-
}
|
|
902
|
-
this.collectionChangeMessageBus.publishAndForget(allCollectionIds);
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
};
|
|
906
|
-
DocumentManagementService = DocumentManagementService_1 = __decorate([
|
|
907
|
-
Singleton({
|
|
908
|
-
providers: [
|
|
909
|
-
{ provide: DatabaseConfig, useFactory: (_, context) => context.resolve(DocumentManagementConfig).database ?? context.resolve(DatabaseConfig, undefined, { skipSelf: true }) }
|
|
910
|
-
]
|
|
911
|
-
})
|
|
912
|
-
], DocumentManagementService);
|
|
913
|
-
export { DocumentManagementService };
|
|
914
|
-
function getDocumentFileKey(id) {
|
|
915
|
-
return `${id.slice(0, 2)} /${id.slice(0, 4)}/${id} `;
|
|
916
|
-
}
|
|
917
|
-
const validators = {
|
|
918
|
-
[DocumentPropertyDataType.Text]: (value) => isString(value) || isNull(value),
|
|
919
|
-
[DocumentPropertyDataType.Integer]: (value) => isNumber(value) || isNull(value),
|
|
920
|
-
[DocumentPropertyDataType.Decimal]: (value) => isNumber(value) || isNull(value),
|
|
921
|
-
[DocumentPropertyDataType.Boolean]: (value) => isBoolean(value) || isNull(value),
|
|
922
|
-
[DocumentPropertyDataType.Date]: (value) => isNumber(value) || isNull(value)
|
|
923
|
-
};
|
|
924
|
-
function validatePropertyValue(propertyId, dataType, value) {
|
|
925
|
-
const valid = validators[dataType](value);
|
|
926
|
-
if (!valid) {
|
|
927
|
-
throw new BadRequestError(`Invalid value for data type ${dataType} for property ${propertyId}.`);
|
|
928
146
|
}
|
|
929
147
|
}
|