python-datamodel 0.10.1__cp313-cp313-win32.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.
- datamodel/__init__.py +13 -0
- datamodel/abstract.py +383 -0
- datamodel/adaptive/__init__.py +0 -0
- datamodel/adaptive/models.py +598 -0
- datamodel/aliases/__init__.py +26 -0
- datamodel/base.py +180 -0
- datamodel/converters.c +43471 -0
- datamodel/converters.cp313-win32.pyd +0 -0
- datamodel/converters.html +17387 -0
- datamodel/converters.pyx +1489 -0
- datamodel/exceptions.c +13455 -0
- datamodel/exceptions.cp313-win32.pyd +0 -0
- datamodel/exceptions.html +1261 -0
- datamodel/exceptions.pxd +13 -0
- datamodel/exceptions.pyx +50 -0
- datamodel/fields.cp313-win32.pyd +0 -0
- datamodel/fields.cpp +17401 -0
- datamodel/fields.html +3912 -0
- datamodel/fields.pyx +309 -0
- datamodel/functions.cp313-win32.pyd +0 -0
- datamodel/functions.cpp +9068 -0
- datamodel/functions.html +1766 -0
- datamodel/functions.pxd +9 -0
- datamodel/functions.pyx +82 -0
- datamodel/jsonld/__init__.py +45 -0
- datamodel/jsonld/models.py +500 -0
- datamodel/libs/__init__.py +1 -0
- datamodel/libs/mapping.c +15067 -0
- datamodel/libs/mapping.cp313-win32.pyd +0 -0
- datamodel/libs/mapping.html +2618 -0
- datamodel/libs/mapping.pxd +11 -0
- datamodel/libs/mapping.pyx +135 -0
- datamodel/libs/mutables.py +127 -0
- datamodel/models.py +814 -0
- datamodel/parsers/__init__.py +0 -0
- datamodel/parsers/encoders.py +15 -0
- datamodel/parsers/json.cp313-win32.pyd +0 -0
- datamodel/parsers/json.cpp +17004 -0
- datamodel/parsers/json.html +3365 -0
- datamodel/parsers/json.pyx +250 -0
- datamodel/profiler.py +21 -0
- datamodel/py.typed +0 -0
- datamodel/rs_core/Cargo.toml +17 -0
- datamodel/rs_core/src/lib.rs +294 -0
- datamodel/rs_parsers/Cargo.toml +22 -0
- datamodel/rs_parsers/src/lib.rs +571 -0
- datamodel/rs_parsers.cp313-win32.pyd +0 -0
- datamodel/rs_validators/Cargo.toml +17 -0
- datamodel/rs_validators/src/lib.rs +0 -0
- datamodel/typedefs/__init__.py +9 -0
- datamodel/typedefs/singleton.c +9169 -0
- datamodel/typedefs/singleton.cp313-win32.pyd +0 -0
- datamodel/typedefs/singleton.html +629 -0
- datamodel/typedefs/singleton.pxd +9 -0
- datamodel/typedefs/singleton.pyx +24 -0
- datamodel/typedefs/types.c +11716 -0
- datamodel/typedefs/types.cp313-win32.pyd +0 -0
- datamodel/typedefs/types.html +732 -0
- datamodel/typedefs/types.pxd +11 -0
- datamodel/typedefs/types.pyx +39 -0
- datamodel/types.c +7165 -0
- datamodel/types.cp313-win32.pyd +0 -0
- datamodel/types.html +716 -0
- datamodel/types.pyx +100 -0
- datamodel/validation.cp313-win32.pyd +0 -0
- datamodel/validation.cpp +17085 -0
- datamodel/validation.html +4769 -0
- datamodel/validation.pyx +315 -0
- datamodel/version.py +13 -0
- examples/nn/examples.py +311 -0
- examples/nn/stores.py +151 -0
- examples/tests/sp_types.py +294 -0
- examples/tests/speed_dates.py +26 -0
- python_datamodel-0.10.1.dist-info/LICENSE +29 -0
- python_datamodel-0.10.1.dist-info/METADATA +320 -0
- python_datamodel-0.10.1.dist-info/RECORD +78 -0
- python_datamodel-0.10.1.dist-info/WHEEL +5 -0
- python_datamodel-0.10.1.dist-info/top_level.txt +7 -0
datamodel/validation.pyx
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# cython: language_level=3, embedsignature=True, initializedcheck=False
|
|
2
|
+
# Copyright (C) 2018-present Jesus Lara
|
|
3
|
+
#
|
|
4
|
+
from typing import get_args, get_origin, Union, Optional
|
|
5
|
+
from collections.abc import Callable, Awaitable
|
|
6
|
+
import typing
|
|
7
|
+
import asyncio
|
|
8
|
+
import inspect
|
|
9
|
+
from libcpp cimport bool as bool_t
|
|
10
|
+
from cpython.object cimport PyObject_IsInstance, PyObject_IsSubclass
|
|
11
|
+
from cpython.type cimport PyType_Check
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from decimal import Decimal
|
|
14
|
+
import datetime
|
|
15
|
+
from uuid import UUID
|
|
16
|
+
import asyncpg.pgproto.pgproto as pgproto
|
|
17
|
+
from .types import uint64_min, uint64_max, Text
|
|
18
|
+
from .fields import Field
|
|
19
|
+
from .functions import (
|
|
20
|
+
is_iterable,
|
|
21
|
+
is_primitive,
|
|
22
|
+
is_dataclass,
|
|
23
|
+
is_function,
|
|
24
|
+
is_callable,
|
|
25
|
+
is_empty
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
cdef str valid_int(object field, str name, object value, object _type):
|
|
30
|
+
# Basic check for integer type.
|
|
31
|
+
if not isinstance(value, int):
|
|
32
|
+
return f"Field {name} expected an integer, got {value!r} of type {type(value).__name__}"
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
cdef str valid_float(object field, str name, object value, object _type):
|
|
36
|
+
if not isinstance(value, (float, int)): # sometimes ints are allowed
|
|
37
|
+
return f"Field {name} expected a float, got {value!r} of type {type(value).__name__}"
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
cdef str valid_str(object field, str name, object value, object _type):
|
|
41
|
+
if not isinstance(value, str):
|
|
42
|
+
return f"Field {name} expected a string, got {value!r} of type {type(value).__name__}"
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
cdef str valid_uuid(object field, str name, object value, object _type):
|
|
46
|
+
if not isinstance(value, (UUID, pgproto.UUID)):
|
|
47
|
+
return f"Field {name} expected a UUID, got {value!r} of type {type(value).__name__}"
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
cdef str valid_boolean(object field, str name, object value, object _type):
|
|
51
|
+
if not isinstance(value, bool):
|
|
52
|
+
return f"Field {name} expected a boolean, got {value!r} of type {type(value).__name__}"
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
cdef str valid_date(object field, str name, object value, object _type):
|
|
56
|
+
if not isinstance(value, datetime.date):
|
|
57
|
+
return f"Field {name} expected a date, got {value!r} of type {type(value).__name__}"
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
cdef str valid_datetime(object field, str name, object value, object _type):
|
|
61
|
+
if isinstance(value, datetime.datetime):
|
|
62
|
+
return None
|
|
63
|
+
if isinstance(value, datetime.date):
|
|
64
|
+
return None
|
|
65
|
+
return f"Field {name} expected a datetime, got {value!r} of type {type(value).__name__}"
|
|
66
|
+
|
|
67
|
+
cdef str valid_timedelta(object field, str name, object value, object _type):
|
|
68
|
+
if not isinstance(value, datetime.timedelta):
|
|
69
|
+
return f"Field {name} expected a timedelta, got {value!r} of type {type(value).__name__}"
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
cdef str valid_time(object field, str name, object value, object _type):
|
|
73
|
+
if not isinstance(value, datetime.time):
|
|
74
|
+
return f"Field {name} expected a time, got {value!r} of type {type(value).__name__}"
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
cdef str valid_decimal(object field, str name, object value, object _type):
|
|
78
|
+
if not isinstance(value, Decimal):
|
|
79
|
+
return f"Field {name} expected a Decimal, got {value!r} of type {type(value).__name__}"
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# List of Validators (primitive types):
|
|
84
|
+
validators = {
|
|
85
|
+
str: valid_str,
|
|
86
|
+
int: valid_int,
|
|
87
|
+
float: valid_float,
|
|
88
|
+
UUID: valid_uuid,
|
|
89
|
+
pgproto.UUID: valid_uuid,
|
|
90
|
+
bool: valid_boolean,
|
|
91
|
+
datetime.date: valid_date,
|
|
92
|
+
datetime.datetime: valid_datetime,
|
|
93
|
+
datetime.timedelta: valid_timedelta,
|
|
94
|
+
datetime.time: valid_time,
|
|
95
|
+
Decimal: valid_decimal,
|
|
96
|
+
Text: valid_str
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
cdef inline bint is_enum_class(object annotated_type):
|
|
101
|
+
cdef int res
|
|
102
|
+
# First, check if annotated_type is a type
|
|
103
|
+
if not PyType_Check(annotated_type):
|
|
104
|
+
return False
|
|
105
|
+
# Then check if it is a subclass of Enum.
|
|
106
|
+
res = PyObject_IsSubclass(annotated_type, <object>Enum)
|
|
107
|
+
if res < 0:
|
|
108
|
+
# If an error occurred, you might want to raise an exception.
|
|
109
|
+
raise RuntimeError("Error in PyObject_IsSubclass")
|
|
110
|
+
return res != 0
|
|
111
|
+
|
|
112
|
+
cdef bool_t is_instanceof(object value, type annotated_type):
|
|
113
|
+
if annotated_type.__module__ == 'typing':
|
|
114
|
+
return True # TODO: validate subscripted generic (typing extensions)
|
|
115
|
+
elif value in (datetime.date, datetime.time, datetime.datetime):
|
|
116
|
+
# check if is a pendulum instance:
|
|
117
|
+
return issubclass(value, (datetime.date, datetime.time, datetime.datetime))
|
|
118
|
+
else:
|
|
119
|
+
try:
|
|
120
|
+
return isinstance(value, annotated_type)
|
|
121
|
+
except (AttributeError, TypeError, ValueError) as e:
|
|
122
|
+
raise TypeError(
|
|
123
|
+
f"{e}"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
cpdef bool_t is_optional_type(object annotated_type):
|
|
127
|
+
if get_origin(annotated_type) is Union:
|
|
128
|
+
return type(None) in get_args(annotated_type)
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
cdef object get_primary_key_field(object annotated_type, str name, object field_meta):
|
|
132
|
+
for f in annotated_type.__dataclass_fields__.values():
|
|
133
|
+
if name == f.name:
|
|
134
|
+
return f
|
|
135
|
+
if field_meta.get('alias') == f.name:
|
|
136
|
+
return f
|
|
137
|
+
if f.metadata.get('primary_key', False):
|
|
138
|
+
return f
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
cpdef dict _validation(
|
|
142
|
+
object F,
|
|
143
|
+
str name,
|
|
144
|
+
object value,
|
|
145
|
+
object annotated_type,
|
|
146
|
+
object val_type,
|
|
147
|
+
str field_type,
|
|
148
|
+
bint as_objects=False
|
|
149
|
+
):
|
|
150
|
+
cdef bint _valid = False
|
|
151
|
+
cdef object field_meta = F.metadata
|
|
152
|
+
cdef dict error = {}
|
|
153
|
+
|
|
154
|
+
if not annotated_type:
|
|
155
|
+
annotated_type = F.type
|
|
156
|
+
elif isinstance(annotated_type, Field):
|
|
157
|
+
annotated_type = annotated_type.type
|
|
158
|
+
|
|
159
|
+
if fn := F.metadata.get('validator', None):
|
|
160
|
+
try:
|
|
161
|
+
result = fn(F, value, annotated_type, val_type)
|
|
162
|
+
if result is False:
|
|
163
|
+
msg = f"Validation failed for *{name}*: {value} with result: {result}"
|
|
164
|
+
return _create_error(name, value, msg, val_type, annotated_type)
|
|
165
|
+
except ValueError:
|
|
166
|
+
raise
|
|
167
|
+
# check: data type hint
|
|
168
|
+
# If field_type is known, short-circuit certain checks
|
|
169
|
+
if F.type == Text:
|
|
170
|
+
if val_type != str:
|
|
171
|
+
return _create_error(name, value, f'invalid type for {annotated_type}.{name}, expected {annotated_type}', val_type, annotated_type)
|
|
172
|
+
return {}
|
|
173
|
+
# if inspect.isclass(annotated_type) and issubclass(annotated_type, Enum):
|
|
174
|
+
if is_enum_class(annotated_type):
|
|
175
|
+
return validate_enum(name, value, annotated_type, val_type)
|
|
176
|
+
if F.origin is Callable:
|
|
177
|
+
if not is_callable(value):
|
|
178
|
+
return _create_error(name, value, f'Invalid function type, expected {annotated_type}', val_type, annotated_type)
|
|
179
|
+
return {}
|
|
180
|
+
elif F.origin is Awaitable:
|
|
181
|
+
if asyncio.iscoroutinefunction(value):
|
|
182
|
+
return _create_error(name, value, f"Field '{name}': provided coroutine function is not awaitable; call it to obtain a coroutine object.", val_type, annotated_type)
|
|
183
|
+
# Otherwise, check if it is awaitable
|
|
184
|
+
elif not hasattr(value, '__await__'):
|
|
185
|
+
return _create_error(name, value, f"Field '{name}': provided object is not awaitable; it does not have an '__await__' method. but got {type(value)}.", val_type, annotated_type)
|
|
186
|
+
return {}
|
|
187
|
+
elif field_type == 'type':
|
|
188
|
+
return validate_type(F, name, value, annotated_type, val_type)
|
|
189
|
+
elif field_type == 'typing' or hasattr(annotated_type, '__module__') and annotated_type.__module__ == 'typing':
|
|
190
|
+
if F.origin is tuple:
|
|
191
|
+
# Check if we are in the homogeneous case: Tuple[T, ...]
|
|
192
|
+
if len(F.args) == 2 and F.args[1] is Ellipsis:
|
|
193
|
+
for i, elem in enumerate(value):
|
|
194
|
+
if not isinstance(elem, F.args[0]):
|
|
195
|
+
return _create_error(
|
|
196
|
+
f"{name}[{i}]",
|
|
197
|
+
elem,
|
|
198
|
+
f"Invalid type at index {i}: expected {F.args[0]}",
|
|
199
|
+
type(elem), F.args[0]
|
|
200
|
+
)
|
|
201
|
+
else:
|
|
202
|
+
if len(value) != len(F.args):
|
|
203
|
+
return _create_error(name, value, f"Invalid length for {annotated_type}.{name}, expected {len(F.args)} elements", val_type, annotated_type)
|
|
204
|
+
else:
|
|
205
|
+
for i, elem in enumerate(value):
|
|
206
|
+
if not isinstance(elem, F.args[i]):
|
|
207
|
+
return _create_error(
|
|
208
|
+
f"{name}[{i}]",
|
|
209
|
+
elem,
|
|
210
|
+
f"Invalid type at index {i}: expected {F.args[i]}",
|
|
211
|
+
type(elem), F.args[i]
|
|
212
|
+
)
|
|
213
|
+
# Handle Optional Types:
|
|
214
|
+
elif F.origin is Union and type(None) in F.args:
|
|
215
|
+
inner_types = [t for t in F.args if t is not type(None)]
|
|
216
|
+
# If value is None then that is valid:
|
|
217
|
+
if value is None:
|
|
218
|
+
return {}
|
|
219
|
+
# Otherwise check that value is an instance of at least one inner type:
|
|
220
|
+
for t in inner_types:
|
|
221
|
+
base_type = get_origin(t) or t
|
|
222
|
+
if isinstance(value, base_type):
|
|
223
|
+
_valid = True
|
|
224
|
+
break
|
|
225
|
+
if not _valid:
|
|
226
|
+
return _create_error(name, value, f"Invalid type for {annotated_type}.{name}, expected one of {inner_types}", val_type, annotated_type)
|
|
227
|
+
elif type(annotated_type).__name__ == "ModelMeta":
|
|
228
|
+
# Check if there's a field in the annotated type that matches the name and type
|
|
229
|
+
if as_objects:
|
|
230
|
+
if isinstance(value, annotated_type):
|
|
231
|
+
# if value is already a Object, no further check needed for columns
|
|
232
|
+
return {}
|
|
233
|
+
try:
|
|
234
|
+
field = annotated_type.get_column(name)
|
|
235
|
+
except AttributeError as e:
|
|
236
|
+
return _create_error(name, value, f'{annotated_type} has no column {name}', val_type, annotated_type, e)
|
|
237
|
+
ftype = field.type
|
|
238
|
+
if ftype <> val_type:
|
|
239
|
+
return _create_error(name, value, f"Invalid type for {annotated_type}.{name}, expected {ftype}", val_type, annotated_type)
|
|
240
|
+
else:
|
|
241
|
+
# Validate primary key
|
|
242
|
+
pk_field = get_primary_key_field(annotated_type, name, field_meta)
|
|
243
|
+
if pk_field:
|
|
244
|
+
pk_type = pk_field.type
|
|
245
|
+
if not isinstance(value, pk_type):
|
|
246
|
+
return _create_error(
|
|
247
|
+
name,
|
|
248
|
+
value,
|
|
249
|
+
f"Invalid type for {annotated_type}.{pk_field.name}, expected {pk_type}",
|
|
250
|
+
val_type,
|
|
251
|
+
pk_type
|
|
252
|
+
)
|
|
253
|
+
elif is_optional_type(annotated_type):
|
|
254
|
+
inner_types = get_args(annotated_type)
|
|
255
|
+
for t in inner_types:
|
|
256
|
+
if t is type(None) and value is None:
|
|
257
|
+
break
|
|
258
|
+
elif is_instanceof(val_type, t):
|
|
259
|
+
break
|
|
260
|
+
elif val_type == t:
|
|
261
|
+
break
|
|
262
|
+
else:
|
|
263
|
+
return _create_error(name, value, f"Invalid type for {annotated_type}.{name}, expected one of {inner_types}", val_type, annotated_type)
|
|
264
|
+
elif val_type != annotated_type:
|
|
265
|
+
instance = is_instanceof(value, annotated_type)
|
|
266
|
+
if not instance:
|
|
267
|
+
return _create_error(name, value, f"Invalid Instance for {annotated_type}.{name}, expected {annotated_type}", val_type, annotated_type)
|
|
268
|
+
return error
|
|
269
|
+
|
|
270
|
+
cdef dict _create_error(str name, object value, object error, object val_type, object annotated_type, object exception = None):
|
|
271
|
+
return {
|
|
272
|
+
"field": name,
|
|
273
|
+
"value": value,
|
|
274
|
+
"error": error,
|
|
275
|
+
"value_type": val_type,
|
|
276
|
+
"annotation": annotated_type,
|
|
277
|
+
"exception": exception
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
# Define a validator function for uint64
|
|
281
|
+
def validate_uint64(value: int) -> None:
|
|
282
|
+
"""Validate uint64 values.
|
|
283
|
+
"""
|
|
284
|
+
if value < uint64_min or value > uint64_max:
|
|
285
|
+
raise ValueError(f"{value} is not a valid uint64")
|
|
286
|
+
|
|
287
|
+
cdef dict validate_type(object F, object name, object value, object annotated_type, object val_type):
|
|
288
|
+
"""
|
|
289
|
+
Validate that 'value' is of type 'annotated_type'.
|
|
290
|
+
If 'annotated_type' is a Union, checks if 'value' is an instance of any of the union types.
|
|
291
|
+
Returns None if valid; otherwise, returns an error object via _create_error.
|
|
292
|
+
"""
|
|
293
|
+
if not isinstance(value, type):
|
|
294
|
+
return _create_error(name, value, f'Invalid type for {annotated_type}.{name}, expected a type', val_type, annotated_type)
|
|
295
|
+
inner_types = get_args(F.args[0])
|
|
296
|
+
for allowed in inner_types:
|
|
297
|
+
if value is allowed:
|
|
298
|
+
return {}
|
|
299
|
+
expected = ', '.join([str(t) for t in F.args])
|
|
300
|
+
return _create_error(name, value, f'Invalid type for {annotated_type}.{name}, expected a type of {expected}', val_type, annotated_type)
|
|
301
|
+
|
|
302
|
+
cdef dict validate_enum(object name, object value, object annotated_type, object val_type):
|
|
303
|
+
"""
|
|
304
|
+
Validate that 'value' is a valid member of the enum 'annotated_type'.
|
|
305
|
+
If 'value' is an instance of 'annotated_type', use its .value.
|
|
306
|
+
Cache the allowed enum values on the enum type to avoid repeated introspection.
|
|
307
|
+
Returns None if valid; otherwise, returns an error object via _create_error.
|
|
308
|
+
"""
|
|
309
|
+
cdef object val = value.value if PyObject_IsInstance(value, annotated_type) else value
|
|
310
|
+
cdef object enum_values = [e.value for e in annotated_type]
|
|
311
|
+
if val not in enum_values:
|
|
312
|
+
return _create_error(
|
|
313
|
+
name, value, f"Invalid value for {annotated_type}.{name}, expected one of {enum_values}", val_type, annotated_type
|
|
314
|
+
)
|
|
315
|
+
return {}
|
datamodel/version.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""DataModel Meta information."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
__title__ = 'python-datamodel'
|
|
5
|
+
__description__ = (
|
|
6
|
+
'simple library based on python +3.8 to use Dataclass-syntax'
|
|
7
|
+
'for interacting with Data'
|
|
8
|
+
)
|
|
9
|
+
__version__ = '0.10.1'
|
|
10
|
+
__copyright__ = 'Copyright (c) 2020-2024 Jesus Lara'
|
|
11
|
+
__author__ = 'Jesus Lara'
|
|
12
|
+
__author_email__ = 'jesuslarag@gmail.com'
|
|
13
|
+
__license__ = 'BSD'
|
examples/nn/examples.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
from typing import List, Any
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from datamodel import BaseModel, Field
|
|
5
|
+
from datamodel.parsers.json import json_decoder
|
|
6
|
+
from datamodel.exceptions import ValidationError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
client_payload = """
|
|
10
|
+
{
|
|
11
|
+
"metadata": {
|
|
12
|
+
"type": "client",
|
|
13
|
+
"transactionType": "UPSERT",
|
|
14
|
+
"source": "MainEvent",
|
|
15
|
+
"client": "global"
|
|
16
|
+
},
|
|
17
|
+
"payload": {
|
|
18
|
+
"client_id": 61,
|
|
19
|
+
"client_name": "ASSEMBLY",
|
|
20
|
+
"status": true,
|
|
21
|
+
"orgid": 71,
|
|
22
|
+
"org_name": "assembly"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
formid_payload = """
|
|
28
|
+
{
|
|
29
|
+
"metadata": {
|
|
30
|
+
"type": "recapDefinition",
|
|
31
|
+
"transactionType": "UPSERT",
|
|
32
|
+
"source": "MainEvent",
|
|
33
|
+
"client": "assembly"
|
|
34
|
+
},
|
|
35
|
+
"payload": {
|
|
36
|
+
"formid": 7,
|
|
37
|
+
"form_name": "Assembly Tech Form",
|
|
38
|
+
"active": true,
|
|
39
|
+
"created_on": "2024-09-24T07:13:20-05:00",
|
|
40
|
+
"updated_on": "2024-09-25T12:51:53-05:00",
|
|
41
|
+
"is_store_stamp": false,
|
|
42
|
+
"client_id": 61,
|
|
43
|
+
"client_name": "ASSEMBLY",
|
|
44
|
+
"orgid": 71
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
metadata_payload = """
|
|
50
|
+
{
|
|
51
|
+
"metadata": {
|
|
52
|
+
"type": "form_metadata",
|
|
53
|
+
"transactionType": "UPSERT",
|
|
54
|
+
"source": "MainEvent",
|
|
55
|
+
"client": "assembly"
|
|
56
|
+
},
|
|
57
|
+
"payload": {
|
|
58
|
+
"column_name": "9080",
|
|
59
|
+
"description": "Were all the Kitchen Suite appliances installed correctly before leaving the store?",
|
|
60
|
+
"is_active": true,
|
|
61
|
+
"data_type": "FIELD_MULTISELECT",
|
|
62
|
+
"formid": 10,
|
|
63
|
+
"form_name": "Lowe's Call Form",
|
|
64
|
+
"client_id": 57,
|
|
65
|
+
"client_name": "HISENSE",
|
|
66
|
+
"orgid": 106
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
user_payload = """
|
|
72
|
+
{
|
|
73
|
+
"metadata": {
|
|
74
|
+
"type": "user",
|
|
75
|
+
"transactionType": "UPSERT",
|
|
76
|
+
"source": "MainEvent",
|
|
77
|
+
"client": "global"
|
|
78
|
+
},
|
|
79
|
+
"payload": {
|
|
80
|
+
"user_id": 2661,
|
|
81
|
+
"first_name": "M",
|
|
82
|
+
"last_name": "Rosado",
|
|
83
|
+
"email": "mrosado@trocglobal.com",
|
|
84
|
+
"mobile_number": "237-222-3576",
|
|
85
|
+
"address": "800 S. Douglas Rd",
|
|
86
|
+
"city": "Coral Gables",
|
|
87
|
+
"state_name": "FL",
|
|
88
|
+
"zipcode": "33134",
|
|
89
|
+
"latitude": 25.763887,
|
|
90
|
+
"longitude": -80.2567,
|
|
91
|
+
"username": "mrosado",
|
|
92
|
+
"role_id": 1,
|
|
93
|
+
"employee_number": 158,
|
|
94
|
+
"physical_country": "USA",
|
|
95
|
+
"role_name": "Global Admin",
|
|
96
|
+
"is_active": true,
|
|
97
|
+
"org_name": "assembly",
|
|
98
|
+
"client_id": [
|
|
99
|
+
61,
|
|
100
|
+
54,
|
|
101
|
+
55,
|
|
102
|
+
56,
|
|
103
|
+
60,
|
|
104
|
+
57,
|
|
105
|
+
58,
|
|
106
|
+
62,
|
|
107
|
+
59,
|
|
108
|
+
65,
|
|
109
|
+
63
|
|
110
|
+
],
|
|
111
|
+
"orgid": [
|
|
112
|
+
71,
|
|
113
|
+
138,
|
|
114
|
+
74,
|
|
115
|
+
69,
|
|
116
|
+
60,
|
|
117
|
+
106,
|
|
118
|
+
137,
|
|
119
|
+
62,
|
|
120
|
+
77,
|
|
121
|
+
3,
|
|
122
|
+
63
|
|
123
|
+
],
|
|
124
|
+
"client_names": [
|
|
125
|
+
"ASSEMBLY",
|
|
126
|
+
"AT&T",
|
|
127
|
+
"BOSE",
|
|
128
|
+
"EPSON",
|
|
129
|
+
"FLEX-ROC",
|
|
130
|
+
"HISENSE",
|
|
131
|
+
"POKEMON",
|
|
132
|
+
"TCT MOBILE",
|
|
133
|
+
"TRENDMICRO",
|
|
134
|
+
"TRO MSO",
|
|
135
|
+
"WORP"
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
class Organization(BaseModel):
|
|
142
|
+
orgid: int = Field(primary_key=True)
|
|
143
|
+
org_name: str
|
|
144
|
+
status: bool = Field(required=True, default=True)
|
|
145
|
+
|
|
146
|
+
class Meta:
|
|
147
|
+
name: str = 'organizations'
|
|
148
|
+
strict: bool = True
|
|
149
|
+
|
|
150
|
+
def create_organization(
|
|
151
|
+
name: str,
|
|
152
|
+
value: Any,
|
|
153
|
+
obj: Any,
|
|
154
|
+
parent_data: BaseModel
|
|
155
|
+
) -> Organization:
|
|
156
|
+
org_name = parent_data.get('org_name', None) if parent_data else None
|
|
157
|
+
print('Creating organization')
|
|
158
|
+
args = {
|
|
159
|
+
name: value,
|
|
160
|
+
"org_name": org_name,
|
|
161
|
+
"status": True,
|
|
162
|
+
}
|
|
163
|
+
return obj(**args)
|
|
164
|
+
|
|
165
|
+
BaseModel.register_parser(Organization, create_organization, 'orgid')
|
|
166
|
+
|
|
167
|
+
class Client(BaseModel):
|
|
168
|
+
client_id: int = Field(primary_key=True)
|
|
169
|
+
client_name: str
|
|
170
|
+
status: bool = Field(required=True, default=True)
|
|
171
|
+
orgid: Organization = Field(required=False)
|
|
172
|
+
org_name: str
|
|
173
|
+
|
|
174
|
+
class Meta:
|
|
175
|
+
name: str = 'clients'
|
|
176
|
+
strict: bool = True
|
|
177
|
+
as_objects: bool = True
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class Form(BaseModel):
|
|
181
|
+
formid: int = Field(primary_key=True)
|
|
182
|
+
form_name: str = Field(required=True)
|
|
183
|
+
active: bool = Field(required=False, default=True)
|
|
184
|
+
created_on: datetime = Field(required=True)
|
|
185
|
+
updated_on: datetime = Field(required=True)
|
|
186
|
+
is_store_stamp: bool = Field(required=True)
|
|
187
|
+
client_id: Client = Field(required=True)
|
|
188
|
+
client_name: Client = Field(required=True)
|
|
189
|
+
orgid: Organization = Field(required=True)
|
|
190
|
+
|
|
191
|
+
class Meta:
|
|
192
|
+
name: str = 'forms'
|
|
193
|
+
strict: bool = True
|
|
194
|
+
|
|
195
|
+
class FormMetadata(BaseModel):
|
|
196
|
+
column_name: str = Field(required=True)
|
|
197
|
+
description: str = Field(required=True)
|
|
198
|
+
is_active: bool = Field(required=False, default=True)
|
|
199
|
+
data_type: str = Field(required=True)
|
|
200
|
+
formid: Form = Field(required=True)
|
|
201
|
+
form_name: Form = Field(required=True)
|
|
202
|
+
client_id: Client = Field(required=True)
|
|
203
|
+
client_name: Client = Field(required=True)
|
|
204
|
+
orgid: Organization = Field(required=True)
|
|
205
|
+
|
|
206
|
+
class Meta:
|
|
207
|
+
name: str = 'form_metadata'
|
|
208
|
+
strict: bool = True
|
|
209
|
+
|
|
210
|
+
class User(BaseModel):
|
|
211
|
+
user_id: int = Field(primary_key=True)
|
|
212
|
+
first_name: str = Field(required=True)
|
|
213
|
+
last_name: str = Field(required=True)
|
|
214
|
+
email: str = Field(required=True)
|
|
215
|
+
mobile_number: str = Field(required=True)
|
|
216
|
+
address: str = Field(required=True)
|
|
217
|
+
city: str = Field(required=True)
|
|
218
|
+
state_name: str = Field(required=True)
|
|
219
|
+
zipcode: str = Field(required=True)
|
|
220
|
+
latitude: float = Field(required=True)
|
|
221
|
+
longitude: float = Field(required=True)
|
|
222
|
+
username: str = Field(required=True)
|
|
223
|
+
role_id: int = Field(required=True)
|
|
224
|
+
employee_number: int = Field(required=True)
|
|
225
|
+
physical_country: str = Field(required=True)
|
|
226
|
+
role_name: str = Field(required=True)
|
|
227
|
+
is_active: bool = Field(required=False, default=True)
|
|
228
|
+
client_id: List[Client] = Field(required=True)
|
|
229
|
+
orgid: List[Organization] = Field(required=True)
|
|
230
|
+
org_name: str
|
|
231
|
+
client_names: List[str] = Field(required=True)
|
|
232
|
+
|
|
233
|
+
class Meta:
|
|
234
|
+
name: str = 'users'
|
|
235
|
+
strict: bool = True
|
|
236
|
+
as_objects: bool = False
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
network_ninja_map = {
|
|
240
|
+
"client": Client,
|
|
241
|
+
"organization": Organization,
|
|
242
|
+
"recapDefinition": Form,
|
|
243
|
+
"form_metadata": FormMetadata,
|
|
244
|
+
"user": User
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
def get_client():
|
|
248
|
+
payload = json_decoder(client_payload)
|
|
249
|
+
metadata = payload.get("metadata")
|
|
250
|
+
payload = payload.get("payload")
|
|
251
|
+
model = network_ninja_map.get(metadata.get("type"))
|
|
252
|
+
try:
|
|
253
|
+
client = model(**payload)
|
|
254
|
+
except ValidationError as e:
|
|
255
|
+
print(e)
|
|
256
|
+
print(e.payload)
|
|
257
|
+
print('CLIENT > ', client)
|
|
258
|
+
return client
|
|
259
|
+
|
|
260
|
+
def get_formid():
|
|
261
|
+
payload = json_decoder(formid_payload)
|
|
262
|
+
metadata = payload.get("metadata")
|
|
263
|
+
payload = payload.get("payload")
|
|
264
|
+
model = network_ninja_map.get(metadata.get("type"))
|
|
265
|
+
form = None
|
|
266
|
+
try:
|
|
267
|
+
form = model(**payload)
|
|
268
|
+
except ValidationError as e:
|
|
269
|
+
print(e)
|
|
270
|
+
print(e.payload)
|
|
271
|
+
if form:
|
|
272
|
+
print('FORM ID > ', form)
|
|
273
|
+
return form
|
|
274
|
+
|
|
275
|
+
def get_formmetadata():
|
|
276
|
+
payload = json_decoder(metadata_payload)
|
|
277
|
+
metadata = payload.get("metadata")
|
|
278
|
+
payload = payload.get("payload")
|
|
279
|
+
model = network_ninja_map.get(metadata.get("type"))
|
|
280
|
+
form_metadata = None
|
|
281
|
+
try:
|
|
282
|
+
form_metadata = model(**payload)
|
|
283
|
+
except ValidationError as e:
|
|
284
|
+
print(e)
|
|
285
|
+
print(e.payload)
|
|
286
|
+
if form_metadata:
|
|
287
|
+
print('FORM METADATA > ', form_metadata)
|
|
288
|
+
return form_metadata
|
|
289
|
+
|
|
290
|
+
def get_user():
|
|
291
|
+
payload = json_decoder(user_payload)
|
|
292
|
+
metadata = payload.get("metadata")
|
|
293
|
+
payload = payload.get("payload")
|
|
294
|
+
model = network_ninja_map.get(metadata.get("type"))
|
|
295
|
+
user = None
|
|
296
|
+
try:
|
|
297
|
+
user = model(**payload)
|
|
298
|
+
except ValidationError as e:
|
|
299
|
+
print(e)
|
|
300
|
+
print(e.payload)
|
|
301
|
+
if user:
|
|
302
|
+
print('USER > ', user)
|
|
303
|
+
return user
|
|
304
|
+
|
|
305
|
+
if __name__ == "__main__":
|
|
306
|
+
# client = get_client()
|
|
307
|
+
# formid = get_formid()
|
|
308
|
+
# form_metadata = get_formmetadata()
|
|
309
|
+
user = get_user()
|
|
310
|
+
# df = pd.DataFrame([client.to_dict(as_values=True)])
|
|
311
|
+
# print('DF = ', df)
|