Building classes from nested dictionaries - python

So I am new to all this, please pardon my poor formatting and only loose grasp of jargon.
In short, I have a
coins = requests.get("https://bittrex.com/api/v1.1/public/getcurrencies")
that returns json() nested dictionaries (sorry for terrible phrasing) that look like this:
{
"success" : true,
"message" : "",
"result" : [{
"Currency" : "BTC",
"CurrencyLong" : "Bitcoin",
"MinConfirmation" : 2,
"TxFee" : 0.00020000,
"IsActive" : true,
"CoinType" : "BITCOIN",
"BaseAddress" : null
}, {
"Currency" : "LTC",
"CurrencyLong" : "Litecoin",
"MinConfirmation" : 5,
"TxFee" : 0.00200000,
"IsActive" : true,
"CoinType" : "BITCOIN",
"BaseAddress" : null
}]
}
In short I would like to access each dictionary within coins["result"] and build my own values with them to generate a class for each coin so that the inherited code gets filled, like:
class coinbuilder(self, currency, currencyLong, minConfirmation...):
def __init__(self):
self.currency = currency
self.currencyLong = currencyLong
ticker = requests.get("https://bittrex.com/api/v1.1/public/getticker?market=BTC-" + currency)
I understand this code is incorrect, but Im trying to give you an idea of what I am trying to accomplish. Sorry for my terrible phrasing, im new to programming in general and while I have a general grasp of function I am still trying to catch up on jargon.

There are several different ways to do this, you could parse the dictionary inside your __init__ function for example.
I tend to handle this by having a separate #classmethod that is responsible for parsing dictionaries to create instances of my class.
Something like this:
class Coinbuilder:
def __init__(self, cur, curlong):
self.currency = cur
self.currency_long = curlong
#classmethod
def build_from_dict(coin_b, d):
attributes = {'Currency', 'CurrencyLong'}
try:
class_dct = {a: d[a] for a in attributes}
except:
raise ValueError('Input did not contain necessary attributes')
return coin_b(class_dct['Currency'], class_dct['CurrencyLong'])
This way I am not forced to pass Coinbuilder a dictionary to create a class instance, but I have a simple method I can use to parse a dictionary to create an instance of my class.
For this simple example, I can do the following:
x = Coinbuilder.build_from_dict({'Currency': 'BTC', 'CurrencyLong': 'Bitcoin'})
Or I could use:
y = Coinbuilder('BTC', 'Bitcoin')
And get two equal class instances:
print(x.currency, x.currency_long)
print(y.currency, y.currency_long)
Output:
BTC Bitcoin
BTC Bitcoin
Using your sample input as a guide, once you write your #classmethod to parse your dictionary, you could then simply use:
my_coins = []
for result in coins['result']:
my_coins.append(Coinbuilder.build_from_dict(result))
Or:
my_coins = [Coinbuilder.build_from_dict(result) for result in coins['result']]

Here is a total script for what you are trying to do:
import requests
import json
class CoinBuilder:
def __init__(self,dict):
self.currency = dict['Currency']
self.currencyLong = dict['CurrencyLong']
self.minConfirmation = dict['MinConfirmation']
self.txFee = dict['TxFee']
self.isActive = dict['IsActive']
self.coinType = dict['CoinType']
self.baseAddress = dict['BaseAddress']
self.notice = dict['Notice']
coins_response = requests.get("https://bittrex.com/api/v1.1/public/getcurrencies")
all_coins = json.loads(coins_response.content)
list_of_coin_obs = []
for coin in all_coins["result"]:
list_of_coin_obs.append(CoinBuilder(coin))
This script is getting the response, then iterating through the dictionaries in result[] and building CoinBuilder objects from that. All of the created objects are also being stored in a list, list_of_coin_obs[].
You can then print the first 10 results you have, for example, like this:
# Print out the first 10 coins
print("First 10 coins:")
for i in range(1,11):
print(i,") ",list_of_coin_obs[i].currency)
For this example, this would output:
First 10 coins:
1 ) LTC
2 ) DOGE
3 ) VTC
4 ) PPC
5 ) FTC
6 ) RDD
7 ) NXT
8 ) DASH
9 ) POT
10 ) BLK
If you wanted to create a method to look up a specific coin by it's ticker symbol, you could create something like this:
# method to retrieve a specific coin from 'list_of_coin_obs[]'
# we are passing in a parameter, 'coin_abr' to give to our filter
def get_specific_coin_by_abr(coin_abr):
return next(filter(lambda x: x.currency == coin_abr, list_of_coin_obs))
# call our method, which returns a 'CoinBuilder' type
specific_coin = get_specific_coin_by_abr('BTC')
# print our results to show it worked
print('CurrencyName: ',specific_coin.currency,'CurrencyLong: ',specific_coin.currencyLong)
This prints:
CurrencyName: BTC CurrencyLong: Bitcoin
Note: This is assuming that you already have the list_of_coin_obs[] created and in the same scope as this method.
One suggestion, here the class name CoinBuilder doesn't totally make the most sense. A better name for the class/objects would simply be Coin or NamedCurrency or some other similar name. I think I know what you were going for, but this is probably a better fit for your project.
Best of luck.

