|
1
|
|
|
""" |
|
2
|
|
|
This module contains a HTMLExportWord Model |
|
3
|
|
|
""" |
|
4
|
|
|
|
|
5
|
|
|
from __future__ import annotations |
|
6
|
|
|
|
|
7
|
|
|
from itertools import groupby |
|
8
|
|
|
from typing import Iterator |
|
9
|
|
|
|
|
10
|
|
|
from loglan_core import Word, Definition |
|
11
|
|
|
from loglan_core.addons.exporter import ExportWordConverter |
|
12
|
|
|
|
|
13
|
|
|
from app.site.compose import DEFAULT_HTML_STYLE, Item |
|
14
|
|
|
from app.site.compose.definition_formatter import DefinitionFormatter |
|
15
|
|
|
|
|
16
|
|
|
|
|
17
|
|
|
class Meaning(Item): |
|
18
|
|
|
def __init__(self, word: Word, style: str = DEFAULT_HTML_STYLE): |
|
19
|
|
|
self.word = word |
|
20
|
|
|
self.style = style |
|
21
|
|
|
self._ewc = ExportWordConverter(word) |
|
22
|
|
|
|
|
23
|
|
|
def export_definition_for_loglan(self, d: Definition) -> str: |
|
24
|
|
|
""" |
|
25
|
|
|
style: |
|
26
|
|
|
:return: |
|
27
|
|
|
""" |
|
28
|
|
|
tags = { |
|
29
|
|
|
# usage, gram, body, tags, definition |
|
30
|
|
|
"normal": [ |
|
31
|
|
|
'<span class="du">%s</span> ', |
|
32
|
|
|
'<span class="dg">(%s)</span> ', |
|
33
|
|
|
'<span class="db">%s</span>', |
|
34
|
|
|
' <span class="dt">[%s]</span>', |
|
35
|
|
|
f'<div class="definition log" id={d.id}>%s</div>', |
|
36
|
|
|
], |
|
37
|
|
|
"ultra": [ |
|
38
|
|
|
"<du>%s</du> ", |
|
39
|
|
|
"(%s) ", |
|
40
|
|
|
"%s", |
|
41
|
|
|
" [%s]", |
|
42
|
|
|
"<d>%s</d>", |
|
43
|
|
|
], |
|
44
|
|
|
} |
|
45
|
|
|
t_d_usage, t_d_gram, t_d_body, t_d_tags, t_definition = tags[self.style] |
|
46
|
|
|
|
|
47
|
|
|
def_usage = t_d_usage % d.usage.replace("%", "—") if d.usage else "" |
|
48
|
|
|
gram_form = f"{d.slots or ''}" + d.grammar_code |
|
49
|
|
|
def_gram = t_d_gram % gram_form if gram_form else "" |
|
50
|
|
|
def_body = t_d_body % DefinitionFormatter(d).body_formatted |
|
51
|
|
|
def_tags = ( |
|
52
|
|
|
t_d_tags % d.case_tags.replace("-", "‍-‍") if d.case_tags else "" |
|
53
|
|
|
) |
|
54
|
|
|
return t_definition % f"{def_usage}{def_gram}{def_body}{def_tags}" |
|
55
|
|
|
|
|
56
|
|
|
def html_origin(self): |
|
57
|
|
|
""" |
|
58
|
|
|
Returns: |
|
59
|
|
|
""" |
|
60
|
|
|
orig = self.word.origin |
|
61
|
|
|
orig_x = self.word.origin_x |
|
62
|
|
|
|
|
63
|
|
|
if not (orig or orig_x): |
|
64
|
|
|
return str() |
|
65
|
|
|
|
|
66
|
|
|
origin = self._compose_origin(orig, orig_x) |
|
67
|
|
|
|
|
68
|
|
|
if self.style == "normal": |
|
69
|
|
|
return f'<span class="m_origin"><{origin}></span> ' |
|
70
|
|
|
return f"<o><{origin}></o> " |
|
71
|
|
|
|
|
72
|
|
|
@staticmethod |
|
73
|
|
|
def _compose_origin(orig: str, orig_x: str) -> str: |
|
74
|
|
|
""" |
|
75
|
|
|
Generate basic 'origin' string |
|
76
|
|
|
Args: |
|
77
|
|
|
orig: |
|
78
|
|
|
orig_x: |
|
79
|
|
|
|
|
80
|
|
|
Returns: |
|
81
|
|
|
|
|
82
|
|
|
""" |
|
83
|
|
|
if orig_x: |
|
84
|
|
|
return f"{orig}={orig_x}" if orig else orig_x |
|
85
|
|
|
return orig |
|
86
|
|
|
|
|
87
|
|
|
def export_as_html(self) -> str: |
|
88
|
|
|
""" |
|
89
|
|
|
Returns: |
|
90
|
|
|
""" |
|
91
|
|
|
n_l = "\n" |
|
92
|
|
|
mid, technical, definitions, used_in = self.generate_meaning() |
|
93
|
|
|
if self.style == "normal": |
|
94
|
|
|
used_in_list = ( |
|
95
|
|
|
f'<div class="used_in">Used In: ' f"{used_in}</div>\n</div>" |
|
96
|
|
|
if used_in |
|
97
|
|
|
else "</div>" |
|
98
|
|
|
) |
|
99
|
|
|
return ( |
|
100
|
|
|
f'<div class="meaning" id="{mid}">\n' |
|
101
|
|
|
f'<div class="technical">{technical}</div>\n' |
|
102
|
|
|
f'<div class="definitions">{n_l}' |
|
103
|
|
|
f"{n_l.join(definitions)}\n</div>\n{used_in_list}" |
|
104
|
|
|
) |
|
105
|
|
|
|
|
106
|
|
|
used_in_list = f"<us>Used In: {used_in}</us>\n</m>" if used_in else "</m>" |
|
107
|
|
|
return ( |
|
108
|
|
|
f"<m>\n<t>{technical}</t>\n" |
|
109
|
|
|
f"<ds>{n_l}" |
|
110
|
|
|
f"{n_l.join(definitions)}\n</ds>\n{used_in_list}" |
|
111
|
|
|
) |
|
112
|
|
|
|
|
113
|
|
|
def generate_meaning(self) -> tuple: |
|
114
|
|
|
""" |
|
115
|
|
|
:return: |
|
116
|
|
|
""" |
|
117
|
|
|
( |
|
118
|
|
|
html_affixes, |
|
119
|
|
|
html_match, |
|
120
|
|
|
html_rank, |
|
121
|
|
|
html_source, |
|
122
|
|
|
html_type, |
|
123
|
|
|
html_used_in, |
|
124
|
|
|
html_year, |
|
125
|
|
|
t_technical, |
|
126
|
|
|
) = self.get_styled_values() |
|
127
|
|
|
|
|
128
|
|
|
html_tech = ( |
|
129
|
|
|
t_technical % f"{html_match}{html_type}{html_source}{html_year}{html_rank}" |
|
130
|
|
|
) |
|
131
|
|
|
html_tech = f"{html_affixes}{self.html_origin()}{html_tech}" |
|
132
|
|
|
return self.word.id, html_tech, self.html_definitions(), html_used_in |
|
133
|
|
|
|
|
134
|
|
|
def html_definitions(self): |
|
135
|
|
|
""" |
|
136
|
|
|
:return: |
|
137
|
|
|
""" |
|
138
|
|
|
return [self.export_definition_for_loglan(d) for d in self.word.definitions] |
|
139
|
|
|
|
|
140
|
|
|
@staticmethod |
|
141
|
|
|
def _tagger(tag: str, value: str | None, default_value: str | None = str()): |
|
142
|
|
|
return tag % value if value else default_value |
|
143
|
|
|
|
|
144
|
|
|
def used_in_as_html(self) -> str: |
|
145
|
|
|
tags = { |
|
146
|
|
|
"normal": '<a class="m_cpx">%s</a>', |
|
147
|
|
|
"ultra": "<cpx>%s</cpx>", |
|
148
|
|
|
} |
|
149
|
|
|
return " | ".join( |
|
150
|
|
|
sorted( |
|
151
|
|
|
{ |
|
152
|
|
|
tags[self.style] % cpx.name |
|
153
|
|
|
for cpx in filter(None, self.word.complexes) |
|
154
|
|
|
} |
|
155
|
|
|
) |
|
156
|
|
|
) |
|
157
|
|
|
|
|
158
|
|
|
def get_styled_values(self) -> tuple: |
|
159
|
|
|
""" |
|
160
|
|
|
|
|
161
|
|
|
Returns: |
|
162
|
|
|
|
|
163
|
|
|
""" |
|
164
|
|
|
tags = { |
|
165
|
|
|
"normal": [ |
|
166
|
|
|
'<span class="m_afx">%s</span> ', |
|
167
|
|
|
'<span class="m_match">%s</span> ', |
|
168
|
|
|
'<span class="m_rank">%s</span>', |
|
169
|
|
|
'<span class="m_author">%s</span> ', |
|
170
|
|
|
'<span class="m_type">%s</span> ', |
|
171
|
|
|
'<span class="m_use">%s</span>', |
|
172
|
|
|
'<span class="m_year">%s</span> ', |
|
173
|
|
|
'<span class="m_technical">%s</span>', |
|
174
|
|
|
], |
|
175
|
|
|
"ultra": [ |
|
176
|
|
|
"<afx>%s</afx> ", |
|
177
|
|
|
"%s ", |
|
178
|
|
|
"%s", |
|
179
|
|
|
"%s ", |
|
180
|
|
|
"%s ", |
|
181
|
|
|
"<use>%s</use>", |
|
182
|
|
|
"%s ", |
|
183
|
|
|
"<tec>%s</tec>", |
|
184
|
|
|
], |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
values = [ |
|
188
|
|
|
self._ewc.e_affixes, |
|
189
|
|
|
self.word.match, |
|
190
|
|
|
self.word.rank, |
|
191
|
|
|
self._ewc.e_source, |
|
192
|
|
|
self.word.type.type_, |
|
193
|
|
|
self.used_in_as_html(), |
|
194
|
|
|
self._ewc.e_year, |
|
195
|
|
|
None, |
|
196
|
|
|
] |
|
197
|
|
|
default_values = [ |
|
198
|
|
|
str(), |
|
199
|
|
|
str(), |
|
200
|
|
|
str(), |
|
201
|
|
|
str(), |
|
202
|
|
|
str(), |
|
203
|
|
|
None, |
|
204
|
|
|
str(), |
|
205
|
|
|
tags[self.style][-1], |
|
206
|
|
|
] |
|
207
|
|
|
|
|
208
|
|
|
return tuple( |
|
209
|
|
|
self._tagger(tag, value, default_value) |
|
210
|
|
|
for tag, value, default_value in zip( |
|
211
|
|
|
tags[self.style], values, default_values |
|
212
|
|
|
) |
|
213
|
|
|
) |
|
214
|
|
|
|
|
215
|
|
|
|
|
216
|
|
|
class LoglanItem(Item): |
|
217
|
|
|
""" |
|
218
|
|
|
LoglanItem Class |
|
219
|
|
|
""" |
|
220
|
|
|
|
|
221
|
|
|
def __init__(self, words: list[Word], style: str = DEFAULT_HTML_STYLE): |
|
222
|
|
|
self.words = words |
|
223
|
|
|
self.style = style |
|
224
|
|
|
|
|
225
|
|
|
def export_as_html(self) -> str: |
|
226
|
|
|
word_template = { |
|
227
|
|
|
"normal": '<div class="word" wid="%s">\n' |
|
228
|
|
|
'<div class="word_line"><span class="word_name">%s</span>,</div>\n' |
|
229
|
|
|
'<div class="meanings">\n%s\n</div>\n</div>', |
|
230
|
|
|
"ultra": '<w wid="%s"><wl>%s,</wl>\n<ms>\n%s\n</ms>\n</w>', |
|
231
|
|
|
} |
|
232
|
|
|
word_name = self.words[0].name |
|
233
|
|
|
meanings = "\n".join( |
|
234
|
|
|
[Meaning(word, self.style).export_as_html() for word in self.words] |
|
235
|
|
|
) |
|
236
|
|
|
return word_template[self.style] % (word_name.lower(), word_name, meanings) |
|
237
|
|
|
|
|
238
|
|
|
|
|
239
|
|
|
class Composer(Item): |
|
240
|
|
|
def __init__(self, words: list[Word], style: str = DEFAULT_HTML_STYLE): |
|
241
|
|
|
self.words = words |
|
242
|
|
|
self.style = style |
|
243
|
|
|
|
|
244
|
|
|
def group_iterator(self) -> Iterator[list[Word]]: |
|
245
|
|
|
grouped_words = groupby(self.words, lambda ent: ent.name) |
|
246
|
|
|
for _, linked_words in grouped_words: |
|
247
|
|
|
yield list(linked_words) |
|
248
|
|
|
|
|
249
|
|
|
def export_as_html(self) -> str: |
|
250
|
|
|
""" |
|
251
|
|
|
Convert all words into one HTML string |
|
252
|
|
|
Args: |
|
253
|
|
|
Returns: |
|
254
|
|
|
|
|
255
|
|
|
""" |
|
256
|
|
|
|
|
257
|
|
|
words_template = { |
|
258
|
|
|
"normal": '<div class="words">\n%s\n</div>\n', |
|
259
|
|
|
"ultra": "<ws>\n%s\n</ws>\n", |
|
260
|
|
|
} |
|
261
|
|
|
|
|
262
|
|
|
iterator = self.group_iterator() |
|
263
|
|
|
items = [ |
|
264
|
|
|
LoglanItem(words_list, self.style).export_as_html() |
|
265
|
|
|
for words_list in iterator |
|
266
|
|
|
] |
|
267
|
|
|
return words_template[self.style] % "\n".join(items) |
|
268
|
|
|
|