Passed
Push — main ( b97eaa...2748a4 )
by torrua
01:46
created

loglan_db.model_html.html_word   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 133
dl 0
loc 277
ccs 93
cts 93
cp 1
rs 9.92
c 0
b 0
f 0
wmc 31

12 Methods

Rating   Name   Duplication   Size   Complexity  
A HTMLExportWord.__generate_origin() 0 18 3
A HTMLExportWord.meaning() 0 18 1
A HTMLExportWord.__case_insensitive_words_filter() 0 4 2
A HTMLExportWord.html_meaning() 0 23 4
A HTMLExportWord.__definitions_by_key() 0 21 2
A HTMLExportWord.__case_sensitive_words_filter() 0 4 2
A HTMLExportWord._get_stylized_words() 0 25 3
A HTMLExportWord.html_definitions() 0 8 1
A HTMLExportWord.get_styled_values() 0 29 2
A HTMLExportWord.translation_by_key() 0 26 2
A HTMLExportWord.html_origin() 0 20 4
B HTMLExportWord.html_all_by_name() 0 44 5
1
# -*- coding: utf-8 -*-
2
# pylint: disable=C0303
3 1
"""
4
This module contains a HTMLExportWord Model
5
"""
6 1
from itertools import groupby
7 1
from typing import Union, Optional, List
8
9 1
from sqlalchemy import or_
10
11 1
from loglan_db.model_db.addons.addon_word_getter import AddonWordGetter
12 1
from loglan_db.model_db.base_event import BaseEvent
13 1
from loglan_db.model_export import ExportWord
14 1
from loglan_db.model_html import DEFAULT_HTML_STYLE
15 1
from loglan_db.model_html.html_definition import HTMLExportDefinition
16
17
18 1
class HTMLExportWord(ExportWord, AddonWordGetter):
19
    """
20
    HTMLExportWord Class
21
    """
22
23 1
    @classmethod
24 1
    def html_all_by_name(
25
            cls, name: str, style: str = DEFAULT_HTML_STYLE,
26
            event_id: Union[BaseEvent, int, str] = None,
27
            case_sensitive: bool = False,
28
            partial_results: bool = False) -> Optional[str]:
29
        """
30
        Convert all words found by name into one HTML string
31
        Args:
32
            name: Name of the search word
33
            style: HTML design style
34
            event_id:
35
            case_sensitive:
36
            partial_results:
37
        Returns:
38
39
        """
40
41 1
        words_template = {
42
            "normal": '<div class="words">\n%s\n</div>\n',
43
            "ultra": '<ws>\n%s\n</ws>\n',
44
        }
45
46 1
        if not event_id:
47 1
            event_id = BaseEvent.latest().id
48
49 1
        event_id = int(event_id) if isinstance(event_id, (int, str)) else BaseEvent.id
50
51 1
        words = cls.query.filter(cls.event_start_id <= event_id) \
52
            .filter(or_(cls.event_end_id > event_id, cls.event_end_id.is_(None)))
53
54 1
        if case_sensitive:
55 1
            words = cls.__case_sensitive_words_filter(name, words, partial_results)
56
        else:
57 1
            words = cls.__case_insensitive_words_filter(name, words, partial_results)
58
59 1
        words = words.order_by(cls.name).all()
60
61 1
        if not words:
62 1
            return None
63
64 1
        items = cls._get_stylized_words(words, style)
65
66 1
        return words_template[style] % "\n".join(items)
67
68 1
    @classmethod
69
    def __case_insensitive_words_filter(cls, name, words, partial_results):
70 1
        return words.filter(cls.name.ilike(f"{name}%")) \
71
            if partial_results else words.filter(cls.name.ilike(name))
72
73 1
    @classmethod
74
    def __case_sensitive_words_filter(cls, name, words, partial_results):
75 1
        return words.filter(cls.name.like(f"{name}%")) \
76
            if partial_results else words.filter(cls.name == name)
77
78 1
    @staticmethod
79 1
    def _get_stylized_words(
80
            words: list, style: str = DEFAULT_HTML_STYLE) -> List[str]:
81
        """
82
83
        Args:
84
            words:
85
            style:
86
87
        Returns:
88
89
        """
90 1
        word_template = {
91
            "normal": '<div class="word" wid="%s">\n'
92
                      '<div class="word_line"><span class="word_name">%s</span>,</div>\n'
93
                      '<div class="meanings">\n%s\n</div>\n</div>',
94
            "ultra": '<w wid="%s"><wl>%s,</wl>\n<ms>\n%s\n</ms>\n</w>',
95
        }
96 1
        grouped_words = groupby(words, lambda ent: ent.name)
97 1
        group_words = {k: list(g) for k, g in grouped_words}
98 1
        items = []
99 1
        for word_name, words_list in group_words.items():
100 1
            meanings = "\n".join([word.html_meaning(style) for word in words_list])
101 1
            items.append(word_template[style] % (word_name.lower(), word_name, meanings))
102 1
        return items
103
104 1
    def html_origin(self, style: str = DEFAULT_HTML_STYLE):
105
        """
106
107
        Args:
108
            style:
109
110
        Returns:
111
112
        """
113 1
        orig = self.origin
114 1
        orig_x = self.origin_x
115
116 1
        if not (orig or orig_x):
117 1
            return str()
118
119 1
        origin = self.__generate_origin(orig, orig_x)
120
121 1
        if style == "normal":
122 1
            return f'<span class="m_origin">&lt;{origin}&gt;</span> '
123 1
        return f'<o>&lt;{origin}&gt;</o> '
