ul-api-utils 7.7.10__py3-none-any.whl → 7.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ul-api-utils might be problematic. Click here for more details.

File without changes
@@ -0,0 +1,5 @@
1
+ from wtforms import SelectMultipleField, widgets # type: ignore
2
+
3
+
4
+ class MultiCheckboxField(SelectMultipleField):
5
+ option_widget = widgets.CheckboxInput()
@@ -0,0 +1,86 @@
1
+ from typing import Any
2
+
3
+ from markupsafe import Markup
4
+ from wtforms import SelectField # type: ignore
5
+ from wtforms.widgets.core import html_params, Select # type: ignore
6
+
7
+
8
+ class CustomLiveSearchPlaceholderSelect(Select):
9
+ """
10
+ Renders a CUSTOM select field.
11
+
12
+ If `multiple` is True, then the `size` property should be specified on
13
+ rendering to make the field useful.
14
+
15
+ The field must provide an `iter_choices()` method which the widget will
16
+ call on rendering; this method must yield tuples of
17
+ `(value, label, selected)`.
18
+ It also must provide a `has_groups()` method which tells whether choices
19
+ are divided into groups, and if they do, the field must have an
20
+ `iter_groups()` method that yields tuples of `(label, choices)`, where
21
+ `choices` is an iterable of `(value, label, selected)` tuples.
22
+ Otherwise, `selected` is False for any option field in select item group.
23
+ """
24
+ def __call__(self, field: SelectField, **kwargs: Any) -> Markup:
25
+ kwargs.setdefault("id", field.id)
26
+ if self.multiple:
27
+ kwargs["multiple"] = True
28
+ flags = getattr(field, "flags", {})
29
+ for k in dir(flags):
30
+ if k in self.validation_attrs and k not in kwargs:
31
+ kwargs[k] = getattr(flags, k)
32
+
33
+ html = ["<select data-live-search='true' data-show-subtext='true' %s>" % html_params(name=field.name, **kwargs),
34
+ "<option value='' disabled selected>Select something...</option>"]
35
+
36
+ if field.has_groups():
37
+ for group, choices in field.iter_groups():
38
+ html.append("<optgroup %s>" % html_params(label=group))
39
+ for val, label, selected in choices:
40
+ html.append(self.render_option(val, label, selected))
41
+ html.append("</optgroup>")
42
+ else:
43
+ for val, label, _ in field.iter_choices():
44
+ html.append(self.render_option(val, label, False))
45
+ html.append("</select>")
46
+ return Markup("".join(html))
47
+
48
+
49
+ class CustomLiveSearchSelect(Select):
50
+ """
51
+ Renders a CUSTOM select field.
52
+
53
+ If `multiple` is True, then the `size` property should be specified on
54
+ rendering to make the field useful.
55
+
56
+ The field must provide an `iter_choices()` method which the widget will
57
+ call on rendering; this method must yield tuples of
58
+ `(value, label, selected)`.
59
+ It also must provide a `has_groups()` method which tells whether choices
60
+ are divided into groups, and if they do, the field must have an
61
+ `iter_groups()` method that yields tuples of `(label, choices)`, where
62
+ `choices` is an iterable of `(value, label, selected)` tuples.
63
+ Otherwise, `selected` is False for any option field in select item group.
64
+ """
65
+ def __call__(self, field: SelectField, **kwargs: Any) -> Markup:
66
+ kwargs.setdefault("id", field.id)
67
+ if self.multiple:
68
+ kwargs["multiple"] = True
69
+ flags = getattr(field, "flags", {})
70
+ for k in dir(flags):
71
+ if k in self.validation_attrs and k not in kwargs:
72
+ kwargs[k] = getattr(flags, k)
73
+
74
+ html = ["<select data-live-search='true' data-show-subtext='true' %s>" % html_params(name=field.name, **kwargs)]
75
+
76
+ if field.has_groups():
77
+ for group, choices in field.iter_groups():
78
+ html.append("<optgroup %s>" % html_params(label=group))
79
+ for val, label, selected in choices:
80
+ html.append(self.render_option(val, label, selected))
81
+ html.append("</optgroup>")
82
+ else:
83
+ for val, label, selected in field.iter_choices():
84
+ html.append(self.render_option(val, label, selected))
85
+ html.append("</select>")
86
+ return Markup("".join(html))
@@ -0,0 +1,42 @@
1
+ from typing import Optional, Any
2
+
3
+ from wtforms import StringField # type: ignore
4
+ from wtforms.widgets import TextInput # type: ignore
5
+ from markupsafe import Markup
6
+
7
+
8
+ class CustomTextInput(TextInput):
9
+ """
10
+ Render a single-line text input with optional input attributes ("required", "maxlength", "minlength", "pattern").
11
+
12
+ examples: TextInputCustom(required=True, min_length=5, max_length=255, pattern=r"^(d+(,d+)*)?$")
13
+ TextInputCustom(max_length=255, pattern=r"^(d+(,d+)*)?$")
14
+ TextInputCustom(required=True, pattern=r"^(d+(,d+)*)?$")
15
+ TextInputCustom(max_length=255)
16
+ In model usage: ... info={"label": "LABEL:", "widget": TextInputCustom(pattern=r"^(d+(,d+)*)?$")}
17
+ """
18
+ validation_attrs = ["required", "maxlength", "minlength", "pattern"]
19
+
20
+ def __init__(self, required: Optional[bool] = None, max_length: Optional[int] = None, min_length: Optional[int] = None, pattern: Optional[str] = None):
21
+ super().__init__()
22
+ self.required = required
23
+ self.max_length = max_length
24
+ self.min_length = min_length
25
+ self.pattern = pattern
26
+
27
+ def __call__(self, field: StringField, **kwargs: Any) -> Markup:
28
+ kwargs.setdefault("id", field.id)
29
+ kwargs.setdefault("type", self.input_type)
30
+ if self.min_length:
31
+ kwargs.setdefault("minlength", self.min_length)
32
+ if self.max_length:
33
+ kwargs.setdefault("maxlength", self.max_length)
34
+ if self.pattern:
35
+ kwargs.setdefault("pattern", self.pattern)
36
+ if "value" not in kwargs:
37
+ kwargs["value"] = field._value()
38
+ flags = getattr(field, "flags", {})
39
+ for k in dir(flags):
40
+ if k in self.validation_attrs and k not in kwargs:
41
+ kwargs[k] = getattr(flags, k)
42
+ return Markup("<input %s>" % self.html_params(name=field.name, **kwargs))
@@ -0,0 +1,75 @@
1
+ from typing import Type, Any, Dict, Optional, Tuple
2
+
3
+ from ul_db_utils.modules.db import db
4
+ from sqlalchemy.dialects import postgresql
5
+ from sqlalchemy.dialects.postgresql import ARRAY
6
+ from wtforms_alchemy import ModelForm, ClassMap, FormGenerator # type: ignore
7
+ from wtforms.fields import SelectField # type: ignore
8
+
9
+ from ul_db_utils.model.base_model import BaseModel
10
+
11
+ from ul_api_utils.resources.web_forms.custom_fields.custom_checkbox_select import MultiCheckboxField
12
+
13
+
14
+ def form_factory(model_obj: Type[BaseModel], edition: bool = False, *, extra_fields: Optional[Dict[str, Any]] = None) -> Type[ModelForm]:
15
+ """
16
+ Returns generated model form.
17
+
18
+ Parameters:
19
+ model_obj (type_of(BaseModel)): model object which will use for form generation
20
+ edition (bool): flag to indicate creation or edition form will be generated
21
+ extra_fields (optional(dict(str, any))): additional fields for generated form
22
+
23
+ Returns:
24
+ Form (type_of(ModelForm)): web model form
25
+ """
26
+
27
+ class ExtraFieldsFormGenerator(FormGenerator):
28
+ def create_fields(self, form: Any, properties: Dict[str, Any]) -> None:
29
+ """
30
+ Creates fields for given form based on given model attributes.
31
+
32
+ :param form: form to attach the generated fields into
33
+ :param properties: model attributes to generate the form fields from
34
+ """
35
+ super(ExtraFieldsFormGenerator, self).create_fields(form, properties)
36
+
37
+ if extra_fields:
38
+ for field_name, field in extra_fields.items():
39
+ setattr(form, field_name, field)
40
+
41
+ class Form(ModelForm):
42
+ """
43
+ A class for representing a web form.
44
+
45
+ ...
46
+
47
+ Attributes
48
+ ----------
49
+ property_columns : dict(str, any)
50
+ Model property columns such as ID, USER_CREATED etc.
51
+
52
+ Methods
53
+ -------
54
+
55
+ """
56
+
57
+ property_columns: Dict[str, Any] = {}
58
+
59
+ def __init__(self, *args: Tuple[Any], **kwargs: Dict[str, Any]) -> None:
60
+ super(Form, self).__init__(*args, **kwargs)
61
+ self.property_columns = model_obj.get_property_columns(self._obj) if self._obj else {}
62
+
63
+ class Meta:
64
+ model = model_obj
65
+ type_map = ClassMap({postgresql.UUID: SelectField, ARRAY: MultiCheckboxField})
66
+ only = model_obj.get_edit_columns() if edition else model_obj.get_create_columns()
67
+ form_generator = ExtraFieldsFormGenerator
68
+
69
+ @classmethod
70
+ def get_session(cls) -> Any:
71
+ return db.session
72
+
73
+ Form.__name__ = f"{model_obj.__name__}Form"
74
+
75
+ return Form
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ul-api-utils
3
- Version: 7.7.10
3
+ Version: 7.8.0
4
4
  Summary: Python api utils
