Skip to content

Commit a716398

Browse files
committed
to_dict now support a "as_values" attribute
1 parent 17fe03a commit a716398

File tree

4 files changed

+355
-3
lines changed

4 files changed

+355
-3
lines changed

datamodel/converters.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ cdef object strtobool(str val):
302302
return False
303303
else:
304304
raise ValueError(
305-
f"Invalid truth value for {val}"
305+
f"Invalid truth value for **'{val}'**"
306306
)
307307

308308
cpdef object to_boolean(object obj):

datamodel/models.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,67 @@ def __convert_enums__(self, obj: Any) -> dict[str, Any]:
174174
return obj.value
175175
return obj
176176

177-
def to_dict(self, remove_nulls: bool = False, convert_enums: bool = False):
177+
def to_dict(
178+
self,
179+
remove_nulls: bool = False,
180+
convert_enums: bool = False,
181+
as_values: bool = False
182+
) -> dict[str, Any]:
183+
if as_values:
184+
return self.__collapse_as_values__(remove_nulls, convert_enums, as_values)
178185
d = as_dict(self, dict_factory=dict)
179186
if convert_enums:
180187
d = self.__convert_enums__(d)
181188
if self.Meta.remove_nulls is True or remove_nulls is True:
182189
return self.remove_nulls(d)
190+
# 4) If as_values => convert sub-models to pk-value
183191
return d
184192

193+
def __collapse_as_values__(
194+
self,
195+
remove_nulls: bool = False,
196+
convert_enums: bool = False,
197+
as_values: bool = False
198+
) -> dict[str, Any]:
199+
"""Recursively converts any BaseModel instances to their primary key value."""
200+
out = {}
201+
fields = self.columns()
202+
for name, field in fields.items():
203+
# datatype = field.type
204+
value = getattr(self, name)
205+
if value is None and remove_nulls:
206+
continue
207+
if isinstance(value, ModelMixin):
208+
if as_values:
209+
out[name] = getattr(value, name)
210+
else:
211+
out[name] = value.__collapse_as_values__(
212+
remove_nulls=remove_nulls,
213+
convert_enums=convert_enums,
214+
as_values=as_values
215+
)
216+
# if it's a list, might contain submodels or scalars
217+
elif isinstance(value, list):
218+
items_out = []
219+
for item in value:
220+
if isinstance(item, ModelMixin):
221+
if as_values:
222+
items_out.append(getattr(item, name))
223+
else:
224+
items_out.append(item.__collapse_as_values__(
225+
remove_nulls=remove_nulls,
226+
convert_enums=convert_enums,
227+
as_values=as_values
228+
))
229+
else:
230+
items_out.append(item)
231+
out[name] = items_out
232+
else:
233+
out[name] = value
234+
if convert_enums:
235+
out = self.__convert_enums__(out)
236+
return out
237+
185238
def json(self, **kwargs):
186239
encoder = self.__encoder__(**kwargs)
187240
return encoder(as_dict(self))

datamodel/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
__title__ = 'python-datamodel'
55
__description__ = ('simple library based on python +3.8 to use Dataclass-syntax'
66
'for interacting with Data')
7-
__version__ = '0.8.4'
7+
__version__ = '0.8.5'
88
__author__ = 'Jesus Lara'
99
__author_email__ = '[email protected]'
1010
__license__ = 'BSD'

