Skip to content

Commit 205c84b

Browse files
committed
tries refactoring
1 parent 7762788 commit 205c84b

10 files changed

Lines changed: 52 additions & 73 deletions

File tree

frontend/forms/form_field.py

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
@author: wf
55
"""
66

7+
from dataclasses import dataclass
78
from typing import Any, Dict, List, Optional, Union
89

910
from basemkit.yamlable import lod_storable
1011

12+
1113
# Type alias: a translatable string is either a plain str or a
1214
# language-keyed dict, e.g. {"en": "Name", "de": "Name", "fr": "Nom"}.
1315
I18nStr = Optional[Union[str, Dict[str, str]]]
@@ -34,51 +36,36 @@ def resolve(value: I18nStr, lang: str = "en") -> str:
3436
Returns:
3537
str: the resolved string for the requested language
3638
"""
37-
if value is None:
38-
return ""
39-
if isinstance(value, dict):
40-
resolved = value.get(lang) or value.get("en")
41-
if resolved is None and value:
42-
resolved = next(iter(value.values()))
43-
return resolved or ""
44-
return value
39+
i18n=""
40+
if value is not None:
41+
if isinstance(value, dict):
42+
i18n = value.get(lang) or value.get("en")
43+
if i18n is None and value:
44+
i18n= next(iter(value.values()))
45+
i18n=i18n or ""
46+
return i18n
4547

4648

4749
def resolve_i18n(value: I18nStr, lang: str = "en") -> str:
4850
"""Backward compatible alias for I18n.resolve()"""
49-
return I18n.resolve(value, lang)
50-
51+
i18n=I18n.resolve(value, lang)
52+
return i18n
5153

54+
@dataclass
5255
class HtmlElement:
5356
"""
5457
Base class for HTML elements with common attributes.
5558
"""
56-
5759
css_class: str = "" # e.g. "form-control selectpicker"
5860
size: str = "" # xs, sm, md, lg
5961

6062

6163
@lod_storable
62-
class FormLabel:
64+
class FormLabel(HtmlElement):
6365
"""
6466
Label element with i18n support for form fields.
65-
66-
Can be defined as a simple string/dict (backward compatible) or as an object:
67-
68-
# Simple (backward compatible):
69-
label: "Name" or label: {en: "Name", de: "Name"}
70-
71-
# Object with CSS class:
72-
label:
73-
text:
74-
en: "Name"
75-
de: "Name"
76-
css_class: "bitplanorange"
7767
"""
78-
7968
text: I18nStr = "" # the label text
80-
css_class: str = "" # additional CSS class (e.g., "bitplanorange")
81-
size: str = "" # xs, sm, md, lg
8269

8370

8471
@lod_storable
@@ -103,16 +90,15 @@ class FormField(HtmlElement):
10390
max: 100
10491
- type: Email
10592
"""
106-
10793
name: str = "" # required but defaults to empty for compatibility
10894
field_type: str = "text" # text | textarea | select | hidden | email
109-
label: Any = None # I18nStr or FormLabel object
110-
placeholder: Any = None # I18nStr
95+
label: FormLabel = None
96+
placeholder: I18nStr = None # I18nStr
11197
glyphicon: Optional[str] = None
11298
required: bool = False
113-
error_msg: Any = None # I18nStr
99+
error_msg: I18nStr= None # I18nStr
114100
value: Optional[str] = None # for hidden fields / pre-filled defaults
115-
choices: Optional[List[str]] = None # for select fields
101+
choices: Optional[List[I18nStr]] = None # for select fields
116102
placeholder_choice: I18nStr = None # e.g. "Bitte wählen" for select fields
117103
validators: Optional[List[Dict[str, Any]]] = None # WTForms validator descriptors
118104

@@ -130,9 +116,9 @@ class FormDefinition:
130116
legend: Any # I18nStr — required, no default
131117
fields: List[FormField]
132118
action: str = ""
133-
submit_label: Any = None # I18nStr; defaults to {"en": "Send", "de": "Absenden"}
119+
submit_label: I18nStr = None # I18nStr; defaults to {"en": "Send", "de": "Absenden"}
134120
submit_glyphicon: Optional[str] = None
135-
success_message: Any = None # I18nStr
121+
success_message: I18nStr = None # I18nStr
136122

137123
def resolved_submit_label(self, lang: str = "en") -> str:
138124
"""

