Passed
Push — master ( 8c7e96...605616 )
by torrua
01:16
created

Meaning.generate_meaning()   A

Complexity

Conditions 1

Size

Total Lines 20
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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