From 318d44f659250f8fcf23ae5c3e3bda28ed2a343d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Such=C3=A1nek?= Date: Mon, 23 Sep 2024 10:22:49 +0200 Subject: [PATCH] feat(docworker): Add file question --- .../dsw/document_worker/consts.py | 2 +- .../dsw/document_worker/model/context.py | 95 ++++++++++++++++++- packages/dsw-tdk/dsw/tdk/consts.py | 2 +- packages/dsw-tdk/dsw/tdk/core.py | 1 + 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/packages/dsw-document-worker/dsw/document_worker/consts.py b/packages/dsw-document-worker/dsw/document_worker/consts.py index 77636319..10c98998 100644 --- a/packages/dsw-document-worker/dsw/document_worker/consts.py +++ b/packages/dsw-document-worker/dsw/document_worker/consts.py @@ -1,7 +1,7 @@ CMD_CHANNEL = 'doc_worker' CMD_COMPONENT = 'doc_worker' COMPONENT_NAME = 'Document Worker' -CURRENT_METAMODEL = 14 +CURRENT_METAMODEL = 14 # TODO: raise for 4.12 to 15 DEFAULT_ENCODING = 'utf-8' EXIT_SUCCESS = 0 NULL_UUID = '00000000-0000-0000-0000-000000000000' diff --git a/packages/dsw-document-worker/dsw/document_worker/model/context.py b/packages/dsw-document-worker/dsw/document_worker/model/context.py index 43c46159..af2d8725 100644 --- a/packages/dsw-document-worker/dsw/document_worker/model/context.py +++ b/packages/dsw-document-worker/dsw/document_worker/model/context.py @@ -629,6 +629,31 @@ def load(path: str, data: dict, **options): ) +class FileReply(Reply): + + def __init__(self, path, created_at, created_by, file_uuid): + super().__init__(path, created_at, created_by, 'FileReply') + self.file_uuid = file_uuid # type: str + self.file = None # type: Optional[QuestionnaireFile] + + @property + def value(self) -> str: + return self.file_uuid + + def _resolve_links(self, ctx): + super()._resolve_links_parent(ctx) + self.file = ctx.questionnaire.files.get(self.file_uuid, None) + + @staticmethod + def load(path: str, data: dict, **options): + return ItemSelectReply( + path=path, + created_at=_datetime(data['createdAt']), + created_by=SimpleAuthor.load(data['createdBy'], **options), + item_uuid=data['value']['value'], + ) + + class Answer: def __init__(self, uuid, label, advice, metric_measures, followup_uuids, @@ -1001,6 +1026,36 @@ def load(data: dict, **options): ) +class FileQuestion(Question): + + def __init__(self, uuid, title, text, tag_uuids, reference_uuids, + expert_uuids, required_phase_uuid, max_size, file_types, + annotations): + super().__init__(uuid, 'FileQuestion', title, text, tag_uuids, + reference_uuids, expert_uuids, required_phase_uuid, + annotations) + self.max_size = max_size + self.file_types = file_types + + def _resolve_links(self, ctx): + super()._resolve_links_parent(ctx) + + @staticmethod + def load(data: dict, **options): + return FileQuestion( + uuid=data['uuid'], + title=data['title'], + text=data['text'], + tag_uuids=data['tagUuids'], + reference_uuids=data['referenceUuids'], + expert_uuids=data['expertUuids'], + required_phase_uuid=data['requiredPhaseUuid'], + max_size=data['maxSize'], + file_types=data['fileTypes'], + annotations=_load_annotations(data['annotations']), + ) + + class Chapter: def __init__(self, uuid, title, text, question_uuids, annotations): @@ -1053,6 +1108,8 @@ def _load_question(data: dict, **options): return IntegrationQuestion.load(data, **options) if data['questionType'] == 'ItemSelectQuestion': return ItemSelectQuestion.load(data, **options) + if data['questionType'] == 'FileQuestion': + return FileQuestion.load(data, **options) raise ValueError(f'Unknown question type: {data["questionType"]}') @@ -1087,6 +1144,8 @@ def _load_reply(path: str, data: dict, **options): return IntegrationReply.load(path, data, **options) if data['value']['type'] == 'ItemSelectReply': return ItemSelectReply.load(path, data, **options) + if data['value']['type'] == 'FileReply': + return FileReply.load(path, data, **options) raise ValueError(f'Unknown reply type: {data["value"]["type"]}') @@ -1318,6 +1377,33 @@ def items(self) -> ItemsView[str, Reply]: return self.replies.items() +class QuestionnaireFile: + + def __init__(self, uuid, file_name, file_size, content_type): + self.uuid = uuid # type: str + self.name = file_name # type: str + self.size = file_size # type: int + self.content_type = content_type # type: str + + self.replies = [] # type: list[FileReply] + self.download_url = '' # type: str + + def _resolve_links(self, ctx): + # Update the download URL: /projects//files//download + project_uuid = ctx.questionnaire.uuid + client_url = ctx.config.client_url + self.download_url = f'{client_url}/projects/{project_uuid}/files/{self.uuid}/download' + + @staticmethod + def load(data: dict, questionnaire_uuid: str, **options): + return QuestionnaireFile( + uuid=data['uuid'], + file_name=data['fileName'], + file_size=data['fileSize'], + content_type=data['contentType'], + ) + + class Questionnaire: def __init__(self, uuid, name, description, created_by, phase_uuid, @@ -1327,6 +1413,7 @@ def __init__(self, uuid, name, description, created_by, phase_uuid, self.description = description # type: str self.version = None # type: Optional[QuestionnaireVersion] self.versions = list() # type: list[QuestionnaireVersion] + self.files = dict() # type: dict[str, QuestionnaireFile] self.todos = list() # type: list[str] self.created_by = created_by # type: User self.phase_uuid = phase_uuid # type: Optional[str] @@ -1342,16 +1429,19 @@ def _resolve_links(self, ctx): @staticmethod def load(data: dict, **options): + questionnaire_uuid = data['uuid'] versions = [QuestionnaireVersion.load(d, **options) for d in data['versions']] version = None replies = {p: _load_reply(p, d, **options) for p, d in data['replies'].items()} + files = {d['uuid']: QuestionnaireFile.load(d, questionnaire_uuid, **options) + for d in data.get('files', [])} for v in versions: if v.uuid == data['versionUuid']: version = v qtn = Questionnaire( - uuid=data['uuid'], + uuid=questionnaire_uuid, name=data['name'], description=data['description'] or '', created_by=User.load(data['createdBy'], **options), @@ -1361,6 +1451,7 @@ def load(data: dict, **options): ) qtn.version = version qtn.versions = versions + qtn.files = files qtn.project_tags = data.get('projectTags', []) qtn.replies.replies = replies qtn.todos = [k for k, v in data.get('labels', {}).items() if TODO_LABEL_UUID in v] @@ -1670,7 +1761,7 @@ def load(data: dict, **options): class DocumentContext: """Document Context smart representation""" - METAMODEL_VERSION = 14 + METAMODEL_VERSION = 15 def __init__(self, ctx, **options): self.metamodel_version = int(ctx.get('metamodelVersion', '0')) diff --git a/packages/dsw-tdk/dsw/tdk/consts.py b/packages/dsw-tdk/dsw/tdk/consts.py index 905465e9..4cdf7f00 100644 --- a/packages/dsw-tdk/dsw/tdk/consts.py +++ b/packages/dsw-tdk/dsw/tdk/consts.py @@ -4,7 +4,7 @@ APP = 'dsw-tdk' VERSION = '4.10.6' -METAMODEL_VERSION = 14 +METAMODEL_VERSION = 15 REGEX_SEMVER = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+$') REGEX_ORGANIZATION_ID = re.compile(r'^(?![.])(?!.*[.]$)[a-zA-Z0-9.]+$') diff --git a/packages/dsw-tdk/dsw/tdk/core.py b/packages/dsw-tdk/dsw/tdk/core.py index 140b6a62..420e5424 100644 --- a/packages/dsw-tdk/dsw/tdk/core.py +++ b/packages/dsw-tdk/dsw/tdk/core.py @@ -49,6 +49,7 @@ def __init__(self, message: str, hint: str): 12: (4, 1, 0), 13: (4, 3, 0), 14: (4, 10, 0), + 15: (4, 11, 0), }