frontend/forms/renderer.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,13 @@ def render(
6666
parts.append(Markup(f"<fieldset>\n<legend>{escape(legend)}</legend>\n"))
6767

6868
for field in form_def.fields:
69-
parts.append(
70-
self._render_field(
69+
field_html=self._render_field(
7170
field,
7271
values.get(field.name, ""),
7372
errors.get(field.name, []),
7473
lang,
7574
)
76-
)
75+
parts.append(field_html)
7776

7877
parts.append(self._render_submit(form_def, lang))
7978
parts.append(Markup("</fieldset>\n</form>"))

frontend/htmlfilter.py

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from frontend.forms.registry import FormRegistry
1313
from frontend.forms.renderer import FormRenderer
14+
from duckdb.experimental.spark.errors.exceptions.base import IllegalArgumentException
1415

1516

1617
@lod_storable
@@ -107,32 +108,22 @@ def filter(self, pc: PageContent):
107108
"""
108109
filter the given page content
109110
"""
110-
soup = self.doFilter(pc.html, self.filterKeys)
111-
html_to_filter = soup.decode_contents()
112-
temp_pc = PageContent(html=html_to_filter, lang=pc.lang)
113-
temp_pc = self.filter_html(temp_pc)
114-
pc.content = temp_pc.html
111+
self.doFilter(pc, self.filterKeys)
112+
self.filter_html(pc)
115113
return pc
116114

117-
def filter_html(self, pc_or_html) -> PageContent:
115+
def filter_html(self, pc:PageContent):
118116
"""
119-
Apply filters directly on the raw HTML string without re-parsing,
120-
to avoid lxml re-serialization mangling content.
117+
Apply filters
121118
Detects wikicms-form divs and replaces them with rendered form HTML
122119
when a form_registry is set.
123120
124121
Args:
125-
pc_or_html: either a PageContent object or a string (for backward compatibility)
122+
pc: a PageContent object
126123
127-
Returns:
128-
If given a string, returns the filtered string.
129-
If given a PageContent, returns the PageContent with filtered html.
130124
"""
131-
return_string = isinstance(pc_or_html, str)
132-
if return_string:
133-
pc = PageContent(html=pc_or_html)
134-
else:
135-
pc = pc_or_html
125+
if not isinstance(pc, PageContent):
126+
raise IllegalArgumentException("Expecting PageContent but got",type(pc))
136127
if "editsection" in self.filterKeys:
137128
pc.html = re.sub(
138129
r'<span class="mw-editsection">.*?</span>\s*</span>',
@@ -141,12 +132,9 @@ def filter_html(self, pc_or_html) -> PageContent:
141132
flags=re.DOTALL,
142133
)
143134
if self.form_registry is not None:
144-
pc = self.replace_form_divs(pc)
145-
if return_string:
146-
return pc.html
147-
return pc
135+
self.replace_form_divs(pc)
148136

149-
def replace_form_divs(self, pc: PageContent) -> PageContent:
137+
def replace_form_divs(self, pc: PageContent):
150138
"""
151139
Find all <div class="wikicms-form" data-form-name="..."> elements and
152140
replace each with the rendered form HTML from the registry.
@@ -157,7 +145,6 @@ def replace_form_divs(self, pc: PageContent) -> PageContent:
157145
Returns:
158146
PageContent: the page content with forms replaced
159147
"""
160-
161148
renderer = FormRenderer()
162149

163150
def replace_match(m: re.Match) -> str:
@@ -173,11 +160,10 @@ def replace_match(m: re.Match) -> str:
173160
pc.html,
174161
flags=re.DOTALL,
175162
)
176-
return pc
177163

178-
def doFilter(self, html, filterKeys):
164+
def doFilter(self, pc:PageContent, filterKeys)->BeautifulSoup:
179165
# https://stackoverflow.com/questions/5598524/can-i-remove-script-tags-with-beautifulsoup
180-
soup = BeautifulSoup(html, self.parser)
166+
soup = BeautifulSoup(pc.html, self.parser)
181167
if "parser-output" in filterKeys:
182168
parserdiv = soup.find("div", {"class": "mw-parser-output"})
183169
if parserdiv:
@@ -190,7 +176,7 @@ def doFilter(self, html, filterKeys):
190176
s.extract()
191177
for comments in soup.findAll(text=lambda text: isinstance(text, Comment)):
192178
comments.extract()
193-
return soup
179+
pc.soup=soup
194180

195181
def fixNode(self, node, attribute, prefix, delim=None):
196182
"""

tests/test_forms.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from frontend.forms.registry import FormRegistry
1414
from frontend.forms.renderer import FormRenderer
1515
from frontend.forms.validators import build_wtforms_validators, validate_with_wtforms
16-
from frontend.htmlfilter import MediaWikiHtmlFilter
16+
from frontend.htmlfilter import MediaWikiHtmlFilter, PageContent
1717

1818
# Path to the built-in contact.yaml shipped with the package
1919
_CONTACT_YAML = (
@@ -389,15 +389,14 @@ def test_htmlfilter_replaces_form_div(self):
389389
"""
390390
form_def = make_contact_form()
391391
FormRegistry.register(form_def)
392-
393-
html = (
392+
pc=PageContent()
393+
pc.html = (
394394
'<div class="wikicms-form" data-form-name="contact">'
395395
'<a href="http://www.bitplan.com/Contact">Contact form</a>'
396396
"</div>"
397397
)
398-
399398
mwf = MediaWikiHtmlFilter()
400-
result = mwf.filter_html(html)
399+
result = mwf.filter_html(pc)
401400
if self.debug:
402401
print(result)
403402
self.assertNotIn("wikicms-form", result)

tests/test_frontend.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
class TestFrontend(WebserverTest):
2929
"""
3030
test the frontend
31+
14 tests in 9.8 secs
3132
"""
3233

3334
instance = None
@@ -397,7 +398,9 @@ def testFixHtml(self):
397398
self.fail(pc.error)
398399
self.assertEqual(pc.page_title, "Welcome")
399400
content = pc.content
400-
if self.debug:
401+
debug=self.debug
402+
debug=True
403+
if debug:
401404
print(content)
402405

403406
self.assertFalse("""href="/index.php""" in content)

tests/test_htmlfilter.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
class TestHtmlFilter(Basetest):
1313
"""
1414
test MediaWiki HTML filter
15+
2 tests in 0.07 secs
1516
"""
1617

17-
def setUp(self, debug=True, profile=True):
18+
def setUp(self, debug=False, profile=True):
1819
Basetest.setUp(self, debug=debug, profile=profile)
1920

2021
def testToReveal(self):

tests/test_parser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def endElement(self, name):
3131
class TestParser(Basetest):
3232
"""
3333
test different parser approaches
34+
2 tests in 0.01 secs
3435
"""
3536

3637
def setUp(self):
@@ -43,7 +44,7 @@ def setUp(self):
4344
<h2><span id="⌘⌘_Slide1"></span><span class="mw-headline" id=".E2.8C.98.E2.8C.98_Slide1">⌘⌘ Slide1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=RevealTest&amp;action=edit&amp;section=1" title="Edit section: ⌘⌘ Slide1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
4445
<p>Content for slide 1</p>
4546
<h2><span id="⌘⌘_Slide2"></span><span class="mw-headline" id=".E2.8C.98.E2.8C.98_Slide2">⌘⌘ Slide2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=RevealTest&amp;action=edit&amp;section=2" title="Edit section: ⌘⌘ Slide2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
46-
<p>Content for slide 2</p>
47+
<p>Content for slide 2</p>
4748
</div>
4849
</body>
4950
</html>"""

tests/test_site.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
class TestSite(Basetest):
1515
"""
1616
test wiki
17+
4 tests in 3.4 secs
1718
"""
1819

1920
def setUp(self, debug=True, profile=True):

tests/test_wikicms.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
class TestWikiCMS(Basetest):
1515
"""
1616
test the Mediawiki based Content Management System
17+
2 tests in 0.2 secs
1718
"""
1819

1920
def setUp(self):

tests/test_wikis.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ def setUp(self, debug=False, profile=True):
1919

2020
def testWikis(self):
2121
"""
22-
test wikis"""
22+
test wiki
23+
e.g. 91 wikis in 2.16 secs
24+
"""
2325
wikis = Wikis()
2426
graph = MogwaiGraph()
2527
wikis.add_to_graph(graph, with_progress=True)

0 commit comments

Comments
 (0)