loglan_db.model_html.html_word   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 282
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 137
dl 0
loc 282
ccs 95
cts 95
cp 1
rs 9.92
c 0
b 0
f 0
wmc 31

13 Methods

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