How do i append to EmbeddedDocumentListField in MongoEngine Flask? - python

Im trying to append additional info to an existing list but i received an error message instead.
Error: 4.Invalid embedded document instance provided to an
EmbeddedDocumentField: ['family']
class Family(db.EmbeddedDocument):
name = db.StringField()
# gender = db.StringField()
class House(db.Document):
house_id = db.IntField(required=True, unique=True)
housingType = db.StringField(required=True)
family = db.EmbeddedDocumentListField(Family)
def to_json(self):
return {
"house_id": self.house_id,
"housingType": self.housingType,
"family_members": self.family
}
#app.route('/api/add_family/<h_id>', methods=['POST'])
def add_family(h_id):
content = request.json
h = House.objects(house_id=h_id).get()
h.family.append(content['family'])
h.save()
return make_response("Added family member successfully", 201)
What im trying to achieve is as follows:
Current data:
{
'house_id': 1,
'family': [{'name': 'John', 'Gender': 'Male'}]
}
After appending, it should look like this:
{
'house_id': 1,
'family': [{'name': 'John, 'Gender': 'Male'}, {'name': 'Peter', 'Gender': 'Male'}]
}

Here is my solution. Hopefully it helps.
#app.route('/api/add_family/<h_id>', methods=['POST'])
def add_family(h_id):
'''
family member is added only if its not already in the database
'''
edited = False
content = request.json
h = House.objects.get(house_id=h_id).to_json()
h = json.loads(h)
family_arr = h['family']
if family_arr:
# family_arr not empty
count = family_arr[-1].get('id') + 1
else:
count = 1
for new_name in content['family']:
if not dup_name_check(family_arr, new_name['name']):
new_name.update({'id': count})
family_arr.append(new_name)
count += 1
edited = True
if edited:
House.objects.get(house_id=h_id).update(family=family_arr)
return make_response(f"Successfully added family member in House ID:{h_id}", 201)
else:
return make_response(f"Duplicated entries detected!", 400)

Related

loosing dict content as soon as am out of loop in python

Team: need some assistance..
sub: loosing dict content as soon as am out of loop. dict is populated with loop vars that are added to dict using subscript approach.
below foo() is always getting executed because the team_oncall_dict is empty outside. any hint how can I retain it as it was inside loop?
def askduty_oncall(self, *args):
session = APISession(PD_API_KEY, default_from=PD_USER_EMAIL)
total = 1 #true or false
limit = 40
teamteamnm = "Team Test Team"
team_esp_name = “Team Test Escalation Policy"
teamteamid = ""
teamesplcyid = ""
team_oncall_dict = {}
if args:
offset = args[0]
total_teams = args[1]
if offset <= total_teams:
print("\nfunc with args with new offset {} called\n".format(offset))
teams = session.get('/teams?limit={0}&total={1}&offset={2}'.format(limit,total,offset))
else:
print("Reached max teams, no more team records to pull")
return
else:
print("\nFunc with no args called, hence pull first set of {} teams as defined by limit var\n".format(limit))
teams = session.get('/teams?limit={0}&total={1}'.format(limit,total))
if not teams.ok:
return
else:
tj = teams.json()
tjd = tj['teams']
for adict in tjd:
if not adict['name'] == teamteamnm:
continue
elif adict['name'] == teamteamnm:
teamteamid = adict['id']
print("\nFound team..\nFetched",adict['name'], "id: {0}".format(teamteamid))
print("Pull escalation policy for team '{}':'{}'".format(teamteamnm,teamteamid))
esclp = session.get('/escalation_policies?total={0}&team_ids%5B%5D={1}'.format(total,teamteamid))
if not esclp.ok:
print("Pulling Escalation polices for team '{}' failed".format(teamteamnm))
return
else:
ep = esclp.json()
epj = esclp.json()['escalation_policies']
if not epj:
print("Escalation polices for team '{}' not defined".format(teamteamnm))
return
else:
for adict2 in epj:
if not adict2['summary'] == team_esp_name:
continue
else:
print("***************FOUND FOUND********************")
teamesplcyid = adict2['id']
print("\nFetched {} id: {}\n".format(team_esp_name, teamesplcyid))
oncalls = session.get('/oncalls?total={0}&escalation_policy_ids%5B%5D={1}'.format(total,teamesplcyid))
if not oncalls.ok:
print(“issue “with oncalls)
return
else:
ocj = oncalls.json()['oncalls']
for adict3 in ocj:
print("\n")
print(adict3['escalation_level'])
if i['escalation_level'] == 1:
print(adict3['schedule']['summary'], adict3['user']['summary'])
team_oncall_dict[adict3['schedule']['summary']] = adict3['user']['summary']
print(team_oncall_dict)
return team_oncall_dict
if not team_oncall_dict: #part of func def
do foo()
output
foo stuff
sample data is a list of dicts
[{'escalation_policy': {'id': 'P8RKTEE', 'type': 'escalation_policy_reference', 'summary': 'Team Escalation Policy'}, 'escalation_level': 3, 'schedule': None, 'user': {'id': 'PX8XYFT', 'type': 'user_reference', 'summary': 'M1’}, 'start': None, 'end': None},
{'escalation_policy': {'id': 'P8RKTEE', 'type': 'escalation_policy_reference', 'summary': 'Team Escalation Policy'}, 'escalation_level': 1, 'schedule': None, 'user': {'id': 'PKXXVJI', 'type': 'user_reference', 'summary': ‘R1’}, 'start': None, 'end': None},
{'escalation_policy': {'id': 'P8RKTEE', 'type': 'escalation_policy_reference', 'summary': 'Team’}, 'escalation_level': 2, 'schedule': None, 'user': {'d': 'PN8F9PC', 'type': 'user_reference’,'summary': ‘T1’}],'start': None, 'end': None}]
btw: above is 4th inner loop.
so flow is like this diagramatically.
def func1()
team_oncall_dict = {}
loop1
loop2
loop3
loop4
...
team_oncall_dict
if not team_oncall_dict:
print("dict is empty")
output
dict is empty
t was local vs global. fixed it by declaring the team_oncall_dict globally outside the function.
intead of
def func1()
team_oncall_dict = {}
team_oncall_dict = {}
def func1()

Returning multiple values in python and appending them to unique columns to a dataframe

Background:
I have a function that gets a bunch of attributes from a database. Here is the function:
def getData(key, full_name, address, city, state, zipcode):
try:
url = 'https://personator.melissadata.net/v3/WEB/ContactVerify/doContactVerify'
payload={
'TransmissionReference': "test", # used by you to keep track of reference
'Actions': 'Check',
'Columns': 'Gender','DateOfBirth','DateOfDeath','EthnicCode','EthnicGroup','Education','PoliticalParty','MaritalStatus','HouseholdSize','ChildrenAgeRange','PresenceOfChildren','PresenceOfSenior','LengthOfResidence','OwnRent','CreditCardUser','Occupation','HouseholdIncome',
'CustomerID': key,# key
'Records': [{'FullName': str(full_name), 'AddressLine1': str(address), 'City': str(city), 'State': str(state), 'PostalCode': str(zipcode)}]
}
headers = {'Content-Type': 'application/json; charset=utf-8', 'Accept':'application/json', 'Host':'personator.melissadata.net','Expect': '100-continue', 'Connection':'Keep-Alive'}
r = requests.post(url, data=json.dumps(payload), headers=headers)
dom = json.loads(r.text)
Gender = dom['Records'][0]['Gender']
DateOfBirth = dom['Records'][0]['DateOfBirth']
DateOfDeath = dom['Records'][0]['DateOfDeath']
EthnicCode = dom['Records'][0]['EthnicCode']
EthnicGroup = dom['Records'][0]['EthnicGroup']
Education = dom['Records'][0]['Education']
PoliticalParty = dom['Records'][0]['PoliticalParty']
MaritalStatus = dom['Records'][0]['MaritalStatus']
HouseholdSize = dom['Records'][0]['HouseholdSize']
ChildrenAgeRange = dom['Records'][0]['ChildrenAgeRange']
PresenceOfChildren = dom['Records'][0]['PresenceOfChildren']
PresenceOfSenior = dom['Records'][0]['PresenceOfSenior']
LengthOfResidence = dom['Records'][0]['LengthOfResidence']
OwnRent = dom['Records'][0]['OwnRent']
CreditCardUser = dom['Records'][0]['CreditCardUser']
Occupation = dom['Records'][0]['Occupation']
HouseholdIncome = dom['Records'][0]['HouseholdIncome']
return Gender
except:
return None
To make a 'Gender' column I wrap the function into a lambda as so
df['Gender'] = df.apply(lambda row: getData(key, row['Full Name'], row['Address'], row['City'], row['State'], row['Zipcode']))
Objective:
I want to do this process simultaneously for all the other attributes you see below Gender, how can I do this in Python.
You can return a dictionary, then expand a series of dictionary objects:
fields = ['Gender', 'DateOfBirth', etc.]
def getData(key, full_name, address, city, state, zipcode):
try:
# your code as before
dom = json.loads(r.text)
return {k: dom['Records'][0][k] for k in fields}
# modify below: good practice to specify exactly which error(s) to catch
except:
return {}
Then expand your series of dictionaries:
dcts = df.apply(lambda row: getData(key, row['Full Name'], row['Address'], row['City'],
row['State'], row['Zipcode']), axis=1)
df = df.join(pd.DataFrame(dcts.tolist()))
As per #spaniard's comment, if you want all available fields, you can simply use:
return json.loads(r.text)['Records'][0]

Generate hierarchical JSON tree structure from Django model

I have a Django model as
class Classification(models.Model):
kingdom = models.CharField(db_column='Kingdom', max_length=50)
phylum = models.CharField(db_column='Phylum', max_length=50)
class_field = models.CharField(db_column='Class', max_length=50)
order = models.CharField(db_column='Order', max_length=50)
family = models.CharField(db_column='Family', max_length=50)
genus = models.CharField(db_column='Genus', max_length=50)
species = models.CharField(db_column='Species', max_length=50)
to represent biological taxonomy classification as shown here:
I have classification records of over 5,000 species. I need to generate JSON hierarchical structure as shown below.
{
'name': "root",
'children': [
{
'name': "Animalia",
'children': [
{
{
'name':"Chordata"
'children': [ ... ]
}
},
...
...
]
},
...
...
]
}
Can you suggest me any method(s) to do so?
You can do the following:
Transform a list of Classifications to a nested dict.
Transform nested dict to the required format
Samples here will operate on slightly reduced Classification class to improve readability:
class Classification:
def __init__(self, kingdom, phylum, klass, species):
self.kingdom = kingdom
self.phylum = phylum
self.klass = klass
self.species = species
First part:
from collections import defaultdict
# in order to work with your actual implementation add more levels of nesting
# as lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
nested_dict = defaultdict(
lambda: defaultdict(
lambda: defaultdict(list)
)
)
for c in all_classifications:
nested_dict[c.kingdom][c.phylum][c.klass].append(c.species)
defaultdict is just a nice tool to guarantee existence of the key in a dictionary, it receives any callable and use it to create a value for missing key.
Now we have nice nested dictionary in the form of
{
'Kingdom1': {
'Phylum1': {
'Class1': ["Species1", "Species2"],
'Class2': ["Species3", "Species4"],
},
'Phylum2': { ... }
},
'Kingdom2': { 'Phylum3': { ... }, 'Phylum4': {... } }
}
Part two: converting to desired output
def nested_to_tree(key, source):
result = {'name': key, 'children':[]}
for key, value in source.items():
if isinstance(value, list):
result['children'] = value
else:
child = nested_to_tree(key, value)
result['children'].append(child)
return result
tree = nested_to_tree('root', nested_dict')
I believe it's self-explanatory - we just convert passed dictionary to desired format and recurse to it's content to form children.
Complete example is here.
Two notes:
Written in python 3. Replacing source.items() with source.iteritems() should suffice to run in python 2.
You haven't specify what leafs should looks like, so I just assumed that leaf nodes should be genus with all species attached as children. If you want species to be leaf nodes - it's pretty straightforward to modify the code to do so. If you have any trouble doing so - let me know in comments.
Finally got what I wanted. Code is not beautiful, near ugly, yet somehow I got what I wanted.
def classification_flare_json(request):
#Extracting from database and sorting the taxonomy from left to right
clazz = Classification.objects.all().order_by('kingdom','phylum','class_field','genus','species')
tree = {'name': "root", 'children': []}
#To receive previous value of given taxa type
def get_previous(type):
types = ['kingdom', 'phylum', 'class_field', 'family', 'genus', 'species']
n = types.index(type)
sub_tree = tree['children']
if not sub_tree: return None
for i in range(n):
if not sub_tree: return None
sub_tree = sub_tree[len(sub_tree)-1]['children']
if not sub_tree: return None
last_item = sub_tree[len(sub_tree)-1]
return last_item['name']
#To add new nodes in the tree
def append(type, item):
types = ['kingdom', 'phylum', 'class_field', 'family', 'genus', 'species_id']
n = types.index(type)
sub_tree = tree['children']
for i in range(n+1):
if not sub_tree: return None
sub_tree = sub_tree[len(sub_tree)-1]['children']
sub_tree.append(item)
for item in clazz:
while True:
if item.kingdom == get_previous('kingdom'):
if item.phylum == get_previous('phylum'):
if item.class_field == get_previous('class_field'):
if item.family == get_previous('family'):
if item.genus == get_previous('genus'):
append('genus', {'name':item.species, 'size': 1})
break;
else:
append('family', {'name':item.genus, 'children': []})
else:
append('class_field', {'name':item.family, 'children':[]})
else:
append('phylum', {'name': item.class_field, 'children':[]})
else:
append('kingdom', {'name': item.phylum, 'children':[]})
else:
tree['children'].append({'name': item.kingdom, 'children':[]})
return HttpResponse(json.dumps(tree), content_type="application/json")

Store path to dictionary value for setting value

Consider a dict that holds a person:
person = {}
person['name'] = 'Jeff Atwood'
person['address'] = {}
person['address']['street'] = 'Main Street'
person['address']['zip'] = '12345'
person['address']['city'] = 'Miami'
How might the path to a location in the dictionary be stored for writing to the value?
# Set city (Existing field)
city_field = ['address', 'city']
person[city_field] = 'London' // Obviously won't work!
# Set country (New field)
country_field = ['address', 'country']
person[city_country] = 'UK' // Obviously won't work!
Note that I had previously asked how to store the path to dictionary value for reading.
Use tuples as index.
city_field = ('address', 'city')
country_field = ('address', 'country')
Usage:
>>> person = {}
>>> city_field = ('address', 'city')
>>> country_field = ('address', 'country')
>>> person[city_field] = 'Miami'
>>> person[country_field] = 'UK'
>>> person
{('address', 'country'): 'UK', ('address', 'city'): 'Miami'}
Got it! Actually my co-worker Moshe is the brains behind this one:
def set_path(someDict, path, value):
for x in path[::-1]:
value = {x: value}
return deepupdate(someDict, value)
def deepupdate(original, update):
for key, value in original.items():
if not key in update:
update[key] = value
elif isinstance(value, dict):
deepupdate(value, update[key])
return update
person = {}
person = set_path(person, ['name'], 'Shalom')
person = set_path(person, ['address', 'city'], 'Toronto')
person = set_path(person, ['address', 'street'], 'Baddessa')
pprint(person)
Returns:
{
'address': {
'city': 'Toronto',
'street': 'Baddessa'
},
'name': 'Shalom'
}
This depends on user Stanislav's excellent recursive dictionary deepmerge() function.

dynamic query getting data post python

I'm doing the following , when I get to post data to the queries :
fields = [
'name',
'surname',
'weight',
'height',
'position',
'agent',
'nationality',
'shirt_name',
'shirt_number',
'preferred_foot',
]
if request.method == 'POST':
filter_spec = None
for fld_name in fields:
value = request.POST.get(fld_name, None)
if value is not None:
if fld_name == 'position':
fld_name = 'position__in'
value = Position.objects.filter(name=value)
if fld_name == 'agent':
fld_name = 'entrepreneur__in'
value = Entrepreneur.objects.filter(name=value)
if filter_spec is None:
filter_spec = Q(**{fld_name: value})
else:
filter_spec &= Q(**{fld_name: value})
players = Player.objects.filter(filter_spec) if filter_spec else Player.objects.none()
result = [p.get_json() for p in players]
py_resp = {
'meta': {
'total_count': len(result),
},
'objects': result,
}
json_resp = json.dumps(py_resp)
return HttpResponse(json_resp, content_type='application/json')
To position the query is as follows:
Player.objects.filter(position__in=Position.objects.filter(name=positionpost))
So I did as it is on top , which I am not able to implement it in the code for the nationality, since the query is thus:
Player.objects.filter(country__in=Country.objects.filter(nationality__in=Nationality.objects.filter(name='Espanhola')))
And I'm not sure how to put this in the code above
Someone can help me ?
Both your queries can be significantly simplified. The first should be:
Player.objects.filter(position__name=positionpost)
and the second:
Player.objects.filter(country__nationality__name='Espanhola')
These will usually also be more efficient, as they are doing JOINs rather than sub-queries.

Categories