124
125 1
    @staticmethod
126 1
    def __generate_origin(orig: str, orig_x: str) -> str:
127
        """
128
        Generate basic 'origin' string
129
        Args:
130
            orig:
131
            orig_x:
132
133
        Returns:
134
135
        """
136 1
        if not orig_x:
137 1
            return orig
138
139 1
        if not orig:
140 1
            return orig_x
141
142 1
        return f'{orig}={orig_x}'
143
144 1
    def html_definitions(self, style: str = DEFAULT_HTML_STYLE):
145
        """
146
147
        :param style:
148
        :return:
149
        """
150 1
        return [HTMLExportDefinition.export_for_loglan(
151
            d, style=style) for d in list(self.definitions)]
152
153 1
    def meaning(self, style: str = DEFAULT_HTML_STYLE) -> dict:
154
        """
155
156
        :param style:
157
        :return:
158
        """
159 1
        html_affixes, html_match, html_rank,\
160
            html_source, html_type, html_used_in,\
161
            html_year, t_technical = self.get_styled_values(style)
162
163 1
        html_tech = t_technical % f'{html_match}{html_type}{html_source}{html_year}{html_rank}'
164 1
        html_tech = f'{html_affixes}{self.html_origin(style)}{html_tech}'
165
166 1
        return {
167
            "mid": self.id,
168
            "technical": html_tech,
169
            "definitions": self.html_definitions(style),
170
            "used_in": html_used_in
171
        }
172
173 1
    def get_styled_values(self, style: str = DEFAULT_HTML_STYLE) -> tuple:
174
        """
175
176
        Args:
177
            style:
178
179
        Returns:
180
181
        """
182 1
        tags = {
183
            "normal": [
184
                '<span class="m_afx">%s</span> ', '<span class="m_match">%s</span> ',
185
                '<span class="m_rank">%s</span>', '<span class="m_author">%s</span> ',
186
                '<span class="m_type">%s</span> ', '<span class="m_use">%s</span>',
187
                '<span class="m_year">%s</span> ', '<span class="m_technical">%s</span>'],
188
            "ultra": [
189
                '<afx>%s</afx> ', '%s ', '%s', '%s ', '%s ',
190
                '<use>%s</use>', '%s ', '<tec>%s</tec>'],
191
        }
192
193 1
        def _stringer(tag: str, value: Optional[str], default_value: Optional[str] = str()):
194 1
            return tag % value if value else default_value
195
196 1
        values = [self.e_affixes, self.match, self.rank, self.e_source, self.type.type,
197
                  self.e_usedin.replace("| ", "|&nbsp;"), self.e_year, None]
198 1
        default_values = [str(), str(), str(), str(), str(), None, str(), tags[style][-1]]
199
200 1
        return tuple([_stringer(tag, value, default_value) for tag, value, default_value in
201
                      zip(tags[style], values, default_values)])
202
203 1
    def html_meaning(self, style: str = DEFAULT_HTML_STYLE) -> str:
204
        """
205
206
        Args:
207
            style:
208
209
        Returns:
210
211
        """
212 1
        n_l = "\n"
213 1
        meaning_dict = self.meaning(style)
214 1
        if style == "normal":
215 1
            used_in_list = f'<div class="used_in">Used In: ' \
216
                           f'{meaning_dict.get("used_in")}</div>\n</div>' \
217
                if meaning_dict.get("used_in") else "</div>"
218 1
            return f'<div class="meaning" id="{meaning_dict.get("mid")}">\n' \
219
                   f'<div class="technical">{meaning_dict.get("technical")}</div>\n' \
220
                   f'<div class="definitions">{n_l}' \
221
                   f'{n_l.join(meaning_dict.get("definitions"))}\n</div>\n{used_in_list}'
222
223 1
        used_in_list = f'<us>Used In: {meaning_dict.get("used_in")}</us>\n</m>' \
224
            if meaning_dict.get("used_in") else '</m>'
225 1
        return f'<m>\n<t>{meaning_dict.get("technical")}</t>\n' \
226
               f'<ds>{n_l}' \
227
               f'{n_l.join(meaning_dict.get("definitions"))}\n</ds>\n{used_in_list}'
228
229 1
    @staticmethod
230 1
    def translation_by_key(
231
            key: str, language: str = None,
232
            style: str = DEFAULT_HTML_STYLE) -> Optional[str]:
233
        """
234
        Get information about loglan words by key in a foreign language
235
        Args:
236
            key:
237
            language:
238
            style:
239
240
        Returns:
241
242
        """
243
244 1
        words = HTMLExportWord.by_key(key, language).order_by(HTMLExportWord.name).all()
245
246 1
        if not words:
247 1
            return None
248
249 1
        result = HTMLExportWord.__definitions_by_key(key, words, style)
250
251 1
        new = '\n'
252
253 1
        return new.join([f"{new.join(definitions)}"
254
                         for _, definitions in result.items()]).strip()
255
256 1
    @staticmethod
257 1
    def __definitions_by_key(
258
            key: str, words: List[ExportWord], style: str = DEFAULT_HTML_STYLE) -> dict:
259
        """
260
261
        Args:
262
            key:
263
            words:
264
            style:
265
266
        Returns:
267
268
        """
269 1
        result = {}
270 1
        for word in words:
271 1
            result[word.name] = []
272 1
            definitions = [
273
                HTMLExportDefinition.export_for_english(d, word=key, style=style)
274
                for d in word.definitions if key.lower() in [key.word.lower() for key in d.keys]]
275 1
            result[word.name].extend(definitions)
276
        return result
277