Path:
strictdoc/export/html/form_objects/grammar_form_object.py
Lines:
240
Non-empty lines:
209
Non-empty lines covered with requirements:
209 / 209 (100.0%)
Functions:
15
Functions covered by requirements:
15 / 15 (100.0%)
1
"""2
@relation(SDOC-SRS-56, scope=file)3
"""4
5
from typing import List, Optional, Set
6
7
from markupsafe import Markup
8
from starlette.datastructures import FormData
9
10
from strictdoc.backend.sdoc.models.document import SDocDocument
11
from strictdoc.backend.sdoc.models.document_grammar import (
12
DocumentGrammar,
13
)14
from strictdoc.backend.sdoc.models.grammar_element import GrammarElement
15
from strictdoc.core.project_config import ProjectConfig
16
from strictdoc.export.html.form_objects.rows.row_with_grammar_element_form_object import (
17
RowWithGrammarElementFormObject,
18
)19
from strictdoc.export.html.html_templates import JinjaEnvironment
20
from strictdoc.helpers.auto_described import auto_described
21
from strictdoc.helpers.cast import assert_cast
22
from strictdoc.helpers.form_data import (
23
ParsedFormData,
24
parse_form_data,
25
)26
from strictdoc.helpers.mid import MID
27
from strictdoc.helpers.string import is_uppercase_underscore_string
28
from strictdoc.server.error_object import ErrorObject
29
from strictdoc.server.helpers.turbo import render_turbo_stream
30
31
32
@auto_described33
class GrammarElementFormField:
34
def __init__(
35
self,
36
is_new: bool,
37
field_mid: str,
38
field_name: str,
39
document_mid: str,
40
):41
self.is_new: bool = is_new
42
self.field_mid: str = field_mid
43
self.field_name: str = field_name
44
self.document_mid: str = document_mid
45
46
@staticmethod47
def create(
48
*,
49
is_new: bool,
50
grammar_element: GrammarElement,
51
document_mid: str,
52
) -> "GrammarElementFormField":
53
return GrammarElementFormField(
54
is_new=is_new,
55
field_mid=grammar_element.mid,
56
field_name=grammar_element.tag,
57
document_mid=document_mid,
58
)59
60
def get_input_field_name(self) -> str:
61
return f"document_grammar_element_field[{self.field_mid}][field_name]"
62
63
def get_input_field_is_new(self) -> str:
64
return f"document_grammar_element_field[{self.field_mid}][is_new]"
65
66
def get_is_new_as_string(self) -> str:
67
return "true" if self.is_new else "false"
68
69
70
@auto_described71
class GrammarFormObject(ErrorObject):
72
def __init__(
73
self,
74
*,
75
document_mid: str,
76
fields: List[GrammarElementFormField],
77
project_config: ProjectConfig,
78
jinja_environment: JinjaEnvironment,
79
imported_grammar_file: Optional[str],
80
):81
assert isinstance(document_mid, str), document_mid
82
super().__init__()
83
self.document_mid = document_mid
84
self.fields: List[GrammarElementFormField] = fields
85
self.project_config: ProjectConfig = project_config
86
self.jinja_environment: JinjaEnvironment = jinja_environment
87
self.imported_grammar_file: Optional[str] = imported_grammar_file
88
89
@staticmethod90
def create_from_request(
91
*,
92
document_mid: str,
93
request_form_data: FormData,
94
project_config: ProjectConfig,
95
jinja_environment: JinjaEnvironment,
96
) -> "GrammarFormObject":
97
form_object_fields: List[GrammarElementFormField] = []
98
request_form_data_as_list = [
99
(field_name, field_value)
100
for field_name, field_value in request_form_data.multi_items()
101
]102
request_form_dict: ParsedFormData = assert_cast(
103
parse_form_data(request_form_data_as_list), dict
104
)105
106
document_grammar_fields = request_form_dict[
107
"document_grammar_element_field"108
]109
110
if not isinstance(document_grammar_fields, dict):
111
raise TypeError(
112
f"Expected a dict, but got {type(document_grammar_fields)}"
113
)114
115
for field_mid, field_dict in document_grammar_fields.items():
116
assert isinstance(field_dict, dict), type(field_dict)
117
118
is_new = field_dict["is_new"] == "true"
119
field_name = assert_cast(field_dict["field_name"], str)
120
121
form_object_field = GrammarElementFormField(
122
is_new=is_new,
123
field_mid=field_mid,
124
field_name=field_name,
125
document_mid=document_mid,
126
)127
form_object_fields.append(form_object_field)
128
129
form_object = GrammarFormObject(
130
document_mid=document_mid,
131
fields=form_object_fields,
132
project_config=project_config,
133
jinja_environment=jinja_environment,
134
imported_grammar_file=None,
135
)136
return form_object
137
138
@staticmethod139
def create_from_document(
140
*,
141
document: SDocDocument,
142
project_config: ProjectConfig,
143
jinja_environment: JinjaEnvironment,
144
) -> "GrammarFormObject":
145
assert isinstance(document, SDocDocument)
146
assert isinstance(document.grammar, DocumentGrammar)
147
148
grammar: DocumentGrammar = document.grammar
149
150
grammar_element_form_fields: List[GrammarElementFormField] = []
151
152
for element_ in grammar.elements:
153
grammar_form_field = GrammarElementFormField.create(
154
is_new=False,
155
grammar_element=element_,
156
document_mid=document.reserved_mid,
157
)158
grammar_element_form_fields.append(grammar_form_field)
159
160
return GrammarFormObject(
161
document_mid=document.reserved_mid,
162
fields=grammar_element_form_fields,
163
project_config=project_config,
164
jinja_environment=jinja_environment,
165
imported_grammar_file=grammar.import_from_file,
166
)167
168
def validate(self) -> bool:
169
fields_so_far: Set[str] = set()
170
for field in self.fields:
171
if len(field.field_name) == 0:
172
self.add_error(
173
field.get_input_field_name(),
174
"Provide a name for the grammar element.",
175
)176
continue177
178
if not is_uppercase_underscore_string(field.field_name):
179
self.add_error(
180
field.get_input_field_name(),
181
(182
"Grammar element title shall consist of "183
"uppercase letters, digits and single underscores."184
),185
)186
187
if field.field_name in fields_so_far:
188
self.add_error(
189
field.get_input_field_name(),
190
f"Grammar element {field.field_name} is not unique.",
191
)192
else:
193
fields_so_far.add(field.field_name)
194
195
return len(self.errors) == 0
196
197
def render(self) -> Markup:
198
rendered_template = self.jinja_environment.render_template_as_markup(
199
"components/grammar_form/index.jinja", form_object=self
200
)201
return render_turbo_stream(
202
content=rendered_template, action="update", target="modal"
203
)204
205
def render_row_with_grammar_element(
206
self, field: GrammarElementFormField
207
) -> Markup:
208
form_object = RowWithGrammarElementFormObject(
209
field=field,
210
errors=self.errors,
211
jinja_environment=self.jinja_environment,
212
)213
return form_object.render()
214
215
def render_row_with_new_grammar_element(self) -> str:
216
field: GrammarElementFormField = GrammarElementFormField(
217
is_new=True,
218
field_mid=MID.create(),
219
field_name="",
220
document_mid=self.document_mid,
221
)222
form_object = RowWithGrammarElementFormObject(
223
field=field,
224
errors=self.errors,
225
jinja_environment=self.jinja_environment,
226
)227
rendered_template: str = form_object.render()
228
return render_turbo_stream(
229
content=rendered_template,
230
action="append",
231
target="document__editable_grammar_elements",
232
)233
234
@staticmethod235
def render_close_form() -> str:
236
return render_turbo_stream(
237
content="",
238
action="update",
239
target="modal",
240
)