@tstdl/base 0.92.85 → 0.92.87
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.service.d.ts +4 -4
- package/ai/ai.service.js +27 -14
- package/ai/types.d.ts +5 -4
- package/api/server/gateway.js +1 -1
- package/authentication/authentication.api.d.ts +9 -9
- package/authentication/models/schemas.d.ts +2 -2
- package/authentication/server/authentication-ancillary.service.d.ts +6 -4
- package/authentication/server/authentication-ancillary.service.js +5 -5
- package/cancellation/token.d.ts +1 -1
- package/context/context.d.ts +1 -9
- package/context/context.js +8 -5
- package/document-management/api/document-management.api.d.ts +142 -110
- package/document-management/models/document-category.model.d.ts +1 -0
- package/document-management/models/document-category.model.js +2 -0
- package/document-management/models/document-collection-document.model.js +7 -3
- package/document-management/models/document-property-value.model.d.ts +13 -14
- package/document-management/models/document-property-value.model.js +60 -27
- package/document-management/models/document-property.model.d.ts +2 -0
- package/document-management/models/document-property.model.js +4 -1
- package/document-management/models/document-request-assignment-task-collection.model.d.ts +7 -0
- package/document-management/models/document-request-assignment-task-collection.model.js +32 -0
- package/document-management/models/document-request-assignment-task.model.d.ts +14 -0
- package/document-management/models/document-request-assignment-task.model.js +72 -0
- package/document-management/models/document-request-collection.model.d.ts +1 -0
- package/document-management/models/document-request-collection.model.js +7 -3
- package/document-management/models/document-request-file.model.d.ts +7 -2
- package/document-management/models/document-request-file.model.js +29 -4
- package/document-management/models/document-request.model.d.ts +1 -0
- package/document-management/models/document-requests-template.js +2 -0
- package/document-management/models/document-type-property.model.js +7 -3
- package/document-management/models/document-type.model.d.ts +1 -0
- package/document-management/models/document-type.model.js +7 -3
- package/document-management/models/document.model.d.ts +5 -2
- package/document-management/models/document.model.js +21 -6
- package/document-management/models/index.d.ts +2 -0
- package/document-management/models/index.js +2 -0
- package/document-management/models/service-models/document.service-model.d.ts +84 -65
- package/document-management/models/service-models/document.service-model.js +11 -6
- package/document-management/models/service-models/document.view-model.d.ts +1 -1
- package/document-management/models/service-models/document.view-model.js +2 -2
- package/document-management/server/drizzle/{0000_sloppy_fenris.sql → 0000_cool_victor_mancha.sql} +99 -43
- package/document-management/server/drizzle/meta/0000_snapshot.json +518 -130
- package/document-management/server/drizzle/meta/_journal.json +2 -2
- package/document-management/server/drizzle.config.js +1 -1
- package/document-management/server/module.d.ts +3 -2
- package/document-management/server/module.js +4 -2
- package/document-management/server/schemas.d.ts +36 -0
- package/document-management/server/schemas.js +37 -0
- package/document-management/server/services/document-management-ancillary.service.d.ts +4 -0
- package/document-management/server/services/document-management-ancillary.service.js +13 -0
- package/document-management/server/services/document-management.service.d.ts +71 -22
- package/document-management/server/services/document-management.service.js +528 -81
- package/document-management/server/services/index.d.ts +1 -0
- package/document-management/server/services/index.js +1 -0
- package/eslint.config.js +1 -0
- package/examples/document-management/main.d.ts +5 -0
- package/examples/document-management/main.js +20 -2
- package/examples/orm/schemas.d.ts +1 -1
- package/file/index.d.ts +1 -0
- package/file/index.js +1 -0
- package/file/temporary-file.d.ts +17 -0
- package/file/temporary-file.js +49 -0
- package/http/server/http-server-response.d.ts +2 -0
- package/http/server/http-server-response.js +13 -0
- package/injector/index.d.ts +1 -0
- package/injector/index.js +1 -0
- package/injector/injector.js +19 -7
- package/injector/interfaces.d.ts +1 -1
- package/injector/interfaces.js +1 -1
- package/injector/resolution.d.ts +15 -0
- package/injector/resolution.js +6 -0
- package/logger/console/logger.d.ts +1 -1
- package/logger/logger.d.ts +1 -1
- package/mail/drizzle.config.js +1 -1
- package/mail/models/schemas.d.ts +1 -1
- package/object-storage/object-storage.d.ts +5 -7
- package/object-storage/s3/s3.object-storage.d.ts +0 -1
- package/object-storage/s3/s3.object-storage.js +0 -3
- package/orm/{server/data-types → data-types}/numeric-date.js +2 -3
- package/orm/decorators.d.ts +17 -8
- package/orm/decorators.js +13 -7
- package/orm/entity.d.ts +5 -7
- package/orm/entity.js +11 -7
- package/orm/index.d.ts +2 -0
- package/orm/index.js +2 -0
- package/orm/query.d.ts +1 -3
- package/orm/query.js +0 -1
- package/orm/repository.types.d.ts +32 -0
- package/orm/repository.types.js +1 -0
- package/orm/server/database-schema.d.ts +4 -4
- package/orm/server/drizzle/schema-converter.d.ts +1 -1
- package/orm/server/drizzle/schema-converter.js +48 -19
- package/orm/server/index.d.ts +1 -0
- package/orm/server/index.js +1 -0
- package/orm/server/query-converter.d.ts +1 -2
- package/orm/server/query-converter.js +66 -61
- package/orm/server/repository.d.ts +80 -43
- package/orm/server/repository.js +219 -112
- package/orm/server/sqls.d.ts +15 -0
- package/orm/server/sqls.js +19 -0
- package/orm/server/types.d.ts +3 -3
- package/orm/types.d.ts +4 -4
- package/orm/utils.d.ts +3 -0
- package/orm/utils.js +6 -0
- package/package.json +23 -19
- package/pdf/pdf.service.d.ts +0 -1
- package/pdf/pdf.service.js +1 -95
- package/pdf/utils.d.ts +3 -1
- package/pdf/utils.js +129 -4
- package/promise/lazy-promise.d.ts +3 -3
- package/queue/enqueue-batch.d.ts +1 -0
- package/queue/enqueue-batch.js +1 -1
- package/queue/mongo/queue.d.ts +9 -4
- package/queue/mongo/queue.js +5 -6
- package/queue/postgres/drizzle/0000_zippy_moondragon.sql +11 -0
- package/queue/postgres/drizzle/meta/0000_snapshot.json +90 -0
- package/queue/postgres/drizzle/meta/_journal.json +13 -0
- package/queue/postgres/drizzle.config.d.ts +2 -0
- package/queue/postgres/drizzle.config.js +11 -0
- package/queue/postgres/index.d.ts +4 -0
- package/queue/postgres/index.js +4 -0
- package/queue/postgres/job.model.d.ts +13 -0
- package/queue/postgres/job.model.js +55 -0
- package/queue/postgres/module.d.ts +9 -0
- package/queue/postgres/module.js +29 -0
- package/queue/postgres/queue.d.ts +28 -0
- package/queue/postgres/queue.js +147 -0
- package/queue/postgres/queue.provider.d.ts +7 -0
- package/queue/postgres/queue.provider.js +21 -0
- package/queue/postgres/schemas.d.ts +3 -0
- package/queue/postgres/schemas.js +4 -0
- package/queue/provider.d.ts +2 -1
- package/queue/queue.d.ts +32 -6
- package/queue/queue.js +43 -0
- package/schema/schemas/object.d.ts +1 -1
- package/utils/date-time.d.ts +4 -2
- package/utils/date-time.js +10 -3
- package/utils/format-error.js +0 -1
- package/utils/object/lazy-property.js +0 -1
- package/utils/timing.d.ts +4 -3
- package/utils/timing.js +3 -1
- package/utils/try-ignore.d.ts +9 -2
- package/utils/try-ignore.js +30 -6
- package/document-management/models/schemas.d.ts +0 -33
- package/document-management/models/schemas.js +0 -34
- /package/orm/{server/data-types → data-types}/bytea.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/bytea.js +0 -0
- /package/orm/{server/data-types → data-types}/index.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/index.js +0 -0
- /package/orm/{server/data-types → data-types}/numeric-date.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/timestamp.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/timestamp.js +0 -0
|
@@ -4,63 +4,179 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
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
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
|
|
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';
|
|
8
63
|
import { Enumerable } from '../../../enumerable/index.js';
|
|
9
64
|
import { BadRequestError } from '../../../errors/index.js';
|
|
10
|
-
import { getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
|
|
11
|
-
import { Singleton, inject, injectArgument, provide, resolveArgumentType } from '../../../injector/index.js';
|
|
65
|
+
import { TemporaryFile, getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
|
|
66
|
+
import { Singleton, afterResolve, inject, injectArgument, provide, resolveArgumentType } from '../../../injector/index.js';
|
|
67
|
+
import { Logger } from '../../../logger/logger.js';
|
|
12
68
|
import { ObjectStorage } from '../../../object-storage/index.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
69
|
+
import { getEntityMap } from '../../../orm/index.js';
|
|
70
|
+
import { DatabaseConfig, EntityRepositoryConfig, TRANSACTION_TIMESTAMP, arrayAgg, coalesce, injectRepository, jsonAgg, toJsonb } from '../../../orm/server/index.js';
|
|
71
|
+
import { getPdfPageCount } from '../../../pdf/index.js';
|
|
72
|
+
import { Queue } from '../../../queue/queue.js';
|
|
73
|
+
import { array, boolean, enumeration, integer, nullable, number, object, string } from '../../../schema/index.js';
|
|
74
|
+
import { distinct, toArray } from '../../../utils/array/index.js';
|
|
75
|
+
import { compareByValueSelectionToOrder } from '../../../utils/comparison.js';
|
|
76
|
+
import { digest } from '../../../utils/cryptography.js';
|
|
77
|
+
import { currentTimestamp, dateObjectToNumericDate } from '../../../utils/date-time.js';
|
|
16
78
|
import { groupToMap } from '../../../utils/iterable-helpers/index.js';
|
|
79
|
+
import { fromEntries, objectEntries } from '../../../utils/object/object.js';
|
|
17
80
|
import { readBinaryStream } from '../../../utils/stream/index.js';
|
|
18
|
-
import {
|
|
81
|
+
import { tryIgnoreLogAsync } from '../../../utils/try-ignore.js';
|
|
82
|
+
import { assertBooleanPass, assertDefined, assertDefinedPass, assertNotNullPass, assertNumberPass, assertStringPass, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, isUndefined } from '../../../utils/type-guards.js';
|
|
83
|
+
import { millisecondsPerMinute } from '../../../utils/units.js';
|
|
84
|
+
import { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
|
|
19
85
|
import { DocumentManagementConfig } from '../module.js';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
86
|
+
import { documentCategory, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
|
|
87
|
+
import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
|
|
88
|
+
const defaultGenerationOptions = {
|
|
89
|
+
model: 'gemini-2.0-flash',
|
|
90
|
+
generationOptions: {
|
|
91
|
+
maxOutputTokens: 2048,
|
|
92
|
+
temperature: 0.2,
|
|
93
|
+
topP: 0.2
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
let DocumentManagementService = DocumentManagementService_1 = class DocumentManagementService {
|
|
97
|
+
#aiService = inject(AiService);
|
|
98
|
+
ancillaryService = inject(DocumentManagementAncillaryService);
|
|
24
99
|
documentCategoryService = injectRepository(DocumentCategory);
|
|
25
|
-
|
|
100
|
+
documentCollectionDocumentService = injectRepository(DocumentCollectionDocument);
|
|
101
|
+
documentCollectionService = injectRepository(DocumentCollection);
|
|
102
|
+
documentFileService = injectRepository(DocumentFile);
|
|
103
|
+
documentPropertyService = injectRepository(DocumentProperty);
|
|
104
|
+
documentPropertyValueService = injectRepository(DocumentPropertyValue);
|
|
105
|
+
documentRequestAssignmentTaskCollectionService = injectRepository(DocumentRequestAssignmentTaskCollection);
|
|
106
|
+
documentRequestAssignmentTaskService = injectRepository(DocumentRequestAssignmentTask);
|
|
107
|
+
documentRequestCollectionService = injectRepository(DocumentRequestCollection);
|
|
108
|
+
documentRequestFilePropertyValueService = injectRepository(DocumentRequestFilePropertyValue);
|
|
109
|
+
documentRequestAssignmentTaskPropertyValueService = injectRepository(DocumentRequestAssignmentTaskPropertyValue);
|
|
110
|
+
documentRequestFileService = injectRepository(DocumentRequestFile);
|
|
26
111
|
documentRequestService = injectRepository(DocumentRequest);
|
|
27
112
|
documentRequestsTemplateService = injectRepository(DocumentRequestsTemplate);
|
|
28
113
|
documentRequestTemplateService = injectRepository(DocumentRequestTemplate);
|
|
29
|
-
|
|
30
|
-
documentRequestFileService = injectRepository(DocumentRequestFile);
|
|
31
|
-
documentPropertyService = injectRepository(DocumentProperty);
|
|
114
|
+
documentService = injectRepository(Document);
|
|
32
115
|
documentTypePropertyService = injectRepository(DocumentTypeProperty);
|
|
33
|
-
|
|
34
|
-
documentPropertyIntegerValueService = injectRepository(DocumentPropertyIntegerValue);
|
|
35
|
-
documentPropertyDecimalValueService = injectRepository(DocumentPropertyDecimalValue);
|
|
36
|
-
documentPropertyBooleanValueService = injectRepository(DocumentPropertyBooleanValue);
|
|
37
|
-
documentCollectionDocumentService = injectRepository(DocumentCollectionDocument);
|
|
116
|
+
documentTypeService = injectRepository(DocumentType);
|
|
38
117
|
fileObjectStorage = inject(ObjectStorage, (injectArgument(this, { optional: true }) ?? inject(DocumentManagementConfig)).fileObjectStorageModule);
|
|
118
|
+
extractionQueue = inject((Queue), { name: 'DocumentManagement:extraction', processTimeout: 15 * millisecondsPerMinute });
|
|
119
|
+
assignmentQueue = inject((Queue), { name: 'DocumentManagement:assignment', processTimeout: 15 * millisecondsPerMinute });
|
|
120
|
+
logger = inject(Logger, DocumentManagementService_1.name);
|
|
121
|
+
[afterResolve](_, { cancellationSignal }) {
|
|
122
|
+
this.processQueues(cancellationSignal);
|
|
123
|
+
}
|
|
124
|
+
processQueues(cancellationSignal) {
|
|
125
|
+
this.extractionQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
|
|
126
|
+
const [entry] = objectEntries(job.data);
|
|
127
|
+
this.logger.info(`Processing extraction job for ${entry?.[0]} "${entry?.[1]}"`);
|
|
128
|
+
await match(job.data)
|
|
129
|
+
.with({ documentId: P.string.select() }, async (documentId) => this.enrichDocument(documentId))
|
|
130
|
+
.with({ requestFileId: P.string.select() }, async (requestFileId) => this.enrichDocumentRequestFile(requestFileId))
|
|
131
|
+
.with({ requestAssignmentTaskId: P.string.select() }, async (requestAssignmentTaskId) => this.enrichDocumentRequestAssignmentTask(requestAssignmentTaskId))
|
|
132
|
+
.exhaustive();
|
|
133
|
+
}, this.logger);
|
|
134
|
+
this.assignmentQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
|
|
135
|
+
this.logger.info(`Processing assignment job "${job.data.requestAssignmentTaskId}"`);
|
|
136
|
+
await this.assignDocumentRequest(job.data.requestAssignmentTaskId);
|
|
137
|
+
}, this.logger);
|
|
138
|
+
}
|
|
139
|
+
async resolveNames(...collectionsOrIds) {
|
|
140
|
+
if (collectionsOrIds.length == 0) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
const loadIds = collectionsOrIds.filter((collection) => isString(collection));
|
|
144
|
+
if (loadIds.length == 0) {
|
|
145
|
+
return this.ancillaryService.resolveNames(collectionsOrIds);
|
|
146
|
+
}
|
|
147
|
+
const loadedCollections = await this.documentCollectionService.loadManyByQuery({ id: { $in: loadIds } });
|
|
148
|
+
const collections = collectionsOrIds.map((collectionOrId) => isString(collectionOrId)
|
|
149
|
+
? assertDefinedPass(loadedCollections.find((collection) => collection.id == collectionOrId), `Could not load collection "${collectionOrId}".`)
|
|
150
|
+
: collectionOrId);
|
|
151
|
+
return this.ancillaryService.resolveNames(collections);
|
|
152
|
+
}
|
|
153
|
+
async resolveNamesMap(...collectionsOrIds) {
|
|
154
|
+
const names = await this.resolveNames(...collectionsOrIds);
|
|
155
|
+
const entries = collectionsOrIds.map((collectionOrId, index) => [isString(collectionOrId) ? collectionOrId : collectionOrId.id, names[index]]);
|
|
156
|
+
return fromEntries(entries);
|
|
157
|
+
}
|
|
39
158
|
async loadData(collectionIds, collectionsMetadata) {
|
|
40
159
|
return this.documentCollectionService.transaction(async (_, transaction) => {
|
|
41
160
|
const [collections, collectionDocuments, requestCollections, categories, types] = await Promise.all([
|
|
42
161
|
this.documentCollectionService.withTransaction(transaction).loadMany(collectionIds),
|
|
43
162
|
this.documentCollectionDocumentService.withTransaction(transaction).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
44
163
|
this.documentRequestCollectionService.withTransaction(transaction).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
45
|
-
this.documentCategoryService.withTransaction(transaction).loadManyByQuery({}, { order:
|
|
46
|
-
this.documentTypeService.withTransaction(transaction).loadManyByQuery({}, { order:
|
|
164
|
+
this.documentCategoryService.withTransaction(transaction).loadManyByQuery({}, { order: 'label' }),
|
|
165
|
+
this.documentTypeService.withTransaction(transaction).loadManyByQuery({}, { order: 'label' })
|
|
47
166
|
]);
|
|
48
167
|
const documentIds = collectionDocuments.map((document) => document.documentId);
|
|
49
168
|
const requestIds = requestCollections.map((requestCollection) => requestCollection.requestId);
|
|
50
|
-
const [documents, requests, requestFiles,
|
|
169
|
+
const [documents, requests, requestFiles, propertyValues] = await Promise.all([
|
|
51
170
|
this.documentService.withTransaction(transaction).loadManyByQuery({ id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
52
171
|
this.documentRequestService.withTransaction(transaction).loadManyByQuery({ id: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
53
172
|
this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
54
|
-
this.
|
|
55
|
-
this.documentPropertyIntegerValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
|
|
56
|
-
this.documentPropertyDecimalValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
|
|
57
|
-
this.documentPropertyBooleanValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
|
|
173
|
+
this.documentPropertyValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
|
|
58
174
|
]);
|
|
59
175
|
const documentFileIds = documents.map((document) => document.fileId);
|
|
60
176
|
const requestFileIds = requestFiles.map((requestFile) => requestFile.fileId);
|
|
61
177
|
const files = await this.documentFileService.withTransaction(transaction).loadManyByQuery({ id: { $in: [...documentFileIds, ...requestFileIds] } }, { order: { 'metadata.createTimestamp': 'desc' } });
|
|
62
178
|
const requestsFilesMap = groupToMap(requestFiles, (requestFile) => requestFile.requestId);
|
|
63
|
-
const valuesMap = Enumerable.from(
|
|
179
|
+
const valuesMap = Enumerable.from(propertyValues).groupToMap((value) => value.documentId);
|
|
64
180
|
const collectionViews = collections.toSorted(compareByValueSelectionToOrder(collectionIds, (collection) => collection.id)).map((collection) => ({
|
|
65
181
|
...collection,
|
|
66
182
|
name: collectionsMetadata?.[collection.id]?.name ?? null,
|
|
@@ -88,7 +204,7 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
88
204
|
}
|
|
89
205
|
async loadDocumentRequestsTemplateData() {
|
|
90
206
|
const [requestsTemplates, requestTemplates] = await Promise.all([
|
|
91
|
-
this.documentRequestsTemplateService.loadManyByQuery({}, { order:
|
|
207
|
+
this.documentRequestsTemplateService.loadManyByQuery({}, { order: 'label' }),
|
|
92
208
|
this.documentRequestTemplateService.loadManyByQuery({})
|
|
93
209
|
]);
|
|
94
210
|
const templates = requestsTemplates.map((requestsTemplate) => ({
|
|
@@ -99,8 +215,8 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
99
215
|
}
|
|
100
216
|
async loadCategoriesAndTypes() {
|
|
101
217
|
const [categories, types] = await Promise.all([
|
|
102
|
-
this.documentCategoryService.loadManyByQuery({}, { order:
|
|
103
|
-
this.documentTypeService.loadManyByQuery({}, { order:
|
|
218
|
+
this.documentCategoryService.loadManyByQuery({}, { order: 'label' }),
|
|
219
|
+
this.documentTypeService.loadManyByQuery({}, { order: 'label' })
|
|
104
220
|
]);
|
|
105
221
|
return { categories, types };
|
|
106
222
|
}
|
|
@@ -169,21 +285,20 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
169
285
|
async deleteDocumentRequestTemplate(parameters) {
|
|
170
286
|
return this.documentRequestTemplateService.delete(parameters.id);
|
|
171
287
|
}
|
|
172
|
-
async createDocument({ typeId,
|
|
173
|
-
|
|
288
|
+
async createDocument({ typeId, title, subtitle, pages, date, summary, tags, originalFileName, collectionIds, properties, metadata }, content) {
|
|
289
|
+
const document = await this.documentService.transaction(async (_, transaction) => {
|
|
174
290
|
const documentFile = await this.createDocumentFile(content, originalFileName, transaction);
|
|
175
|
-
const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
|
|
179
|
-
}
|
|
291
|
+
const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata });
|
|
292
|
+
for (const collectionId of toArray(collectionIds)) {
|
|
293
|
+
await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
|
|
180
294
|
}
|
|
181
295
|
if (isDefined(properties)) {
|
|
182
|
-
|
|
183
|
-
await this.setDocumentProperties(mappedProperties, transaction);
|
|
296
|
+
await this.setPropertyValues({ documentId: document.id, properties }, transaction);
|
|
184
297
|
}
|
|
185
298
|
return document;
|
|
186
299
|
});
|
|
300
|
+
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ documentId: document.id }), this.logger);
|
|
301
|
+
return document;
|
|
187
302
|
}
|
|
188
303
|
async approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }) {
|
|
189
304
|
return this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
@@ -196,15 +311,18 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
196
311
|
const document = await this.documentService.withTransaction(transaction).insert({
|
|
197
312
|
fileId: requestFile.fileId,
|
|
198
313
|
typeId: request.typeId,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
314
|
+
title: requestFile.title,
|
|
315
|
+
subtitle: requestFile.subtitle,
|
|
316
|
+
pages: requestFile.pages,
|
|
317
|
+
date: requestFile.date,
|
|
318
|
+
summary: requestFile.summary,
|
|
319
|
+
tags: requestFile.tags,
|
|
202
320
|
metadata: documentMetadata
|
|
203
321
|
});
|
|
204
322
|
for (const { collectionId } of requestCollections) {
|
|
205
323
|
await this.addDocumentToCollection({ collectionId, documentId: document.id }, transaction);
|
|
206
324
|
}
|
|
207
|
-
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: true, approvalComment, approvalTimestamp:
|
|
325
|
+
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: true, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, createdDocumentId: document.id, metadata: requestFileMetadata });
|
|
208
326
|
return document;
|
|
209
327
|
});
|
|
210
328
|
}
|
|
@@ -214,11 +332,11 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
214
332
|
if (requestFile.approval == true) {
|
|
215
333
|
throw new BadRequestError('Document request file already accepted.');
|
|
216
334
|
}
|
|
217
|
-
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp:
|
|
335
|
+
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
218
336
|
});
|
|
219
337
|
}
|
|
220
|
-
async updateDocumentRequestFile({ id,
|
|
221
|
-
return this.documentRequestFileService.update(id, {
|
|
338
|
+
async updateDocumentRequestFile({ id, title, approvalComment, metadata }) {
|
|
339
|
+
return this.documentRequestFileService.update(id, { title, approvalComment, metadata });
|
|
222
340
|
}
|
|
223
341
|
async deleteDocumentRequestFile({ id, metadata }) {
|
|
224
342
|
const requestFile = await this.documentRequestFileService.load(id);
|
|
@@ -227,12 +345,14 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
227
345
|
}
|
|
228
346
|
await this.documentRequestFileService.delete(id, metadata);
|
|
229
347
|
}
|
|
230
|
-
async createDocumentFile(content, originalFileName,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const documentFile = await this.documentFileService
|
|
348
|
+
async createDocumentFile(content, originalFileName, transaction) {
|
|
349
|
+
const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
|
|
350
|
+
const hash = await digest('SHA-256', contentAsUint8Array).toHex();
|
|
351
|
+
const mimeType = await getMimeType(contentAsUint8Array);
|
|
352
|
+
return this.documentFileService.useTransaction(transaction, async (_, transaction) => {
|
|
353
|
+
const documentFile = await this.documentFileService
|
|
354
|
+
.withTransaction(transaction)
|
|
355
|
+
.insert({
|
|
236
356
|
originalFileName,
|
|
237
357
|
mimeType,
|
|
238
358
|
hash,
|
|
@@ -243,8 +363,8 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
243
363
|
return documentFile;
|
|
244
364
|
});
|
|
245
365
|
}
|
|
246
|
-
async createDocumentRequestFile({ requestId,
|
|
247
|
-
|
|
366
|
+
async createDocumentRequestFile({ requestId, title, subtitle, date, summary, tags, originalFileName, metadata }, content) {
|
|
367
|
+
const documentRequestFile = await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
248
368
|
const [request, existingRequestFiles] = await Promise.all([
|
|
249
369
|
this.documentRequestService.withTransaction(transaction).load(requestId),
|
|
250
370
|
this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId })
|
|
@@ -254,8 +374,26 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
254
374
|
throw new BadRequestError('Maximum amount of allowed files reached.');
|
|
255
375
|
}
|
|
256
376
|
const file = await this.createDocumentFile(content, originalFileName, transaction);
|
|
257
|
-
return this.documentRequestFileService
|
|
377
|
+
return await this.documentRequestFileService
|
|
378
|
+
.withTransaction(transaction)
|
|
379
|
+
.insert({
|
|
380
|
+
requestId,
|
|
381
|
+
fileId: file.id,
|
|
382
|
+
title,
|
|
383
|
+
subtitle,
|
|
384
|
+
pages: null,
|
|
385
|
+
date,
|
|
386
|
+
summary,
|
|
387
|
+
tags,
|
|
388
|
+
createdDocumentId: null,
|
|
389
|
+
approval: null,
|
|
390
|
+
approvalComment: null,
|
|
391
|
+
approvalTimestamp: null,
|
|
392
|
+
metadata
|
|
393
|
+
});
|
|
258
394
|
});
|
|
395
|
+
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id }), this.logger);
|
|
396
|
+
return documentRequestFile;
|
|
259
397
|
}
|
|
260
398
|
async createDocumentRequest(parameters, transaction) {
|
|
261
399
|
if (parameters.collectionIds.length == 0) {
|
|
@@ -268,13 +406,36 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
268
406
|
return request;
|
|
269
407
|
});
|
|
270
408
|
}
|
|
409
|
+
async createDocumentRequestAssignmentTask({ originalFileName, collectionIds }, content) {
|
|
410
|
+
if (collectionIds.length == 0) {
|
|
411
|
+
throw new BadRequestError('No target collectionId specified.');
|
|
412
|
+
}
|
|
413
|
+
const file = await this.createDocumentFile(content, originalFileName);
|
|
414
|
+
return this.documentRequestAssignmentTaskService.transaction(async (_, transaction) => {
|
|
415
|
+
const task = await this.documentRequestAssignmentTaskService.withTransaction(transaction).insert({
|
|
416
|
+
fileId: file.id,
|
|
417
|
+
assignedRequestFileId: null,
|
|
418
|
+
typeId: null,
|
|
419
|
+
title: null,
|
|
420
|
+
subtitle: null,
|
|
421
|
+
pages: null,
|
|
422
|
+
date: null,
|
|
423
|
+
summary: null,
|
|
424
|
+
tags: null,
|
|
425
|
+
assignmentTries: 0
|
|
426
|
+
});
|
|
427
|
+
const newTaksCollections = collectionIds.map((collectionId) => ({ requestAssignmentTaskId: task.id, collectionId }));
|
|
428
|
+
await this.documentRequestAssignmentTaskCollectionService.withTransaction(transaction).insertMany(newTaksCollections);
|
|
429
|
+
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id }), this.logger);
|
|
430
|
+
return task;
|
|
431
|
+
});
|
|
432
|
+
}
|
|
271
433
|
async updateDocument(parameters, transaction) {
|
|
272
434
|
const { id: documentId, properties: propertyUpdates, ...update } = parameters;
|
|
273
435
|
await this.documentService.useTransaction(transaction, async (repository, tx) => {
|
|
274
436
|
await repository.update(documentId, update);
|
|
275
437
|
if (isDefined(propertyUpdates)) {
|
|
276
|
-
|
|
277
|
-
await this.setDocumentProperties(mappedPropertyUpdates, tx);
|
|
438
|
+
await this.setPropertyValues({ documentId, properties: propertyUpdates }, tx);
|
|
278
439
|
}
|
|
279
440
|
});
|
|
280
441
|
}
|
|
@@ -297,19 +458,41 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
297
458
|
}
|
|
298
459
|
await this.documentRequestService.withOptionalTransaction(transaction).delete(id, metadata);
|
|
299
460
|
}
|
|
300
|
-
async
|
|
301
|
-
if ((
|
|
461
|
+
async setPropertyValues({ documentId, requestFileId, requestAssignmentTaskId, properties: items }, transaction) {
|
|
462
|
+
if ((items.length == 0)) {
|
|
302
463
|
return;
|
|
303
464
|
}
|
|
465
|
+
if ((Number(isDefined(documentId)) + Number(isDefined(requestFileId)) + Number(isDefined(requestAssignmentTaskId))) != 1) {
|
|
466
|
+
throw new BadRequestError('Exactly one of documentId, requestFileId or requestAssignmentTaskId must be specified.');
|
|
467
|
+
}
|
|
304
468
|
await this.documentPropertyService.useTransaction(transaction, async (_, tx) => {
|
|
305
|
-
const propertyIds =
|
|
469
|
+
const propertyIds = items.map((property) => property.propertyId);
|
|
306
470
|
const properties = await this.documentPropertyService.withTransaction(tx).loadManyByQuery({ id: { $in: propertyIds } });
|
|
307
471
|
const propertiesMap = getEntityMap(properties);
|
|
308
|
-
|
|
472
|
+
const propertyItems = items.map(({ propertyId, value, metadata }) => {
|
|
309
473
|
const property = assertDefinedPass(propertiesMap.get(propertyId));
|
|
310
474
|
validatePropertyValue(propertyId, property.dataType, value);
|
|
311
|
-
|
|
312
|
-
|
|
475
|
+
return {
|
|
476
|
+
propertyId,
|
|
477
|
+
text: (property.dataType == 'text') ? assertStringPass(value) : null,
|
|
478
|
+
integer: (property.dataType == 'integer') ? assertNumberPass(value) : null,
|
|
479
|
+
decimal: (property.dataType == 'decimal') ? assertNumberPass(value) : null,
|
|
480
|
+
boolean: (property.dataType == 'boolean') ? assertBooleanPass(value) : null,
|
|
481
|
+
date: (property.dataType == 'date') ? assertNumberPass(value) : null,
|
|
482
|
+
metadata
|
|
483
|
+
};
|
|
484
|
+
});
|
|
485
|
+
if (isDefined(documentId)) {
|
|
486
|
+
await this.documentPropertyValueService.withTransaction(tx)
|
|
487
|
+
.upsertMany(['documentId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, documentId })));
|
|
488
|
+
}
|
|
489
|
+
else if (isDefined(requestFileId)) {
|
|
490
|
+
await this.documentRequestFilePropertyValueService.withTransaction(tx)
|
|
491
|
+
.upsertMany(['requestFileId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestFileId })));
|
|
492
|
+
}
|
|
493
|
+
else if (isDefined(requestAssignmentTaskId)) {
|
|
494
|
+
await this.documentRequestAssignmentTaskPropertyValueService.withTransaction(tx)
|
|
495
|
+
.upsertMany(['requestAssignmentTaskId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestAssignmentTaskId })));
|
|
313
496
|
}
|
|
314
497
|
});
|
|
315
498
|
}
|
|
@@ -317,7 +500,7 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
317
500
|
await this.documentCollectionDocumentService.withOptionalTransaction(transaction).upsert(['collectionId', 'documentId'], { ...parameters, archiveTimestamp: null });
|
|
318
501
|
}
|
|
319
502
|
async archiveDocument({ collectionId, documentId, metadata }) {
|
|
320
|
-
await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp:
|
|
503
|
+
await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
321
504
|
}
|
|
322
505
|
async createProperty(parameters) {
|
|
323
506
|
return this.documentPropertyService.insert(parameters);
|
|
@@ -325,19 +508,282 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
325
508
|
async assignPropertyToType(parameters) {
|
|
326
509
|
await this.documentTypePropertyService.insert(parameters);
|
|
327
510
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
511
|
+
async enrichDocument(documentId) {
|
|
512
|
+
const document = await this.documentService.load(documentId);
|
|
513
|
+
const { properties, ...extractionResult } = await this.extractFileInformation(document.fileId, document.typeId);
|
|
514
|
+
await this.documentService.transaction(async (documentService, transaction) => {
|
|
515
|
+
await documentService.update(document.id, extractionResult);
|
|
516
|
+
await this.setPropertyValues({ documentId, properties: properties }, transaction);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
async enrichDocumentRequestFile(requestFileId) {
|
|
520
|
+
const requestFile = await this.documentRequestFileService.load(requestFileId);
|
|
521
|
+
const request = await this.documentRequestService.load(requestFile.requestId);
|
|
522
|
+
const { properties, typeId: _, ...extractionResult } = await this.extractFileInformation(requestFile.fileId, request.typeId);
|
|
523
|
+
await this.documentRequestFileService.transaction(async (documentRequestFileService, transaction) => {
|
|
524
|
+
await documentRequestFileService.update(requestFile.id, extractionResult);
|
|
525
|
+
await this.setPropertyValues({ requestFileId, properties: properties }, transaction);
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
async enrichDocumentRequestAssignmentTask(requestAssignmentTaskId) {
|
|
529
|
+
const task = await this.documentRequestAssignmentTaskService.load(requestAssignmentTaskId);
|
|
530
|
+
const { properties, ...extractionResult } = await this.extractFileInformation(task.fileId);
|
|
531
|
+
await this.documentRequestAssignmentTaskService.transaction(async (documentRequestAssignmentTaskService, transaction) => {
|
|
532
|
+
await documentRequestAssignmentTaskService.update(task.id, extractionResult);
|
|
533
|
+
await this.setPropertyValues({ requestAssignmentTaskId, properties: properties }, transaction);
|
|
534
|
+
});
|
|
535
|
+
void tryIgnoreLogAsync(async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId }), this.logger);
|
|
536
|
+
}
|
|
537
|
+
async assignDocumentRequest(requestAssignmentTaskId) {
|
|
538
|
+
const session = this.documentRequestAssignmentTaskCollectionService.session;
|
|
539
|
+
const requestAssignmentTaskProperties = session.$with('requestAssignmentTaskProperty').as(session
|
|
540
|
+
.select({
|
|
541
|
+
requestAssignmentTaskId: documentRequestAssignmentTaskPropertyValue.requestAssignmentTaskId,
|
|
542
|
+
propertyId: documentRequestAssignmentTaskPropertyValue.propertyId,
|
|
543
|
+
label: documentProperty.label,
|
|
544
|
+
value: coalesce(toJsonb(documentRequestAssignmentTaskPropertyValue.text), toJsonb(documentRequestAssignmentTaskPropertyValue.integer), toJsonb(documentRequestAssignmentTaskPropertyValue.decimal), toJsonb(documentRequestAssignmentTaskPropertyValue.boolean), toJsonb(documentRequestAssignmentTaskPropertyValue.date)).as('value')
|
|
545
|
+
})
|
|
546
|
+
.from(documentRequestAssignmentTaskPropertyValue)
|
|
547
|
+
.innerJoin(documentProperty, eq(documentProperty.id, documentRequestAssignmentTaskPropertyValue.propertyId)));
|
|
548
|
+
const [task] = await session
|
|
549
|
+
.with(requestAssignmentTaskProperties)
|
|
550
|
+
.select({
|
|
551
|
+
fileId: documentRequestAssignmentTask.fileId,
|
|
552
|
+
collectionIds: arrayAgg(documentRequestAssignmentTaskCollection.collectionId),
|
|
553
|
+
typeCategory: documentCategory.label,
|
|
554
|
+
typeGroup: documentType.group,
|
|
555
|
+
typeLabel: documentType.label,
|
|
556
|
+
title: documentRequestAssignmentTask.title,
|
|
557
|
+
subtitle: documentRequestAssignmentTask.subtitle,
|
|
558
|
+
pages: documentRequestAssignmentTask.pages,
|
|
559
|
+
date: documentRequestAssignmentTask.date,
|
|
560
|
+
summary: documentRequestAssignmentTask.summary,
|
|
561
|
+
tags: documentRequestAssignmentTask.tags,
|
|
562
|
+
assignmentTries: documentRequestAssignmentTask.assignmentTries,
|
|
563
|
+
properties: sql `coalesce(${jsonAgg(requestAssignmentTaskProperties)} FILTER (WHERE ${requestAssignmentTaskProperties.propertyId} IS NOT NULL), '[]'::json)`
|
|
564
|
+
})
|
|
565
|
+
.from(documentRequestAssignmentTask)
|
|
566
|
+
.innerJoin(documentRequestAssignmentTaskCollection, eq(documentRequestAssignmentTaskCollection.requestAssignmentTaskId, documentRequestAssignmentTask.id))
|
|
567
|
+
.innerJoin(documentType, eq(documentType.id, documentRequestAssignmentTask.typeId))
|
|
568
|
+
.innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
569
|
+
.leftJoin(requestAssignmentTaskProperties, eq(requestAssignmentTaskProperties.requestAssignmentTaskId, documentRequestAssignmentTask.id))
|
|
570
|
+
.where(eq(documentRequestAssignmentTask.id, requestAssignmentTaskId))
|
|
571
|
+
.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);
|
|
572
|
+
assertDefined(task);
|
|
573
|
+
if (task.assignmentTries >= 3) {
|
|
574
|
+
this.logger.error(`Ignoring assignment task "${requestAssignmentTaskId}" because max tries are reached.`);
|
|
575
|
+
return;
|
|
340
576
|
}
|
|
577
|
+
const requestsWithoutFiles = await session
|
|
578
|
+
.select({
|
|
579
|
+
id: documentRequest.id,
|
|
580
|
+
collectionIds: arrayAgg(documentRequestCollection.collectionId),
|
|
581
|
+
documentCategory: documentCategory.label,
|
|
582
|
+
documentGroup: documentType.group,
|
|
583
|
+
documentType: documentType.label,
|
|
584
|
+
comment: documentRequest.comment
|
|
585
|
+
})
|
|
586
|
+
.from(documentRequest)
|
|
587
|
+
.innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequest.id))
|
|
588
|
+
.innerJoin(documentType, eq(documentType.id, documentRequest.typeId))
|
|
589
|
+
.innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
590
|
+
.where(and(inArray(documentRequestCollection.collectionId, task.collectionIds), notExists(session
|
|
591
|
+
.select()
|
|
592
|
+
.from(documentRequestFile)
|
|
593
|
+
.where(and(eq(documentRequestFile.requestId, documentRequest.id), ne(documentRequestFile.approval, false))))))
|
|
594
|
+
.groupBy(documentRequest.id, documentCategory.label, documentType.group, documentType.label, documentRequest.comment);
|
|
595
|
+
const requestsCollectionIds = distinct(requestsWithoutFiles.flatMap((request) => request.collectionIds));
|
|
596
|
+
const collectionNamesMap = await this.resolveNamesMap(...requestsCollectionIds);
|
|
597
|
+
const requests = requestsWithoutFiles.map((request) => ({
|
|
598
|
+
id: request.id,
|
|
599
|
+
collections: request.collectionIds.map((collectionId) => assertDefinedPass(collectionNamesMap[collectionId])),
|
|
600
|
+
documentCategory: request.documentCategory,
|
|
601
|
+
documentGroup: request.documentGroup ?? undefined,
|
|
602
|
+
documentType: request.documentType,
|
|
603
|
+
comment: request.comment ?? undefined
|
|
604
|
+
}));
|
|
605
|
+
const propertyEntries = task.properties.map((property) => [property.label, assertNotNullPass(property.value)]);
|
|
606
|
+
const context = {
|
|
607
|
+
file: {
|
|
608
|
+
documentCategory: task.typeCategory,
|
|
609
|
+
documentGroup: task.typeGroup ?? undefined,
|
|
610
|
+
documentType: task.typeLabel,
|
|
611
|
+
properties: fromEntries(propertyEntries)
|
|
612
|
+
},
|
|
613
|
+
requests
|
|
614
|
+
};
|
|
615
|
+
const result = await this.#aiService.generate({
|
|
616
|
+
...defaultGenerationOptions,
|
|
617
|
+
generationOptions: {
|
|
618
|
+
maxOutputTokens: 100,
|
|
619
|
+
temperature: 0,
|
|
620
|
+
topP: 0.2
|
|
621
|
+
},
|
|
622
|
+
generationSchema: object({ requestId: nullable(string()) }),
|
|
623
|
+
contents: [{
|
|
624
|
+
role: 'user',
|
|
625
|
+
parts: [
|
|
626
|
+
{
|
|
627
|
+
text: `<context>
|
|
628
|
+
${JSON.stringify(context, null, 2)}
|
|
629
|
+
</context>
|
|
630
|
+
|
|
631
|
+
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.`
|
|
632
|
+
}
|
|
633
|
+
]
|
|
634
|
+
}]
|
|
635
|
+
});
|
|
636
|
+
await this.documentRequestAssignmentTaskService.update(requestAssignmentTaskId, { assignmentTries: sql `${documentRequestAssignmentTask.assignmentTries} + 1` });
|
|
637
|
+
if (isNull(result.json.requestId)) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
641
|
+
const requestFile = await this.documentRequestFileService.withTransaction(transaction).insert({
|
|
642
|
+
requestId: result.json.requestId,
|
|
643
|
+
fileId: task.fileId,
|
|
644
|
+
title: task.title,
|
|
645
|
+
subtitle: task.subtitle,
|
|
646
|
+
pages: task.pages,
|
|
647
|
+
date: task.date,
|
|
648
|
+
summary: task.summary,
|
|
649
|
+
tags: task.tags,
|
|
650
|
+
createdDocumentId: null,
|
|
651
|
+
approval: null,
|
|
652
|
+
approvalComment: null,
|
|
653
|
+
approvalTimestamp: null
|
|
654
|
+
});
|
|
655
|
+
await this.setPropertyValues({ requestFileId: requestFile.id, properties: task.properties }, transaction);
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
async extractFileInformation(fileId, assumeTypeId) {
|
|
659
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
660
|
+
try {
|
|
661
|
+
const file = await this.documentFileService.load(fileId);
|
|
662
|
+
const fileContentStream = this.getDocumentFileContentStream(fileId);
|
|
663
|
+
const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
|
|
664
|
+
const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
|
|
665
|
+
const pages = file.mimeType.includes('pdf')
|
|
666
|
+
? await tryIgnoreLogAsync(async () => getPdfPageCount(tmpFile.path), this.logger, null)
|
|
667
|
+
: null;
|
|
668
|
+
const types = await this.documentTypeService.session
|
|
669
|
+
.select({
|
|
670
|
+
typeId: documentType.id,
|
|
671
|
+
categoryLabel: documentCategory.label,
|
|
672
|
+
typeGroup: documentType.group,
|
|
673
|
+
typeLabel: documentType.label
|
|
674
|
+
})
|
|
675
|
+
.from(documentType)
|
|
676
|
+
.leftJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
677
|
+
.where(isString(assumeTypeId) ? eq(documentType.id, assumeTypeId) : undefined);
|
|
678
|
+
const typeLabelEntries = types.map((type) => ({ id: type.typeId, label: `${type.categoryLabel} | ${type.typeGroup} | ${type.typeLabel}` }));
|
|
679
|
+
const typeLabels = typeLabelEntries.map(({ label }) => label);
|
|
680
|
+
const documentTypeGeneration = await this.#aiService.generate({
|
|
681
|
+
...defaultGenerationOptions,
|
|
682
|
+
generationSchema: object({
|
|
683
|
+
documentType: enumeration(typeLabels)
|
|
684
|
+
}),
|
|
685
|
+
contents: [
|
|
686
|
+
{
|
|
687
|
+
role: 'user',
|
|
688
|
+
parts: [
|
|
689
|
+
{ file: filePart.file },
|
|
690
|
+
{ text: `Klassifiziere den Inhalt des Dokuments in das angegebenen JSON Schema.` }
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
]
|
|
694
|
+
});
|
|
695
|
+
const typeId = typeLabelEntries.find((entry) => entry.label == documentTypeGeneration.json.documentType)?.id;
|
|
696
|
+
const typeProperties = isDefined(typeId) ? await this.documentTypePropertyService.loadManyByQuery({ typeId }) : undefined;
|
|
697
|
+
const propertyIds = typeProperties?.map((property) => property.propertyId);
|
|
698
|
+
const properties = isDefined(propertyIds) ? await this.documentPropertyService.loadManyByQuery({ id: { $in: propertyIds } }) : undefined;
|
|
699
|
+
const propertiesSchemaEntries = properties?.map((property) => {
|
|
700
|
+
const schema = match(property.dataType)
|
|
701
|
+
.with('text', () => nullable(string()))
|
|
702
|
+
.with('integer', () => nullable(integer()))
|
|
703
|
+
.with('decimal', () => nullable(number()))
|
|
704
|
+
.with('boolean', () => nullable(boolean()))
|
|
705
|
+
.with('date', () => nullable(object({ year: integer(), month: integer(), day: integer() })))
|
|
706
|
+
.exhaustive();
|
|
707
|
+
return [property.label, schema];
|
|
708
|
+
});
|
|
709
|
+
const generationSchema = object({
|
|
710
|
+
documentTitle: string(),
|
|
711
|
+
documentSubtitle: nullable(string()),
|
|
712
|
+
documentSummary: string(),
|
|
713
|
+
documentTags: array(string()),
|
|
714
|
+
documentDate: nullable(object({ year: integer(), month: integer(), day: integer() })),
|
|
715
|
+
...((isUndefined(propertiesSchemaEntries) || (propertiesSchemaEntries.length == 0))
|
|
716
|
+
? {}
|
|
717
|
+
: { documentProperties: object(fromEntries(propertiesSchemaEntries)) })
|
|
718
|
+
});
|
|
719
|
+
const { json: extraction } = await this.#aiService.generate({
|
|
720
|
+
model: 'gemini-2.0-flash',
|
|
721
|
+
generationOptions: {
|
|
722
|
+
maxOutputTokens: 2048,
|
|
723
|
+
temperature: 0.2,
|
|
724
|
+
topP: 0.2
|
|
725
|
+
},
|
|
726
|
+
generationSchema,
|
|
727
|
+
contents: [
|
|
728
|
+
{
|
|
729
|
+
role: 'user',
|
|
730
|
+
parts: [
|
|
731
|
+
{ file: filePart.file },
|
|
732
|
+
{
|
|
733
|
+
text: `Extrahiere den Inhalt des Dokuments in das angegebenen JSON Schema.
|
|
734
|
+
|
|
735
|
+
Gib in der summary ausführlich an, welche Informationen in dem Dokument vorkommen(ohne konkrete Werte).
|
|
736
|
+
Erstelle bis zu 7 möglichst spezifische Tags.
|
|
737
|
+
Antworte auf deutsch.`
|
|
738
|
+
}
|
|
739
|
+
]
|
|
740
|
+
}
|
|
741
|
+
]
|
|
742
|
+
});
|
|
743
|
+
const filteredDocumentTags = extraction.documentTags.filter((tag) => (tag != extraction.documentTitle) && (tag != extraction.documentSubtitle));
|
|
744
|
+
const date = isNotNull(extraction.documentDate) ? dateObjectToNumericDate(extraction.documentDate) : null;
|
|
745
|
+
const parsedProperties = isUndefined(extraction.documentProperties)
|
|
746
|
+
? []
|
|
747
|
+
: objectEntries(extraction.documentProperties)
|
|
748
|
+
.map(([propertyLabel, rawValue]) => {
|
|
749
|
+
if (isNull(rawValue)) {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
const property = assertDefinedPass(properties?.find((property) => property.label == propertyLabel));
|
|
753
|
+
const value = match(rawValue)
|
|
754
|
+
.with({ year: P.number }, (val) => dateObjectToNumericDate(val))
|
|
755
|
+
.otherwise((val) => val);
|
|
756
|
+
return { propertyId: property.id, dataType: property.dataType, value };
|
|
757
|
+
})
|
|
758
|
+
.filter(isNotNull);
|
|
759
|
+
return {
|
|
760
|
+
typeId: typeId ?? null,
|
|
761
|
+
title: extraction.documentTitle,
|
|
762
|
+
subtitle: extraction.documentSubtitle,
|
|
763
|
+
pages,
|
|
764
|
+
date,
|
|
765
|
+
summary: extraction.documentSummary,
|
|
766
|
+
tags: filteredDocumentTags,
|
|
767
|
+
properties: parsedProperties
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
catch (e_1) {
|
|
771
|
+
env_1.error = e_1;
|
|
772
|
+
env_1.hasError = true;
|
|
773
|
+
}
|
|
774
|
+
finally {
|
|
775
|
+
const result_1 = __disposeResources(env_1);
|
|
776
|
+
if (result_1)
|
|
777
|
+
await result_1;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
async getDocumentFileContent(fileId) {
|
|
781
|
+
const key = getDocumentFileKey(fileId);
|
|
782
|
+
return this.fileObjectStorage.getContent(key);
|
|
783
|
+
}
|
|
784
|
+
getDocumentFileContentStream(fileId) {
|
|
785
|
+
const key = getDocumentFileKey(fileId);
|
|
786
|
+
return this.fileObjectStorage.getContentStream(key);
|
|
341
787
|
}
|
|
342
788
|
async getDocumentFileContentObjectUrl(title, file, download) {
|
|
343
789
|
const key = getDocumentFileKey(file.id);
|
|
@@ -346,11 +792,11 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
346
792
|
const filename = `${title}.${fileExtension}`;
|
|
347
793
|
return this.fileObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
|
|
348
794
|
'Response-Content-Type': file.mimeType,
|
|
349
|
-
'Response-Content-Disposition': `${disposition}; filename="${encodeURIComponent(filename)}"`
|
|
795
|
+
'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`
|
|
350
796
|
});
|
|
351
797
|
}
|
|
352
798
|
};
|
|
353
|
-
DocumentManagementService = __decorate([
|
|
799
|
+
DocumentManagementService = DocumentManagementService_1 = __decorate([
|
|
354
800
|
Singleton({
|
|
355
801
|
providers: [
|
|
356
802
|
provide(EntityRepositoryConfig, { useValue: { schema: 'document_management' } }),
|
|
@@ -360,13 +806,14 @@ DocumentManagementService = __decorate([
|
|
|
360
806
|
], DocumentManagementService);
|
|
361
807
|
export { DocumentManagementService };
|
|
362
808
|
function getDocumentFileKey(id) {
|
|
363
|
-
return `${id.slice(0, 2)}/${id.slice(0, 4)}/${id}`;
|
|
809
|
+
return `${id.slice(0, 2)} /${id.slice(0, 4)}/${id} `;
|
|
364
810
|
}
|
|
365
811
|
const validators = {
|
|
366
812
|
[DocumentPropertyDataType.Text]: (value) => isString(value) || isNull(value),
|
|
367
813
|
[DocumentPropertyDataType.Integer]: (value) => isNumber(value) || isNull(value),
|
|
368
814
|
[DocumentPropertyDataType.Decimal]: (value) => isNumber(value) || isNull(value),
|
|
369
|
-
[DocumentPropertyDataType.Boolean]: (value) => isBoolean(value) || isNull(value)
|
|
815
|
+
[DocumentPropertyDataType.Boolean]: (value) => isBoolean(value) || isNull(value),
|
|
816
|
+
[DocumentPropertyDataType.Date]: (value) => isNumber(value) || isNull(value)
|
|
370
817
|
};
|
|
371
818
|
function validatePropertyValue(propertyId, dataType, value) {
|
|
372
819
|
const valid = validators[dataType](value);
|