Related

Python: mapping between class and json

I am getting Data via a REST-Interface and I want to store those data in a class-object.
my class could looks like this:
class Foo:
firstname = ''
lastname = ''
street = ''
number = ''
and the json may look like this:
[
{
"fname": "Carl",
"lname": "any name",
"address": ['carls street', 12]
}
]
What's the easiest way to map between the json and my class?
My problem is: I want to have a class with a different structure than the json.
I want the names of the attributes to be more meaningful.
Of course I know that I could simply write a to_json method and a from_json method which does what I want.
The thing is: I have a lot of those classes and I am looking for more declarative way to write the code.
e.g. in Java I probably would use mapstruct.
Thanks for your help!
Use a dict for the json input. Use **kwargs in an __init__ method in your class and map the variables accordingly.
I had a similar problem, and I solved it by using #classmethod
import json
class Robot():
def __init__(self, x, y):
self.type = "new-robot"
self.x = x
self.y = y
#classmethod
def create_robot(cls, sdict):
if sdict["type"] == "new-robot":
position = sdict["position"]
return cls(position['x'], position['y'])
else:
raise Exception ("Unable to create a new robot!!!")
if __name__=='__main__':
input_string = '{"type": "new-robot", "position": {"x": 3, "y": 3}}'
cmd = json.loads(input_string)
bot = Robot.create_robot(cmd)
print(bot.type)
Perhaps you could you two classes, one directly aligned with the Json (your source class) and the other having the actual structure you need. Then you could map them using the ObjectMapper class[https://pypi.org/project/object-mapper/]. This is very close to the MapStruct Library for Java.
ObjectMapper is a class for automatic object mapping. It helps you to create objects between project layers (data layer, service layer, view) in a simple, transparent way.

Django ManyToMany passing variables

I have started my Django project and I want to share some data between 2 classes.
I'm not sure if I am doing it correctly. It works but i don't want to populate in my project bad practices.
My code looks like this:
class Products(models.Model):
name = models.CharField(max_length=50)
protein = models.FloatField()
carbohydrates = models.FloatField()
fat = models.FloatField()
food_type = models.CharField(max_length=6, choices=(
("1", "Meat"),
("2", "Fruit")
)
)
class Meals(models.Model):#Child
name = models.CharField(max_length=50)
ingredient = models.ManyToManyField(Products)
def protein(self):
protein = 0
for ing in self.ingredient.all():
protein += ing.protein
return protein
#property
def carbohydrates(self):
carbohydrates = 0
for ing in self.ingredient.all():
carbohydrates += ing.carbohydrates
return carbohydrates
def diet_category(self):
diet_types = "vegan, vegeterian, Keto, Paleo, Gluten-free"
food_types = ""
for ing in self.ingredient.all():
food_types += ing.food_type
if "1" in food_types:
diet_types.replace("vegan, ", "").replace(" vegeterian,", "")
return (diet_types + " | " + food_types)
Additionally I have problem with .replace() function in python, which i want to use here to exclude some words from variable.
Summing up my questions are:
-Retrieving properties from another class is done by referring to an object of that class.
In this case Products.objects.all()
-How can I remove words from variable in models.
-Should i use #property for functions which are returning values from another class?
Replace()
The replace() function returns a copy of the string with the replaced values. It will not change the original string.
So you need to do something like
diet_types = diet_types.replace("vegan, ", "")
diet_types = diet_types.replace("vegetarian, ", "")
Fetching values
You can loop like you do or use a query to achieve the same, somthing like:
def protein(self):
return self.ingredient.aggregate(Sum('protein'))['protein__sum']
See the docs on aggregation for more details.
Properties
IMHO it is a matter of taste
Your classes can have properties dependent on other classes but make sure that you handle exception where there are no values found in other class.
So I believe using #property is not a bad idea.
Moreover, did you try using
diet_types.replace("vegan, ", " vegeterian")

How to print specific value from specific key from JSON in Python

I wrote 2 functions so I can get champion ID knowing champion Name but then I wanted to get champion Name knowing champion ID but I cannot figure it out how to extract the name because of how the data structured.
"data":{"Aatrox":{"version":"8.23.1","id":"Aatrox","key":"266","name":"Aatrox"
so in my code I wrote ['data']['championName'(in this case Aatrox)]['key'] to get the champion ID/key. But how can I reverse it if for example I don't know the champion Name but champions ID. How can I get the champion Name if after writing ['data'] I need to write champion Name so I can go deeper and get all the champions info like ID, title etc..
link: http://ddragon.leagueoflegends.com/cdn/8.23.1/data/en_US/champion.json
Code:
def requestChampionData(championName):
name = championName.lower()
name = name.title()
URL = "http://ddragon.leagueoflegends.com/cdn/8.23.1/data/en_US/champion/" + name + ".json"
response = requests.get(URL)
return response.json()
def championID(championName):
championData = requestChampionData(championName)
championID = str(championData['data'][championName]['key'])
return championID
since python values are passed by reference you can make a new dict with keys as the champion id pointing to the values of the previous dict, that way you dont duplicate too much data. but be carefull if you change data in one dict the data will be changed in the other one too
def new_dict(d):
return { val["id"]:val for val in d.values() }
I solved my problem with this code:
def championNameByID(id):
championData = requestChampionData()
allChampions = championData['data']
for champion in allChampions:
if id == allChampions[champion]['key']:
championName = allChampions[champion]['name']
return championName

Orator ORM model Create method invalid SQL

I have a database I created with a migration. One of my tables looks like this
def create_customer_table(self):
with self.schema.create("customer") as table:
table.char("name",120).unique()
table.integer("transmitting_hours").default(24) #how many hours after transmission vehicle is considered transmitting
table.boolean("is_tpms").default(False)
table.boolean("is_dor").default(False)
table.boolean("is_otr").default(False)
table.boolean("is_track_and_trace").default(False)
table.char("contact_person",25)
table.char("created_by",25)
table.enum("temperature_unit",TEMP_UNITS)
table.enum("pressure_unit",PRESSURE_UNITS)
table.enum("distance_unit",DISTANCE_UNITS)
table.char("time_zone",25)
table.char("language",2)
table.timestamps()
I have a very simplistic ORM model on top
class Customer(Model):
__table__ = "customer"
__timestamps__ = False
__primary_key__ = "name"
__fillable__ = ['*']
I then try to do a basic insert with the following code
def add_sample_customer():
sample_customer = {}
sample_customer["name"] = "customer_2"
sample_customer["contact_person"] = "Abradolf"
sample_customer["created_by"] = "Frodo"
sample_customer["time_zone"] = "GMT-5"
sample_customer["language"] = "EN"
sample_customer["temperature_unit"] = "FAHRENHEIT"
sample_customer["pressure_unit"] = "PSI"
sample_customer["distance_unit"] = "MI"
customer_model = Customer.create(_attributes = sample_customer)
The exception I get from this code looks like
orator.exceptions.query.QueryException: syntax error at or near ")"
LINE 1: INSERT INTO "customer" () VALUES () RETURNING "name"
(SQL: INSERT INTO "customer" () VALUES () RETURNING "name" ([]))
it looks like orator just isn't filling in the cols and vals here. I have also tried it with a few different syntactic ways of dropping the dict in there, using **sample_customer and also just putting the dict in directly and none of them work, all with the same exception. I started debugging by printing stuff out of the orator libraries but haven't gotten anywhere yet.
my inserts work if I do the model attribute assignment individually and use the model.save() method like this
def add_sample_customer():
sample_customer = {}
sample_customer["name"] = "customer_2"
sample_customer["contact_person"] = "Abradolf"
sample_customer["created_by"] = "Frodo"
sample_customer["time_zone"] = "GMT-5"
sample_customer["language"] = "EN"
sample_customer["temperature_unit"] = "FAHRENHEIT"
sample_customer["pressure_unit"] = "PSI"
sample_customer["distance_unit"] = "MI"
customer_model = Customer()
for k,v in sample_customer.items():
setattr(customer_model,k,v)
customer_model.save()
Does anyone understand why the model.create() syntax fails?
I would think the answer would be:
Simply passing the dictionary instead of using keyword notation with attributes:
Customer.create(sample_customer)
or
Customer.create(attribute=value,attribute2=value2,..etc)
Which are the valid notations

Wtforms- Create Dict using formfield that has two values per key

I'd like to be able to collect both the track name and the start time for each track on a cd into a dict or json column in a table.
I have defined a formfield to catch the data relating to the track names and save it in a dict:
class SeperateTracks(NoCsrfForm):
track1 = TextField('track1')
track2 = TextField('track2')
track3 = TextField('track3')
track4 = TextField('track4')
class SendForm(Form):
alltracks = FormField(SeperateTracks)
This creates a dictionary that looks something like this so:
{"track1": "songname1", "track2": "songname2", "track3": "songname3", "track4": "songname4"}
What I'd like to achieve, is to have two TextFields per track- one for the track name and one for the start time of the track.
I realize that in terms of creating more fields to accomodate this, I could simply create more text fields to hold the start time data like so:
class SeperateTracks(NoCsrfForm):
track1 = TextField('track1')
track2 = TextField('track2')
track3 = TextField('track3')
track4 = TextField('track4')
starttime1 = TextField('starttime1')
starttime2 = TextField('starttime2')
starttime3 = TextField('starttime3')
starttime4 = TextField('starttime4')
However, this wouldn't associate the times with the corresponding tracks. What would be the recommended method for doing something like this?
You'd want something like this:
class Track(NoCsrfForm):
songname = StringField('Song Name')
starttime = StringField('start time')
class SeparateTracks(NoCsrfForm):
tracks = FieldList(FormField(Track), min_entries=1, max_entries=4)
I made assumptions on max_entries based on your example, but it's not strictly required, and you can manage anywhere between 1 and N entries this way.
the data in python would look something like:
[{songname: "name 1", starttime: "123"}, {songname: "name 2", starttime: "456"}, ... ]
More info:
See more on Field Enclosures here.
A more extensive example

Categories

Resources