5
5
  Author: Unic-lab
6
6
  Author-email:
@@ -77,6 +77,13 @@ ul_api_utils/resources/health_check/const.py,sha256=QzVZP_ZKmVKleUACiOGjzP-v54FD
77
77
  ul_api_utils/resources/health_check/health_check.py,sha256=bb_ONn3akNXxNFELIUcW6foqwiKqeinmr-Wl0fQ7Q4c,18449
78
78
  ul_api_utils/resources/health_check/health_check_template.py,sha256=Qih-sVoFVoVxfmDYBTzwlNSicCr7zNelUJLJMnM-C_Q,2572
79
79
  ul_api_utils/resources/health_check/resource.py,sha256=SPd9kMzBOVhFZgMVfV26bDpZya3BmwxTfOR4MZ2dL4o,4199
80
+ ul_api_utils/resources/web_forms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
+ ul_api_utils/resources/web_forms/uni_form.py,sha256=x71Ti_Rkb0WxXq_SkwltHMYX0BYKsoV-8DPivra0HHw,2699
82
+ ul_api_utils/resources/web_forms/custom_fields/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
+ ul_api_utils/resources/web_forms/custom_fields/custom_checkbox_select.py,sha256=M2k9qqSXSXjd3ajGMjK3dQwCFefNKf-AL89PSOU0GxU,158
84
+ ul_api_utils/resources/web_forms/custom_widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ ul_api_utils/resources/web_forms/custom_widgets/custom_select_widget.py,sha256=691DZl8znlcKvl03SflpZWcp_9Rs_4LvPLfjsqf2Tn4,3777
86
+ ul_api_utils/resources/web_forms/custom_widgets/custom_text_input_widget.py,sha256=Mn6MpFrZIHPjM-l1ytWC6yGiHsIdcfbJoZiTF9XIENc,1888
80
87
  ul_api_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
