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
|
|
|
|