examples/nn/examples.py

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
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+
}
23+
}
24+
"""
25+
26+
formid_payload = """
27+
{
28+
"metadata": {
29+
"type": "recapDefinition",
30+
"transactionType": "UPSERT",
31+
"source": "MainEvent",
32+
"client": "assembly"
33+
},
34+
"payload": {
35+
"formid": 7,
36+
"form_name": "Assembly Tech Form",
37+
"active": true,
38+
"created_on": "2024-09-24T07:13:20-05:00",
39+
"updated_on": "2024-09-25T12:51:53-05:00",
40+
"is_store_stamp": false,
41+
"client_id": 61,
42+
"client_name": "ASSEMBLY",
43+
"orgid": 71
44+
}
45+
}
46+
"""
47+
48+
metadata_payload = """
49+
{
50+
"metadata": {
51+
"type": "form_metadata",
52+
"transactionType": "UPSERT",
53+
"source": "MainEvent",
54+
"client": "assembly"
55+
},
56+
"payload": {
57+
"column_name": "9080",
58+
"description": "Were all the Kitchen Suite appliances installed correctly before leaving the store?",
59+
"is_active": true,
60+
"data_type": "FIELD_MULTISELECT",
61+
"formid": 10,
62+
"form_name": "Lowe's Call Form",
63+
"client_id": 57,
64+
"client_name": "HISENSE",
65+
"orgid": 106
66+
}
67+
}
68+
"""
69+
70+
user_payload = """
71+
{
72+
"metadata": {
73+
"type": "user",
74+
"transactionType": "UPSERT",
75+
"source": "MainEvent",
76+
"client": "global"
77+
},
78+
"payload": {
79+
"user_id": 2661,
80+
"first_name": "M",
81+
"last_name": "Rosado",
82+
"email": "[email protected]",
83+
"mobile_number": "237-222-3576",
84+
"address": "800 S. Douglas Rd",
85+
"city": "Coral Gables",
86+
"state_name": "FL",
87+
"zipcode": "33134",
88+
"latitude": 25.763887,
89+
"longitude": -80.2567,
90+
"username": "mrosado",
91+
"role_id": 1,
92+
"employee_number": 158,
93+
"physical_country": "USA",
94+
"role_name": "Global Admin",
95+
"is_active": true,
96+
"client_id": [
97+
61,
98+
54,
99+
55,
100+
56,
101+
60,
102+
57,
103+
58,
104+
62,
105+
59,
106+
65,
107+
63
108+
],
109+
"orgid": [
110+
71,
111+
138,
112+
74,
113+
69,
114+
60,
115+
106,
116+
137,
117+
62,
118+
77,
119+
3,
120+
63
121+
],
122+
"client_names": [
123+
"ASSEMBLY",
124+
"AT&T",
125+
"BOSE",
126+
"EPSON",
127+
"FLEX-ROC",
128+
"HISENSE",
129+
"POKEMON",
130+
"TCT MOBILE",
131+
"TRENDMICRO",
132+
"TRO MSO",
133+
"WORP"
134+
]
135+
}
136+
}
137+
"""
138+
139+
class Organization(BaseModel):
140+
orgid: int = Field(primary_key=True)
141+
org_name: str
142+
status: bool = Field(required=True, default=True)
143+
144+
class Meta:
145+
name: str = 'organizations'
146+
strict: bool = True
147+
148+
def create_organization(name: str, value: Any, target_type: Any):
149+
print('Organization Target: ', target_type, value, name)
150+
args = {
151+
name: value,
152+
"status": True,
153+
}
154+
return target_type(**args)
155+
156+
BaseModel.register_converter(Organization, create_organization, 'orgid')
157+
158+
class Client(BaseModel):
159+
client_id: int = Field(primary_key=True)
160+
client_name: str
161+
status: bool = Field(required=True, default=True)
162+
orgid: Organization = Field(required=False)
163+
164+
class Meta:
165+
name: str = 'clients'
166+
strict: bool = True
167+
168+
169+
class Form(BaseModel):
170+
formid: int = Field(primary_key=True)
171+
form_name: str = Field(required=True)
172+
active: bool = Field(required=False, default=True)
173+
created_on: datetime = Field(required=True)
174+
updated_on: datetime = Field(required=True)
175+
is_store_stamp: bool = Field(required=True)
176+
client_id: Client = Field(required=True)
177+
client_name: Client = Field(required=True)
178+
orgid: Organization = Field(required=True)
179+
180+
class Meta:
181+
name: str = 'forms'
182+
strict: bool = True
183+
184+
class FormMetadata(BaseModel):
185+
column_name: str = Field(required=True)
186+
description: str = Field(required=True)
187+
is_active: bool = Field(required=False, default=True)
188+
data_type: str = Field(required=True)
189+
formid: Form = Field(required=True)
190+
form_name: Form = Field(required=True)
191+
client_id: Client = Field(required=True)
192+
client_name: Client = Field(required=True)
193+
orgid: Organization = Field(required=True)
194+
195+
class Meta:
196+
name: str = 'form_metadata'
197+
strict: bool = True
198+
199+
class User(BaseModel):
200+
user_id: int = Field(primary_key=True)
201+
first_name: str = Field(required=True)
202+
last_name: str = Field(required=True)
203+
email: str = Field(required=True)
204+
mobile_number: str = Field(required=True)
205+
address: str = Field(required=True)
206+
city: str = Field(required=True)
207+
state_name: str = Field(required=True)
208+
zipcode: str = Field(required=True)
209+
latitude: float = Field(required=True)
210+
longitude: float = Field(required=True)
211+
username: str = Field(required=True)
212+
role_id: int = Field(required=True)
213+
employee_number: int = Field(required=True)
214+
physical_country: str = Field(required=True)
215+
role_name: str = Field(required=True)
216+
is_active: bool = Field(required=False, default=True)
217+
client_id: List[Client] = Field(required=True)
218+
orgid: List[Organization] = Field(required=True)
219+
client_names: List[str] = Field(required=True)
220+
221+
class Meta:
222+
name: str = 'users'
223+
strict: bool = True
224+
as_objects: bool = False
225+
226+
227+
network_ninja_map = {
228+
"client": Client,
229+
"organization": Organization,
230+
"recapDefinition": Form,
231+
"form_metadata": FormMetadata,
232+
"user": User
233+
}
234+
235+
def get_client():
236+
payload = json_decoder(client_payload)
237+
metadata = payload.get("metadata")
238+
payload = payload.get("payload")
239+
model = network_ninja_map.get(metadata.get("type"))
240+
try:
241+
client = model(**payload)
242+
except ValidationError as e:
243+
print(e)
244+
print(e.payload)
245+
print('CLIENT > ', client)
246+
return client
247+
248+
def get_formid():
249+
payload = json_decoder(formid_payload)
250+
metadata = payload.get("metadata")
251+
payload = payload.get("payload")
252+
model = network_ninja_map.get(metadata.get("type"))
253+
form = None
254+
try:
255+
form = model(**payload)
256+
except ValidationError as e:
257+
print(e)
258+
print(e.payload)
259+
if form:
260+
print('FORM ID > ', form)
261+
return form
262+
263+
def get_formmetadata():
264+
payload = json_decoder(metadata_payload)
265+
metadata = payload.get("metadata")
266+
payload = payload.get("payload")
267+
model = network_ninja_map.get(metadata.get("type"))
268+
form_metadata = None
269+
try:
270+
form_metadata = model(**payload)
271+
except ValidationError as e:
272+
print(e)
273+
print(e.payload)
274+
if form_metadata:
275+
print('FORM METADATA > ', form_metadata)
276+
return form_metadata
277+
278+
def get_user():
279+
payload = json_decoder(user_payload)
280+
metadata = payload.get("metadata")
281+
payload = payload.get("payload")
282+
model = network_ninja_map.get(metadata.get("type"))
283+
user = None
284+
try:
285+
user = model(**payload)
286+
except ValidationError as e:
287+
print(e)
288+
print(e.payload)
289+
if user:
290+
print('USER > ', user)
291+
return user
292+
293+
if __name__ == "__main__":
294+
client = get_client()
295+
# formid = get_formid()
296+
# form_metadata = get_formmetadata()
297+
# user = get_user()
298+
df = pd.DataFrame([client.to_dict(as_values=True)])
299+
print('DF = ', df)

0 commit comments

Comments
 (0)