app.site.compose.loglan_item   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 268
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 29
eloc 163
dl 0
loc 268
rs 10
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A Meaning.generate_meaning() 0 20 1
A Composer.group_iterator() 0 4 3
A LoglanItem.export_as_html() 0 12 1
A Composer.__init__() 0 3 1
A Meaning.__init__() 0 4 1
A Meaning.used_in_as_html() 0 10 1
A Meaning.html_origin() 0 15 4
A Composer.export_as_html() 0 19 1
A LoglanItem.__init__() 0 3 1
B Meaning.get_styled_values() 0 54 1
A Meaning._compose_origin() 0 14 3
A Meaning.export_as_html() 0 22 4
A Meaning.export_definition_for_loglan() 0 32 4
A Meaning.html_definitions() 0 5 1
A Meaning._tagger() 0 3 2
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("-", "&zwj;-&zwj;") 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">&lt;{origin}&gt;</span> '
70
        return f"<o>&lt;{origin}&gt;</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 " |&nbsp;".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