oots-lib 0.1.0__py3-none-any.whl

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.
oots_lib/Transport.py ADDED
@@ -0,0 +1,139 @@
1
+ import logging
2
+ from abc import ABC, abstractmethod
3
+ from typing import NoReturn
4
+
5
+ from XRoad import RedisCache, Transport, XClient
6
+ from XRoad.plugins import UXPHistoryPlugin
7
+
8
+ from oots_lib.import_env import import_env
9
+ from oots_lib.lib.exception import EDMException, TransportError
10
+ from oots_lib.lib.toLogger import ToLogger
11
+ from oots_lib.lib.UseRedis import get_redis_client
12
+
13
+ _logger = logging.getLogger(__name__)
14
+
15
+ TREMBITA_URL = import_env("TREMBITA_URL")
16
+ TREMBITA_CLIENT_ID = import_env("TREMBITA_CLIENT_ID")
17
+ TREMBITA_CACHE = int(import_env("TREMBITA_CACHE"))
18
+ REDIS_URL = import_env("REDIS_URL")
19
+
20
+
21
+ class SOAPTransport(ABC):
22
+ def __init__(self, service: str, conversation_id: str, if_send_error: bool = True):
23
+ self.service = service
24
+ self.conversation_id = conversation_id
25
+ self.if_send_error = if_send_error
26
+
27
+ self.to_logger = ToLogger(conversation_id)
28
+ self.redis = get_redis_client()
29
+
30
+ _logger.info(f"Надсилаємо запит до сервісу: {self.service}")
31
+
32
+ cache = RedisCache(REDIS_URL, timeout=3600 * 24)
33
+ transport = Transport(operation_timeout=60 * 5, cache=cache)
34
+ self.history = UXPHistoryPlugin()
35
+
36
+ self.client = None
37
+ try:
38
+ self.client = XClient(
39
+ TREMBITA_URL, client=TREMBITA_CLIENT_ID, service=self.service,
40
+ transport=transport,
41
+ plugins=[self.history, ]
42
+ )
43
+ except Exception as e:
44
+ _logger.exception("Помилка створення XClient")
45
+ self.send_error_message(
46
+ code="EDM:ERR:0006", # NOSONAR
47
+ message="Сталася помилка при створенні з'єднення до Трембіти",
48
+ detail=f"Сталася помилка при створенні з'єднення до Трембіти: {e}",
49
+ cause=e,
50
+ )
51
+
52
+ _logger.info(f"XClient успішно створений: {self.client}")
53
+
54
+ def send_error_message(
55
+ self,
56
+ code: str,
57
+ message: str,
58
+ detail: str,
59
+ cause: BaseException | None = None,
60
+ ) -> NoReturn:
61
+ """Сигналізує про помилку транспорту виключенням.
62
+
63
+ При `if_send_error` помилка додатково публікується до Redis та черги
64
+ через :class:`EDMException`, інакше кидається :class:`TransportError`.
65
+ Метод завжди кидає виключення, щоб помилка не залишалась непоміченою.
66
+ """
67
+ _logger.error(f"Помилка: code={code}, message={message}, detail={detail}")
68
+ if self.if_send_error:
69
+ raise EDMException(
70
+ redis=self.redis,
71
+ queue=None,
72
+ key=None,
73
+ message_id=str(self.conversation_id),
74
+ code=code,
75
+ message=message,
76
+ detail=detail,
77
+ ) from cause
78
+
79
+ raise TransportError(f"[{code}] {message}: {detail}") from cause
80
+
81
+ @abstractmethod
82
+ def parsing_response(self, responce: dict) -> list[dict]:
83
+ ...
84
+
85
+ def response(self, request: dict) -> list[dict]:
86
+
87
+ _logger.info(f"Запит до сервісу: {self.service}")
88
+ if self.client is None:
89
+ self.send_error_message(
90
+ code="EDM:ERR:0006",
91
+ message="З'єднання до Трембіти не встановлено",
92
+ detail=f"XClient для сервісу {self.service} не створений",
93
+ )
94
+
95
+ try:
96
+ response = self.client.request(**request)
97
+ except Exception as e:
98
+ _logger.exception("Помилка виконання запиту до Трембіти")
99
+ self.send_error_message(
100
+ code="EDM:ERR:0006",
101
+ message="Сталася помилка при виконанні запиту до Сервісу даних",
102
+ detail=f"Сталася помилка при виконанні запиту до Сервісу даних: {e}",
103
+ cause=e,
104
+ )
105
+
106
+ try:
107
+ result = response["body"]
108
+ except (KeyError, IndexError, TypeError) as e:
109
+ self.send_error_message(
110
+ code="EDM:ERR:0006",
111
+ message="Відповідь від Сервісу даних має некоректну структуру",
112
+ detail=f"Не вдалося отримати body.GetDocumentsByPersonResult: {e}",
113
+ cause=e,
114
+ )
115
+
116
+ self._logging_trembita_transaction()
117
+
118
+ _logger.debug(f"Результат відповіді: {result}")
119
+ return self.parsing_response(result)
120
+
121
+ def _logging_trembita_transaction(self):
122
+ if self.to_logger is None or self.client is None or self.history is None:
123
+ _logger.debug("Журналювання транзакції пропущено: відсутні client/history або логер")
124
+ return
125
+
126
+ self.to_logger.append_calls(
127
+ {
128
+ "dataservice": self.service,
129
+ "timestamp": self.history.transaction_date.isoformat(),
130
+ "trembita_msg_id": self.client.id,
131
+ "transaction_id": self.history.transaction_id,
132
+ }
133
+ )
134
+
135
+ _logger.debug(f"Дані журналу транзакції: {self.to_logger.payload}")
136
+ try:
137
+ self.to_logger.send_to_logger()
138
+ except Exception:
139
+ _logger.exception("Не вдалося надіслати журнал транзакції")
oots_lib/__init__.py ADDED
@@ -0,0 +1,77 @@
1
+ """Public API for the OOTS evidence library."""
2
+
3
+ from importlib import import_module
4
+
5
+ __all__ = [
6
+ "Base",
7
+ "MainBase",
8
+ "NS",
9
+ "SOAPTransport",
10
+ "EDMException",
11
+ "BaseEDMException",
12
+ "AuthenticationException",
13
+ "AuthorizationException",
14
+ "InvalidRequestException",
15
+ "ObjectNotFoundException",
16
+ "QueryException",
17
+ "TimeoutException",
18
+ "UnresolvedReferenceException",
19
+ "UnsupportedCapabilityException",
20
+ "MakeEvidence",
21
+ "UseRedisAsync",
22
+ "get_redis_client",
23
+ "initialize_redis",
24
+ "close_redis",
25
+ "generate_pdf_from_xslt",
26
+ "Identifier",
27
+ "Person",
28
+ "EDMRequest",
29
+ "Evidences",
30
+ "Description",
31
+ "Classification",
32
+ "RepositoryItemRef",
33
+ "ExtrinsicObjectType",
34
+ "RegistryPackageType",
35
+ ]
36
+
37
+ _LAZY_IMPORTS = {
38
+ "Base": ("oots_lib.models.Base", "Base"),
39
+ "MainBase": ("oots_lib.models.Base", "MainBase"),
40
+ "NS": ("oots_lib.lib.NS", "NS"),
41
+ "SOAPTransport": ("oots_lib.Transport", "SOAPTransport"),
42
+ "EDMException": ("oots_lib.lib.exception", "EDMException"),
43
+ "BaseEDMException": ("oots_lib.lib.exceptions", "EDMException"),
44
+ "AuthenticationException": ("oots_lib.lib.exceptions", "AuthenticationException"),
45
+ "AuthorizationException": ("oots_lib.lib.exceptions", "AuthorizationException"),
46
+ "InvalidRequestException": ("oots_lib.lib.exceptions", "InvalidRequestException"),
47
+ "ObjectNotFoundException": ("oots_lib.lib.exceptions", "ObjectNotFoundException"),
48
+ "QueryException": ("oots_lib.lib.exceptions", "QueryException"),
49
+ "TimeoutException": ("oots_lib.lib.exceptions", "TimeoutException"),
50
+ "UnresolvedReferenceException": ("oots_lib.lib.exceptions", "UnresolvedReferenceException"),
51
+ "UnsupportedCapabilityException": ("oots_lib.lib.exceptions", "UnsupportedCapabilityException"),
52
+ "MakeEvidence": ("oots_lib.lib.MakeEvidence", "MakeEvidence"),
53
+ "UseRedisAsync": ("oots_lib.lib.UseRedis", "UseRedisAsync"),
54
+ "get_redis_client": ("oots_lib.lib.UseRedis", "get_redis_client"),
55
+ "initialize_redis": ("oots_lib.lib.UseRedis", "initialize_redis"),
56
+ "close_redis": ("oots_lib.lib.UseRedis", "close_redis"),
57
+ "generate_pdf_from_xslt": ("oots_lib.lib.CreatePDF", "generate_pdf_from_xslt"),
58
+ "Identifier": ("oots_lib.models.Person", "Identifier"),
59
+ "Person": ("oots_lib.models.Person", "Person"),
60
+ "EDMRequest": ("oots_lib.models.RequestEDM", "EDMRequest"),
61
+ "Evidences": ("oots_lib.models.ResponseEvidences", "Evidences"),
62
+ "Description": ("oots_lib.models.ResponseEvidences", "Description"),
63
+ "Classification": ("oots_lib.models.ResponseEvidences", "Classification"),
64
+ "RepositoryItemRef": ("oots_lib.models.ResponseEvidences", "RepositoryItemRef"),
65
+ "ExtrinsicObjectType": ("oots_lib.models.ResponseEvidences", "ExtrinsicObjectType"),
66
+ "RegistryPackageType": ("oots_lib.models.ResponseEvidences", "RegistryPackageType"),
67
+ }
68
+
69
+
70
+ def __getattr__(name: str):
71
+ if name not in _LAZY_IMPORTS:
72
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
73
+ module_name, attr_name = _LAZY_IMPORTS[name]
74
+ module = import_module(module_name)
75
+ value = getattr(module, attr_name)
76
+ globals()[name] = value
77
+ return value
oots_lib/import_env.py ADDED
@@ -0,0 +1,12 @@
1
+ import logging
2
+ import os
3
+
4
+ _logger = logging.getLogger(__name__)
5
+
6
+
7
+ def import_env(key: str, default: str | None = None) -> str:
8
+ value = os.getenv(key, default)
9
+ if value is None:
10
+ _logger.error("Environment variable %s is not set", key)
11
+ raise ValueError(f"Environment variable {key} is not set")
12
+ return value
@@ -0,0 +1,83 @@
1
+ import logging
2
+ import pathlib
3
+ import re
4
+ from collections.abc import Iterable
5
+
6
+ from lxml import etree
7
+
8
+ from oots_lib.lib.xml_safety import safe_fromstring, safe_parse
9
+
10
+ try:
11
+ from weasyprint import CSS, HTML # type: ignore
12
+ except ModuleNotFoundError: # pragma: no cover - optional dependency
13
+ CSS = None
14
+ HTML = None
15
+
16
+
17
+ _logger = logging.getLogger(__name__)
18
+
19
+ path = pathlib.Path(__file__).parent
20
+ XSLT_FILE = path / "rdf-display.xsl"
21
+ BIRTH_CERTIFICATE_XSLT_FILE = path / "annexI_birth.xsl"
22
+ MARRIAGE_CERTIFICATE_XSLT_FILE = path / "annexIV_marriage.xsl"
23
+ DISABILITY_CERTIFICATE_XSLT_FILE = path / "disability.xsl"
24
+
25
+
26
+ def generate_pdf_from_xslt(
27
+ rdf: str | bytes,
28
+ xslt_file: pathlib.Path | str = XSLT_FILE,
29
+ css: Iterable[str] | None = None,
30
+ ) -> bytes:
31
+ """
32
+ Виконує XSLT-трансформацію XML у HTML, а потім перетворює HTML у PDF.
33
+ """
34
+ if HTML is None or CSS is None:
35
+ raise ModuleNotFoundError(
36
+ "weasyprint is required for PDF generation. "
37
+ "Install the optional dependency to use generate_pdf_from_xslt()."
38
+ )
39
+
40
+ # Крок 1: Завантаження та виконання XSLT-трансформації
41
+
42
+ # Конвертація xslt_file у pathlib.Path якщо потрібно
43
+ if isinstance(xslt_file, str):
44
+ xslt_file = pathlib.Path(xslt_file)
45
+
46
+ # Завантаження XML-документа
47
+ if isinstance(rdf, bytes):
48
+ rdf = rdf.decode("utf-8")
49
+
50
+ xml = re.sub(r"<\?xml [^>]*\?>\s*", '', rdf, count=1)
51
+ xml = xml.strip()
52
+ xml_tree = safe_fromstring(xml)
53
+
54
+ # Завантаження XSLT-таблиці стилів
55
+ xslt_root = safe_parse(xslt_file)
56
+ transform = etree.XSLT(
57
+ xslt_root,
58
+ access_control=etree.XSLTAccessControl.DENY_ALL, # type: ignore[attr-defined]
59
+ )
60
+
61
+ # Виконання трансформації: XML -> HTML (як etree.ElementTree)
62
+ html_tree = transform(xml_tree)
63
+
64
+ # Серіалізація HTML у рядок
65
+ html_content = etree.tostring(
66
+ html_tree,
67
+ pretty_print=True,
68
+ method="html",
69
+ encoding='utf-8'
70
+ ).decode('utf-8')
71
+
72
+ # Крок 2: Перетворення HTML у PDF за допомогою WeasyPrint
73
+
74
+ html_object = HTML(string=html_content)
75
+
76
+ stylesheets = [CSS(string='@page { size: A4; margin: 1.5cm; }')]
77
+ for c in (css or []):
78
+ stylesheets.append(CSS(string=c))
79
+
80
+ # Виклик write_pdf() без аргументу target повертає PDF як bytes.
81
+ pdf_bytes: bytes = html_object.write_pdf(stylesheets=stylesheets) # type: ignore[assignment]
82
+
83
+ return pdf_bytes
@@ -0,0 +1,92 @@
1
+ import logging
2
+ import uuid
3
+
4
+ from lxml import etree
5
+
6
+ from oots_lib.lib.NS import NS
7
+
8
+ _logger = logging.getLogger(__name__)
9
+
10
+ __all__ = [
11
+ "EMetadata",
12
+ "IssuingAuthority",
13
+ "IsConformantTo",
14
+ "Distribution",
15
+ "IsAbout",
16
+ ]
17
+
18
+ class EMetadata(NS):
19
+ def __init__(self):
20
+ super().__init__()
21
+ self._xml: etree._Element = self._element("sdg", "Evidence")
22
+ self._subelement(self._xml, "sdg", "Identifier", text=uuid.uuid4())
23
+
24
+ def issuingAuthority(self, issuingAuthority: etree._Element): # NOSONAR
25
+ self._xml.append(issuingAuthority)
26
+
27
+ def isConformeant(self, isConformeant: etree._Element): # NOSONAR
28
+ self._xml.append(isConformeant)
29
+
30
+ def distribution(self, distribution: etree._Element):
31
+ self._xml.append(distribution)
32
+
33
+ def isAbout(self, isAbout: etree._Element): # NOSONAR
34
+ self._xml.append(isAbout)
35
+
36
+
37
+ class IssuingAuthority(NS):
38
+ def __init__(self, shema, value):
39
+ super().__init__()
40
+ self._xml: etree._Element = self._element("sdg", "IssuingAuthority")
41
+ self._subelement(
42
+ self._xml, "sdg", "Identifier", text=value, attrib={"schemeID": shema}
43
+ )
44
+
45
+ def name(self, lang: str, name: str):
46
+ self._subelement(
47
+ self._xml, "sdg", "Name", text=name, attrib={"lang": f"{lang}".upper()}
48
+ )
49
+ return self
50
+
51
+
52
+ class Distribution(NS):
53
+ def __init__(self, content_type):
54
+ super().__init__()
55
+ self._xml: etree._Element = self._element("sdg", "Distribution")
56
+ self._subelement(self._xml, "sdg", "Format", text=content_type)
57
+
58
+ def ConformsTo(self, url):
59
+ self._subelement(self._xml, "sdg", "ConformsTo", text=url)
60
+
61
+ def Transformation(self, url):
62
+ self._subelement(self._xml, "sdg", "Transformation", text=url)
63
+
64
+
65
+ class IsConformantTo(NS):
66
+ def __init__(self, conformance):
67
+ super().__init__()
68
+ self._xml: etree._Element = self._element("sdg", "IsConformantTo")
69
+ self._subelement(
70
+ self._xml, "sdg", "EvidenceTypeClassification", text=conformance
71
+ )
72
+
73
+ def title(self, lang, title):
74
+ self._subelement(
75
+ self._xml, "sdg", "Title", text=title, attrib={"lang": lang.upper()}
76
+ )
77
+
78
+ def description(self, lang, description):
79
+ self._subelement(
80
+ self._xml,
81
+ "sdg",
82
+ "Description",
83
+ text=description,
84
+ attrib={"lang": lang.upper()},
85
+ )
86
+
87
+
88
+ class IsAbout(NS):
89
+ def __init__(self, person: etree._Element):
90
+ super().__init__()
91
+ self._xml: etree._Element = self._element("sdg", "IsAbout")
92
+ self._xml.append(person)
@@ -0,0 +1,282 @@
1
+ import base64
2
+ import logging
3
+ from dataclasses import is_dataclass
4
+
5
+ from pyRegRep4 import deep_get
6
+ from pyRegRep4.RIMParsing import Parsing
7
+
8
+ from oots_lib.import_env import import_env
9
+ from oots_lib.lib.EvidanceMetadata import (
10
+ Distribution,
11
+ EMetadata,
12
+ IsAbout,
13
+ IsConformantTo,
14
+ IssuingAuthority,
15
+ )
16
+ from oots_lib.lib.exception import EDMException
17
+ from oots_lib.lib.toLogger import ToLogger
18
+ from oots_lib.lib.UseRedis import UseRedisAsync as Redis
19
+ from oots_lib.models.Base import MainBase
20
+ from oots_lib.models.Person import Person, get_person_from_redis
21
+ from oots_lib.models.RequestEDM import get_edm_request_from_redis
22
+ from oots_lib.models.ResponseEvidences import (
23
+ Classification,
24
+ Evidences,
25
+ ExtrinsicObjectType,
26
+ RegistryPackageType,
27
+ RepositoryItemRef,
28
+ save_evidences_to_redis,
29
+ )
30
+ from oots_lib.redis_keys import Keys
31
+
32
+ _logger = logging.getLogger(__name__)
33
+
34
+ KEYS = Keys()
35
+ QUEUE_OUTCOMING = import_env("QUEUE_OUTCOMING")
36
+ IF_PREVIEW: bool = import_env("IF_PREVIEW").lower() == 'true'
37
+
38
+
39
+ class MakeEvidence:
40
+ ISSUING_AUTHORITY_ID = ""
41
+ ISSUING_AUTHORITY_SCHEME = ""
42
+ ISSUING_AUTHORITY_NAME = ""
43
+ CONFORMANT_TO_URL = ''
44
+
45
+ def __init__(self, message_id: str, redis: Redis):
46
+ super().__init__()
47
+
48
+ self.message_id = message_id
49
+ self.redis = redis
50
+
51
+ self.person: Person | None = None
52
+ self.request: Parsing | None = None
53
+ self.as4: dict | None | None = None
54
+
55
+ self.log: ToLogger | None | None = None
56
+ self.evidence: Evidences | None = None
57
+ self._request_content_type: str | None = None
58
+
59
+ self.request_to_service: dict | None = None
60
+
61
+ self.response_from_service = None
62
+ self.data: MainBase | None = None
63
+ self.title = ""
64
+ self.description: list[str] = []
65
+
66
+ def _edm_exception(self, code: str, message: str, detail: str) -> EDMException:
67
+ """Створює EDMException з контекстом цього повідомлення."""
68
+ return EDMException(
69
+ redis=self.redis,
70
+ queue=None,
71
+ key=None,
72
+ message_id=str(self.message_id),
73
+ code=code,
74
+ message=message,
75
+ detail=detail,
76
+ )
77
+
78
+ @property
79
+ def if_preview(self):
80
+ _logger.debug(f"Перевірка прапора preview для повідомлення {self.message_id}")
81
+
82
+ if self.request is None:
83
+ raise ValueError(
84
+ f"Запит для повідомлення {self.message_id} не зчитаний: спершу викличте read_data()"
85
+ )
86
+
87
+ request: dict = self.request.serialize()
88
+ preview: bool = deep_get(request, 'doc', 'PossibilityForPreview', default=False)
89
+
90
+ _logger.debug(f"Значення прапора preview у запиті: {preview}")
91
+ _logger.debug(f"Прапор preview встановлено, використовуємо глобальне налаштування IF_PREVIEW: {IF_PREVIEW}")
92
+
93
+ pr = False if not preview and not IF_PREVIEW else True
94
+
95
+ _logger.debug(f"Результат перевірки прапора preview для повідомлення {self.message_id}: {pr}")
96
+ return pr
97
+
98
+ async def read_data(self):
99
+ """Зчитує вхідні дані повідомлення з Redis.
100
+
101
+ Raises:
102
+ EDMException: Якщо запит EDM або дані особи відсутні у Redis
103
+ """
104
+ request_key = KEYS.get_request_edm(self.message_id)
105
+ request = await get_edm_request_from_redis(self.redis, request_key)
106
+ if request is None:
107
+ raise self._not_found(
108
+ message="Запит EDM не знайдено",
109
+ detail=f"У Redis відсутній запит EDM за ключем {request_key}",
110
+ )
111
+ self.request = Parsing(request.content)
112
+
113
+ person_key = KEYS.get_request_person(self.message_id)
114
+ self.person = await get_person_from_redis(self.redis, person_key)
115
+ if self.person is None:
116
+ raise self._not_found(
117
+ message="Інформацію про людину не знайдено",
118
+ detail=f"У Redis відсутні дані особи за ключем {person_key}",
119
+ )
120
+
121
+ self.as4 = await self.redis.get_from_redis(KEYS.get_request_as4(self.message_id))
122
+
123
+ def _not_found(self, message: str, detail: str) -> EDMException:
124
+ return self._edm_exception(
125
+ code="EDM:ERR:0004", message=message, detail=detail
126
+ )
127
+
128
+ async def load_data_to_redis(self):
129
+ """
130
+ Зберігає сформовані докази до Redis у форматі Evidences.
131
+
132
+ Після збереження перевіряє preview-прапор через get_flag()
133
+ і за потреби відправляє повідомлення на обробку.
134
+ """
135
+ _logger.info(f"Збереження набору доказів для повідомлення {self.message_id}")
136
+
137
+ if is_dataclass(self.evidence):
138
+ await save_evidences_to_redis(self.redis, KEYS.get_response_evidence(self.message_id), self.evidence)
139
+ else:
140
+ raise TypeError(f"Очікувався Evidences, отримано {type(self.evidence)}")
141
+
142
+ # Перевіряємо, чи потрібно відправляти на обробку
143
+ if not self.if_preview:
144
+ _logger.info(f"Відправляємо повідомлення {self.message_id} до черги обробки")
145
+ await self.redis.push_to_queue(QUEUE_OUTCOMING, f"{self.message_id}")
146
+ else:
147
+ _logger.debug(f"Повідомлення {self.message_id} потребує preview, обробка відкладена")
148
+
149
+ def generate_metadata(self, main_evidence: bool = True):
150
+ if self.person is None:
151
+ raise ValueError(
152
+ f"Дані особи для повідомлення {self.message_id} не зчитані: спершу викличте read_data()"
153
+ )
154
+ person_tree = self.person.xml_tree
155
+ if person_tree is None:
156
+ raise ValueError(
157
+ f"XML особи для повідомлення {self.message_id} не сформовано"
158
+ )
159
+
160
+ distribution = Distribution(self.request_content_type)
161
+ conformantTo = IsConformantTo(self.CONFORMANT_TO_URL) # NOSONAR
162
+ usingAuthority = IssuingAuthority(self.ISSUING_AUTHORITY_SCHEME, self.ISSUING_AUTHORITY_ID) # NOSONAR
163
+ usingAuthority.name(lang='UA', name=self.ISSUING_AUTHORITY_NAME)
164
+ about = IsAbout(person_tree)
165
+
166
+ metadata = EMetadata()
167
+
168
+ if main_evidence:
169
+ about_tree = about.xml_tree
170
+ conformant_tree = conformantTo.xml_tree
171
+ distribution_tree = distribution.xml_tree
172
+ authority_tree = usingAuthority.xml_tree
173
+ assert about_tree is not None
174
+ assert conformant_tree is not None
175
+ assert distribution_tree is not None
176
+ assert authority_tree is not None
177
+ metadata.isAbout(about_tree)
178
+ metadata.isConformeant(conformant_tree)
179
+ metadata.distribution(distribution_tree)
180
+ metadata.issuingAuthority(authority_tree)
181
+ else:
182
+ ...
183
+
184
+ return metadata.xml_string
185
+
186
+ @property
187
+ def request_content_type(self):
188
+ if self.request is None:
189
+ raise ValueError("Запит не ініціалізовано")
190
+ if self._request_content_type is None:
191
+ self._request_content_type = deep_get(
192
+ self.request.serialize(any_type=True),
193
+ 'query',
194
+ 'EvidenceRequest',
195
+ 'sdg:DataServiceEvidenceType',
196
+ 'sdg:DistributedAs',
197
+ 'sdg:Format'
198
+ )
199
+ return self._request_content_type
200
+
201
+ async def transform_data(self):
202
+
203
+ if self.data is None:
204
+ raise ValueError(
205
+ f"Джерело даних для повідомлення {self.message_id} не встановлено: "
206
+ "задайте self.data перед transform_data()"
207
+ )
208
+
209
+ try:
210
+ documents = await self.data.generate_data()
211
+ except Exception as e:
212
+ raise self._not_found(
213
+ message="Інформацію про людину не знайдено",
214
+ detail=str(e),
215
+ ) from e
216
+
217
+ content_type = self.request_content_type
218
+ _logger.info(f"Тип контенту: {content_type}")
219
+
220
+ if not content_type:
221
+ _logger.error("Тип контенту у запиті не вказаний")
222
+ raise self._edm_exception(
223
+ code="EDM:ERR:0003",
224
+ message="Тип контенту у запиті не вказаний",
225
+ detail="У запиті відсутній sdg:DistributedAs/sdg:Format",
226
+ )
227
+
228
+ evidences = []
229
+
230
+ for doc in documents:
231
+
232
+ contents = []
233
+ extrinsic = ExtrinsicObjectType(
234
+ classification=Classification(classificationNode='MainEvidence'),
235
+ EvidenceMetadata=self.generate_metadata(main_evidence=True),
236
+ RepositoryItemRef=RepositoryItemRef(title="Cerificate of Marriage"),
237
+ )
238
+
239
+ if 'pdf' in content_type:
240
+ _logger.debug("Перетворення документу у PDF")
241
+ pdf = doc.get_pdf()
242
+ extrinsic.content_type = "application/pdf"
243
+ extrinsic.encoding = 'base64'
244
+ extrinsic.content = base64.b64encode(pdf).decode("utf-8")
245
+ contents.append(extrinsic)
246
+
247
+ elif 'xml' in content_type:
248
+ _logger.debug("Перетворення документу у XML")
249
+ extrinsic.content_type = "application/xml"
250
+ extrinsic.content = doc.get_xml()
251
+ contents.append(extrinsic)
252
+
253
+ elif 'json' in content_type:
254
+ _logger.debug("Перетворення документу у JSON")
255
+ extrinsic.content_type = "application/json"
256
+ extrinsic.content = doc.get_json()
257
+ contents.append(extrinsic)
258
+
259
+ else:
260
+ _logger.error(f"Невідомий тип контенту: {content_type}")
261
+ raise self._edm_exception(
262
+ code="EDM:ERR:0006",
263
+ message="Невідомий тип контенту",
264
+ detail=f"Невідомий тип контенту: {content_type}",
265
+ )
266
+ _logger.debug(f"Створено ExtrinsicObjectType: {extrinsic}")
267
+ # додати інші представлення до contents
268
+ ...
269
+ _logger.debug('Додали всі представлення доказу')
270
+
271
+ _logger.debug(f"Створюємо RegistryPackageType для документа: {doc}")
272
+ evidences.append(RegistryPackageType(RegistryPackage=contents))
273
+
274
+ _logger.debug(f"Створено всі RegistryPackageType для документів, кількість: {len(evidences)}")
275
+ evidence = Evidences(
276
+ title=self.title,
277
+ PreviewDescription=self.description,
278
+ preview=self.if_preview,
279
+ evidences=evidences,
280
+ )
281
+ _logger.debug(f'Створили Evidence: {evidence}')
282
+ self.evidence = evidence