malac-utils 1.3.3__tar.gz → 1.4.1a74__tar.gz
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.
- {malac_utils-1.3.3/malac_utils.egg-info → malac_utils-1.4.1a74}/PKG-INFO +9 -12
- malac_utils-1.4.1a74/README.md +16 -0
- malac_utils-1.4.1a74/malac/utils/__init__.py +2 -0
- malac_utils-1.4.1a74/malac/utils/date.py +61 -0
- malac_utils-1.4.1a74/malac/utils/dotnet/malac-utils.dll +0 -0
- malac_utils-1.4.1a74/malac/utils/exceptions.py +10 -0
- {malac_utils-1.3.3 → malac_utils-1.4.1a74}/malac/utils/fhirpath.py +19 -7
- malac_utils-1.4.1a74/malac/utils/html.py +176 -0
- malac_utils-1.4.1a74/malac/utils/java/malac-utils.jar +0 -0
- malac_utils-1.4.1a74/malac/utils/js/malac-utils.js +15845 -0
- {malac_utils-1.3.3 → malac_utils-1.4.1a74/malac_utils.egg-info}/PKG-INFO +9 -12
- {malac_utils-1.3.3 → malac_utils-1.4.1a74}/malac_utils.egg-info/SOURCES.txt +7 -1
- malac_utils-1.4.1a74/malac_utils.egg-info/requires.txt +3 -0
- {malac_utils-1.3.3 → malac_utils-1.4.1a74}/pyproject.toml +17 -3
- malac_utils-1.4.1a74/tests/test_date.py +68 -0
- malac_utils-1.3.3/README.md +0 -20
- malac_utils-1.3.3/malac/utils/__init__.py +0 -2
- malac_utils-1.3.3/malac/utils/date.py +0 -27
- malac_utils-1.3.3/malac_utils.egg-info/requires.txt +0 -1
- {malac_utils-1.3.3 → malac_utils-1.4.1a74}/LICENSE +0 -0
- {malac_utils-1.3.3 → malac_utils-1.4.1a74}/malac_utils.egg-info/dependency_links.txt +0 -0
- {malac_utils-1.3.3 → malac_utils-1.4.1a74}/malac_utils.egg-info/top_level.txt +0 -0
- {malac_utils-1.3.3 → malac_utils-1.4.1a74}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: malac-utils
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1a74
|
|
4
4
|
Summary: Mapping Language Compiler Utils
|
|
5
5
|
Author-email: cdeHealth-Team <contact-project+cdehealth-malac-hd-52276676-issue-@incoming.gitlab.com>
|
|
6
6
|
Project-URL: Homepage, https://gitlab.com/cdehealth/malac-hd
|
|
@@ -23,7 +23,8 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest<10,>=9; extra == "test"
|
|
27
28
|
Dynamic: license-file
|
|
28
29
|
|
|
29
30
|
# MaLaC Utils
|
|
@@ -32,17 +33,13 @@ MaLaC-HD (MApping LAnguage Compiler for Health Data) is a tool that you can use
|
|
|
32
33
|
|
|
33
34
|
This is the utils package to execute generated mappings. You can use it independently from the `malac-hd` package, together with the appropriate model packages.
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
It contains the Python modules mappings compiled to Python use (`malac.utils`), and ships the runtimes of mappings compiled to JavaScript, Java and C#, which malac-hd runs them with:
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
- `malac/utils/js/malac-utils.js`, the JavaScript runtime as one self-contained file for Node.js and web browsers (also published to npm as [`malac-utils`](https://www.npmjs.com/package/malac-utils)),
|
|
39
|
+
- `malac/utils/java/malac-utils.jar`, the Java runtime as one self-contained jar (also published to Maven Central as `io.gitlab.cdehealth:malac-utils`),
|
|
40
|
+
- `malac/utils/dotnet/malac-utils.dll`, the .NET runtime (also published to NuGet as [`Malac.Utils`](https://www.nuget.org/packages/Malac.Utils)).
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) to make participation in our community a harassment free experience for everyone.
|
|
41
|
-
|
|
42
|
-
## Authors and acknowledgment
|
|
43
|
-
We want to thank
|
|
44
|
-
- [ELGA GmbH](https://www.elga.gv.at/) with their [CDA2FHIR](https://collab.hl7.at/display/BAL/AG+ELGA+CDA+Laborbefund+zu+FHIR) projects and
|
|
45
|
-
- [AIT Austrian Institute of Technology GmbH](https://www.ait.ac.at/) with their [SmartFOX](https://www.smart-fox.at/) project.
|
|
42
|
+
See the [repository](https://gitlab.com/cdehealth/malac/utils) for their sources and how to build them.
|
|
46
43
|
|
|
47
44
|
## License
|
|
48
|
-
This is a LGPL licensed project.
|
|
45
|
+
This is a LGPL licensed project (LGPL-3.0-only).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# MaLaC Utils
|
|
2
|
+
|
|
3
|
+
MaLaC-HD (MApping LAnguage Compiler for Health Data) is a tool that you can use to convert mappings between different health data formats to executable code. It can also be used as a library to dynamically execute mappings.
|
|
4
|
+
|
|
5
|
+
This is the utils package to execute generated mappings. You can use it independently from the `malac-hd` package, together with the appropriate model packages.
|
|
6
|
+
|
|
7
|
+
It contains the Python modules mappings compiled to Python use (`malac.utils`), and ships the runtimes of mappings compiled to JavaScript, Java and C#, which malac-hd runs them with:
|
|
8
|
+
|
|
9
|
+
- `malac/utils/js/malac-utils.js`, the JavaScript runtime as one self-contained file for Node.js and web browsers (also published to npm as [`malac-utils`](https://www.npmjs.com/package/malac-utils)),
|
|
10
|
+
- `malac/utils/java/malac-utils.jar`, the Java runtime as one self-contained jar (also published to Maven Central as `io.gitlab.cdehealth:malac-utils`),
|
|
11
|
+
- `malac/utils/dotnet/malac-utils.dll`, the .NET runtime (also published to NuGet as [`Malac.Utils`](https://www.nuget.org/packages/Malac.Utils)).
|
|
12
|
+
|
|
13
|
+
See the [repository](https://gitlab.com/cdehealth/malac/utils) for their sources and how to build them.
|
|
14
|
+
|
|
15
|
+
## License
|
|
16
|
+
This is a LGPL licensed project (LGPL-3.0-only).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import datetime
|
|
3
|
+
|
|
4
|
+
# FHIR date, dateTime and instant as well as FHIRPath date and dateTime literals: YYYY, YYYY-MM
|
|
5
|
+
# or YYYY-MM-DD, followed by T and hh, hh:mm or hh:mm:ss with optional fractional seconds and an
|
|
6
|
+
# offset Z or +/-hh:mm up to 14:00, see https://hl7.org/fhir/datatypes.html#primitive and
|
|
7
|
+
# https://hl7.org/fhirpath/#datetime (also a space as separator, as str(datetime) gives)
|
|
8
|
+
FHIR_PATTERN = re.compile(r"^([0-9]{4})(?:-([0-9]{2})(?:-([0-9]{2}))?)?(?:[T ]([0-9]{2})(?::([0-9]{2})(?::([0-9]{2})(?:\.([0-9]+))?)?)?(Z|[+-][0-9]{2}:?[0-9]{2})?|T)?$")
|
|
9
|
+
|
|
10
|
+
# CDA TS: any portion from YYYY to YYYYMMDDhhmmss, fractional seconds only if precise
|
|
11
|
+
# to the second, and an optional offset +/-HHMM, see
|
|
12
|
+
# https://build.fhir.org/ig/HL7/CDA-core-2.0/StructureDefinition-ts-simple.html
|
|
13
|
+
TS_PATTERN = re.compile(r"^([0-9]{4})(?:([0-9]{2})(?:([0-9]{2})(?:([0-9]{2})(?:([0-9]{2})(?:([0-9]{2})(?:\.([0-9]+))?)?)?)?)?)?([+-][0-9]{1,4})?$")
|
|
14
|
+
|
|
15
|
+
# FHIR permits offsets from -14:00 to +14:00, which also bounds CDA offsets mapped to FHIR
|
|
16
|
+
MAX_OFFSET = datetime.timedelta(hours=14)
|
|
17
|
+
|
|
18
|
+
class PartialDate:
|
|
19
|
+
|
|
20
|
+
def __init__(self, value):
|
|
21
|
+
self.value = value
|
|
22
|
+
|
|
23
|
+
def isoformat(self):
|
|
24
|
+
return self.value
|
|
25
|
+
|
|
26
|
+
def __repr__(self):
|
|
27
|
+
return self.value
|
|
28
|
+
|
|
29
|
+
def _tzinfo(offset):
|
|
30
|
+
if not offset:
|
|
31
|
+
return None
|
|
32
|
+
if offset == "Z":
|
|
33
|
+
return datetime.timezone.utc
|
|
34
|
+
digits = offset[1:].replace(":", "")
|
|
35
|
+
hours, minutes = (digits, 0) if len(digits) <= 2 else (digits[:-2], digits[-2:])
|
|
36
|
+
delta = datetime.timedelta(hours=int(hours), minutes=int(minutes))
|
|
37
|
+
if int(minutes) > 59 or delta > MAX_OFFSET:
|
|
38
|
+
raise ValueError("Invalid offset: %s" % offset)
|
|
39
|
+
return datetime.timezone(-delta if offset[0] == "-" else delta)
|
|
40
|
+
|
|
41
|
+
def _datetime(year, month, day, hour, minute, second, fraction, offset):
|
|
42
|
+
tzinfo = _tzinfo(offset)
|
|
43
|
+
microsecond = int(fraction[:6].ljust(6, "0")) if fraction else 0 # datetime supports microseconds only
|
|
44
|
+
return datetime.datetime(int(year), int(month), int(day), int(hour or 0), int(minute or 0), int(second or 0), microsecond, tzinfo)
|
|
45
|
+
|
|
46
|
+
def parse(datestr):
|
|
47
|
+
datestr = getattr(datestr, "value", datestr).strip()
|
|
48
|
+
if (match := FHIR_PATTERN.match(datestr)) and (match.group(3) or not match.group(4)): # time only with a full date
|
|
49
|
+
year, month, day = match.groups()[:3]
|
|
50
|
+
if not day:
|
|
51
|
+
return PartialDate("-".join(part for part in (year, month) if part))
|
|
52
|
+
return _datetime(*match.groups())
|
|
53
|
+
elif match := TS_PATTERN.match(datestr):
|
|
54
|
+
year, month, day, hour = match.groups()[:4]
|
|
55
|
+
if not hour:
|
|
56
|
+
# date precision, an offset is not representable here but must still be valid
|
|
57
|
+
_tzinfo(match.group(8))
|
|
58
|
+
return PartialDate("-".join(part for part in (year, month, day) if part))
|
|
59
|
+
return _datetime(*match.groups())
|
|
60
|
+
else:
|
|
61
|
+
raise ValueError("Unknown date format: %s" % datestr)
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exception hierarchy for MaLaC-HD-generated code
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
class MappingExecutionError(Exception):
|
|
6
|
+
"""Base class for errors raised while executing a mapping."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CheckFailedError(MappingExecutionError):
|
|
10
|
+
"""Error in case a check clause failed."""
|
|
@@ -7,6 +7,7 @@ import math
|
|
|
7
7
|
from urllib.parse import urlparse
|
|
8
8
|
|
|
9
9
|
from . import date as dateutil
|
|
10
|
+
from . import html
|
|
10
11
|
|
|
11
12
|
time_units = ["year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds"]
|
|
12
13
|
|
|
@@ -100,6 +101,8 @@ class FHIRPathUtils(object):
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
def get(self, val, *attrs, **options):
|
|
104
|
+
if len(attrs) == 1 and options.get("strip", False):
|
|
105
|
+
return [html.strip_tags(val)]
|
|
103
106
|
attr = attrs[0]
|
|
104
107
|
try:
|
|
105
108
|
result = getattr(val, attr) or []
|
|
@@ -119,10 +122,6 @@ class FHIRPathUtils(object):
|
|
|
119
122
|
prefix = getattr(val, key)
|
|
120
123
|
if prefix and attr.lower() == (prefix + key[:-10]).lower():
|
|
121
124
|
result = getattr(val, key[:-10]) or []
|
|
122
|
-
if result != [] and len(attrs) == 1 and options.get("strip", False):
|
|
123
|
-
result = str(result).strip()
|
|
124
|
-
if result == "":
|
|
125
|
-
result = []
|
|
126
125
|
result = result if isinstance(result, list) else [result]
|
|
127
126
|
if len(attrs) == 1:
|
|
128
127
|
return result
|
|
@@ -369,7 +368,12 @@ class FHIRPathUtils(object):
|
|
|
369
368
|
unit = val_a if isinstance(val_a, self.model.Quantity) else val_b # TODO divide units (g / m -> g/m)
|
|
370
369
|
return [self.model.Quantity(value=self.model.decimal(value=v_a / v_b), unit=unit.unit, code=unit.code, system=unit.system)]
|
|
371
370
|
else:
|
|
372
|
-
|
|
371
|
+
# decimal division, also for integers (not float division)
|
|
372
|
+
if isinstance(val_a, int):
|
|
373
|
+
val_a = decimal.Decimal(val_a)
|
|
374
|
+
if isinstance(val_b, int):
|
|
375
|
+
val_b = decimal.Decimal(val_b)
|
|
376
|
+
return [round(val_a / val_b, 8)]
|
|
373
377
|
|
|
374
378
|
def div(self, val_a, val_b):
|
|
375
379
|
val_a = self.single_value(val_a)
|
|
@@ -563,6 +567,14 @@ class FHIRPathUtils(object):
|
|
|
563
567
|
else:
|
|
564
568
|
return [-1]
|
|
565
569
|
|
|
570
|
+
def lastIndexOf(self, val, other):
|
|
571
|
+
val = self.string_value(val)
|
|
572
|
+
other = self.string_value(self.single_value(other, str, self.model.string))
|
|
573
|
+
if val == [] or other == []:
|
|
574
|
+
return []
|
|
575
|
+
else:
|
|
576
|
+
return [val.rfind(other)]
|
|
577
|
+
|
|
566
578
|
def concat(self, val, other):
|
|
567
579
|
val = self.string_value(self.single_value(val))
|
|
568
580
|
other = self.string_value(self.single_value(other))
|
|
@@ -1106,13 +1118,13 @@ class FHIRPathUtils(object):
|
|
|
1106
1118
|
return [val]
|
|
1107
1119
|
elif isinstance(val, self.model.decimal):
|
|
1108
1120
|
return [decimal.Decimal(val.value)]
|
|
1121
|
+
elif isinstance(val, self.model.boolean) or isinstance(val, bool): # before int, which bool is a subclass of
|
|
1122
|
+
return [decimal.Decimal("1.0") if self.boolean_value(val) else decimal.Decimal("0.0")]
|
|
1109
1123
|
elif isinstance(val, self.model.integer) or isinstance(val, int):
|
|
1110
1124
|
if isinstance(val, self.model.integer):
|
|
1111
1125
|
return [decimal.Decimal(val.value)]
|
|
1112
1126
|
else:
|
|
1113
1127
|
return [decimal.Decimal(val)]
|
|
1114
|
-
elif isinstance(val, self.model.boolean) or isinstance(val, bool):
|
|
1115
|
-
return [decimal.Decimal("1.0") if self.boolean_value(val) else decimal.Decimal("0.0")]
|
|
1116
1128
|
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1117
1129
|
try:
|
|
1118
1130
|
return [decimal.Decimal(self.string_value(val))]
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import logging
|
|
3
|
+
from html import escape as html_escape
|
|
4
|
+
from html.parser import HTMLParser
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
def _attr(name, value):
|
|
9
|
+
return f' {name}="{html_escape(str(value))}"'
|
|
10
|
+
|
|
11
|
+
def _handle_tr(tr):
|
|
12
|
+
html = ""
|
|
13
|
+
attrs = ""
|
|
14
|
+
if tr.ID:
|
|
15
|
+
attrs += _attr("id", tr.ID)
|
|
16
|
+
if tr.styleCode:
|
|
17
|
+
attrs += _attr("class", tr.styleCode)
|
|
18
|
+
html += "<tr %s>" % attrs
|
|
19
|
+
for th in tr.th:
|
|
20
|
+
html += _handle_tdh(th, "th")
|
|
21
|
+
for td in tr.td:
|
|
22
|
+
html += _handle_tdh(td, "td")
|
|
23
|
+
html += "</tr>"
|
|
24
|
+
return html
|
|
25
|
+
|
|
26
|
+
def _handle_tdh(tdh, tag):
|
|
27
|
+
html = ""
|
|
28
|
+
attrs = ""
|
|
29
|
+
if tdh.ID:
|
|
30
|
+
attrs += _attr("id", tdh.ID)
|
|
31
|
+
if tdh.styleCode:
|
|
32
|
+
attrs += _attr("class", tdh.styleCode)
|
|
33
|
+
html += f"<{tag}%s>" % attrs
|
|
34
|
+
html += _handle_content(tdh)
|
|
35
|
+
html += f"</{tag}>"
|
|
36
|
+
return html
|
|
37
|
+
|
|
38
|
+
def _handle_content(elem):
|
|
39
|
+
html = ""
|
|
40
|
+
for content in elem.content_:
|
|
41
|
+
if content.category == 1:
|
|
42
|
+
html += html_escape(content.value)
|
|
43
|
+
elif content.category == 2:
|
|
44
|
+
if content.name == "br":
|
|
45
|
+
html += f'<br />'
|
|
46
|
+
else:
|
|
47
|
+
logger.warning("Skipping unhandled simple narrative content '%s'", content.name)
|
|
48
|
+
elif content.category == 3:
|
|
49
|
+
attrs = ""
|
|
50
|
+
if getattr(content.value, "ID", None):
|
|
51
|
+
attrs += _attr("id", content.value.ID)
|
|
52
|
+
if content.name == "table":
|
|
53
|
+
html += "<table%s>" % attrs
|
|
54
|
+
if content.value.thead:
|
|
55
|
+
html += "<thead>"
|
|
56
|
+
for tr in content.value.thead.tr:
|
|
57
|
+
html += _handle_tr(tr)
|
|
58
|
+
html += "</thead>"
|
|
59
|
+
if content.value.tfoot:
|
|
60
|
+
html += "<tfoot>"
|
|
61
|
+
for tr in content.value.tfoot.tr:
|
|
62
|
+
html += _handle_tr(tr)
|
|
63
|
+
html += "</tfoot>"
|
|
64
|
+
for tbody in content.value.tbody or [None]:
|
|
65
|
+
html += "<tbody>"
|
|
66
|
+
for tr in (tbody.tr if tbody else []):
|
|
67
|
+
html += _handle_tr(tr)
|
|
68
|
+
html += "</tbody>"
|
|
69
|
+
html += "</table>"
|
|
70
|
+
elif content.name == "content":
|
|
71
|
+
if content.value.styleCode == "Italics":
|
|
72
|
+
attrs += ' class="italics"'
|
|
73
|
+
elif content.value.styleCode:
|
|
74
|
+
attrs += _attr("class", content.value.styleCode)
|
|
75
|
+
html += "<span%s>" % attrs
|
|
76
|
+
html += _handle_content(content.value)
|
|
77
|
+
html += "</span>"
|
|
78
|
+
elif content.name == "paragraph":
|
|
79
|
+
if content.value.styleCode:
|
|
80
|
+
attrs += _attr("class", content.value.styleCode)
|
|
81
|
+
html += "<p%s>" % attrs
|
|
82
|
+
html += _handle_content(content.value)
|
|
83
|
+
html += "</p>"
|
|
84
|
+
elif content.name == "sup":
|
|
85
|
+
html += "<sup%s>" % attrs
|
|
86
|
+
html += _handle_content(content.value)
|
|
87
|
+
html += "</sup>"
|
|
88
|
+
elif content.name == "footnote":
|
|
89
|
+
html += '<span class="footnote"%s>' % attrs
|
|
90
|
+
html += _handle_content(content.value)
|
|
91
|
+
html += "</span>"
|
|
92
|
+
elif content.name == "renderMultiMedia":
|
|
93
|
+
html += '<img%s%s />' % (_attr("src", content.value.referencedObject), attrs)
|
|
94
|
+
elif content.name == "br":
|
|
95
|
+
html += f'<br />'
|
|
96
|
+
elif content.name == "caption":
|
|
97
|
+
html += "<caption%s>" % attrs
|
|
98
|
+
html += _handle_content(content.value)
|
|
99
|
+
html += "</caption>"
|
|
100
|
+
elif content.name == "list":
|
|
101
|
+
if content.value.listType in [None, "unordered"]:
|
|
102
|
+
if content.value.styleCode:
|
|
103
|
+
attrs += _attr("style", f"list-style-type:{content.value.styleCode.lower()}")
|
|
104
|
+
html += "<ul%s>" % attrs
|
|
105
|
+
for item in content.value.item:
|
|
106
|
+
html += "<li>"
|
|
107
|
+
html += _handle_content(item)
|
|
108
|
+
html += "</li>"
|
|
109
|
+
html += "</ul>"
|
|
110
|
+
elif content.value.listType == "ordered":
|
|
111
|
+
styleCodeTrans = {"LittleRoman": "i", "BigRoman": "I", "LittleAlpha": "a", "BigAlpha": "A", "Arabic": "1"}
|
|
112
|
+
listType = styleCodeTrans.get(content.value.styleCode)
|
|
113
|
+
if listType:
|
|
114
|
+
attrs += f' type="{listType}"'
|
|
115
|
+
html += "<ol%s>" % attrs
|
|
116
|
+
for item in content.value.item:
|
|
117
|
+
html += "<li>"
|
|
118
|
+
html += _handle_content(item)
|
|
119
|
+
html += "</li>"
|
|
120
|
+
html += "</ol>"
|
|
121
|
+
else:
|
|
122
|
+
html += _handle_content(content.value)
|
|
123
|
+
else:
|
|
124
|
+
logger.warning("Skipping unhandled narrative content category %s", content.category)
|
|
125
|
+
return html
|
|
126
|
+
|
|
127
|
+
def strucdoctext2html(module, strucdoctext):
|
|
128
|
+
html = _handle_content(strucdoctext)
|
|
129
|
+
div = module.Flow(content_=[module.MixedContainer(1, 0, "", html)])
|
|
130
|
+
div.original_tagname_ = "div"
|
|
131
|
+
div.valueOf_ = ""
|
|
132
|
+
return div
|
|
133
|
+
|
|
134
|
+
def ed2html(module, ed):
|
|
135
|
+
# TODO: improve
|
|
136
|
+
div = module.Flow(content_=[module.MixedContainer(1, 0, "", f"<v3:reference>{ed.reference.value}</v3:reference>")])
|
|
137
|
+
div.original_tagname_ = "div"
|
|
138
|
+
div.valueOf_ = ""
|
|
139
|
+
return div
|
|
140
|
+
|
|
141
|
+
def ed2markdown(module, ed):
|
|
142
|
+
# TODO: improve
|
|
143
|
+
return module.markdown(value=f"<v3:reference>{ed.reference.value}</v3:reference>")
|
|
144
|
+
|
|
145
|
+
def strucdoctext2markdown(module, strucdoctext):
|
|
146
|
+
# TODO: implement properly
|
|
147
|
+
return module.markdown(value=strip_tags(strucdoctext))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def strip_tags(val):
|
|
151
|
+
class MLStripper(HTMLParser):
|
|
152
|
+
def __init__(self):
|
|
153
|
+
super().__init__()
|
|
154
|
+
self.reset()
|
|
155
|
+
self.strict = False
|
|
156
|
+
self.convert_charrefs= True
|
|
157
|
+
self.text = io.StringIO()
|
|
158
|
+
def handle_data(self, d):
|
|
159
|
+
self.text.write(d)
|
|
160
|
+
self.text.write(" ")
|
|
161
|
+
def get_data(self):
|
|
162
|
+
return self.text.getvalue()
|
|
163
|
+
|
|
164
|
+
html = _handle_content(val)
|
|
165
|
+
s = MLStripper()
|
|
166
|
+
s.feed(html)
|
|
167
|
+
text = s.get_data()
|
|
168
|
+
text = " ".join(text.split())
|
|
169
|
+
return text
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def builddiv(module, content):
|
|
173
|
+
div = module.Flow(content_=[module.MixedContainer(1, 0, "", content)])
|
|
174
|
+
div.original_tagname_ = "div"
|
|
175
|
+
div.valueOf_ = ""
|
|
176
|
+
return div
|
|
Binary file
|