88
  ul_api_utils/utils/api_encoding.py,sha256=dqXknOKgc3HBOk48zDoVOLLwc1vRFATkBY8JW_RVLCo,1450
82
89
  ul_api_utils/utils/api_format.py,sha256=wGuk5QP6b7oyl9SJ1ebNVirzCipmyTxqjoEPh2cJzAY,2025
@@ -126,9 +133,9 @@ ul_api_utils/validators/validate_empty_object.py,sha256=3Ck_iwyJE_M5e7l6s1i88aqb
126
133
  ul_api_utils/validators/validate_uuid.py,sha256=EfvlRirv2EW0Z6w3s8E8rUa9GaI8qXZkBWhnPs8NFrA,257
127
134
  ul_api_utils/validators/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
128
135
  ul_api_utils/validators/__tests__/test_custom_fields.py,sha256=QLZ7DFta01Z7DOK9Z5Iq4uf_CmvDkVReis-GAl_QN48,1447
129
- ul_api_utils-7.7.10.dist-info/LICENSE,sha256=6Qo8OdcqI8aGrswJKJYhST-bYqxVQBQ3ujKdTSdq-80,1062
130
- ul_api_utils-7.7.10.dist-info/METADATA,sha256=K7S32IQ3Ick056nvnE5wSiVhw_WrGjKpAUmVayd4W8M,14455
131
- ul_api_utils-7.7.10.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
132
- ul_api_utils-7.7.10.dist-info/entry_points.txt,sha256=8tL3ySHWTyJMuV1hx1fHfN8zumDVOCOm63w3StphkXg,53
133
- ul_api_utils-7.7.10.dist-info/top_level.txt,sha256=1XsW8iOSFaH4LOzDcnNyxHpHrbKU3fSn-aIAxe04jmw,21
134
- ul_api_utils-7.7.10.dist-info/RECORD,,
136
+ ul_api_utils-7.8.0.dist-info/LICENSE,sha256=6Qo8OdcqI8aGrswJKJYhST-bYqxVQBQ3ujKdTSdq-80,1062
137
+ ul_api_utils-7.8.0.dist-info/METADATA,sha256=ye4_rHtf-C06Ltam7yrnIsVqoJ5IqMFZxpIQEh4xOfg,14454
138
+ ul_api_utils-7.8.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
139
+ ul_api_utils-7.8.0.dist-info/entry_points.txt,sha256=8tL3ySHWTyJMuV1hx1fHfN8zumDVOCOm63w3StphkXg,53
140
+ ul_api_utils-7.8.0.dist-info/top_level.txt,sha256=1XsW8iOSFaH4LOzDcnNyxHpHrbKU3fSn-aIAxe04jmw,21
141
+ ul_api_utils-7.8.0.dist-info/RECORD,,