How can I add a variable to a function - python

I'm very new to python, but learning.
I have below error in my code, I could solve 1 of the solutions, but I'm not sure is correct.
1. UnboundLocalError: local variable 'newdict' referenced before assignment
In my function I want to copy data to newdict if [elt['id'] for elt in response_json['data']] is higher but I get the error number 1. I then googled and found out that I could add Global newdict, but I am not sure if it is the right of way of expressing variables in Python. I am not sure how I can solve it.
I want to copy only the 'data' key from the nested dictionary response_json, it looks like it is a nested dictionary, so I can compare the 'id' element, because if that's higher than the previous, then I need to do something.
I hope someone can help me.
import json
import pprint, datetime
import numpy as np
import schedule
import time
from http import client
trading_pair_id = 48
offset = 0
limit = 1
newdict = {'id': 0} # <--- Not sure I have done this correct
def trades():
global newdict # <--- I'm not sure this is pythonically correct
# call the API `GET trades` endpoint
conn = client.HTTPSConnection("trade.blocktrade.com")
conn.request("GET", "/api/v1/trades/%d?offset=%d&limit=%d" % (trading_pair_id,offset,limit), headers={
'Content-Type': 'application/json',})
response = conn.getresponse()
response_raw = response.read()
try:
response_json = json.loads(response_raw)
pprint.pprint(response_json)
except:
print(response_raw)
print([elt['id'] for elt in response_json['data']])
if [elt['id'] for elt in response_json['data']] > newdict['id']:
newdict = dict(response_json)
print(newdict)
schedule.every(5).seconds.do(trades)
while True:
schedule.run_pending()
time.sleep(1)

If I understand you correctly, this should be ok
import json
import pprint, datetime
import numpy as np
import time
from http import client
trading_pair_id = 48
offset = 0
limit = 1
newdict = {'id': 0} # <-- Init variable
def trades(newdict): # <-- Pass variable to the function
# call the API `GET trades` endpoint
conn = client.HTTPSConnection("trade.blocktrade.com")
conn.request("GET", "/api/v1/trades/%d?offset=%d&limit=%d" % (trading_pair_id,offset,limit), headers={
'Content-Type': 'application/json',})
response = conn.getresponse()
response_raw = response.read()
try:
response_json = json.loads(response_raw)
pprint.pprint(response_json)
# <-- replace newdict data with newest id
for elt in response_json['data']:
if elt['id'] > newdict['id']:
newdict = elt
print(newdict)
except:
print(response_raw)
schedule.every(5).seconds.do(trades(newdict))

Related

Changing variable for parameters for http json get requests

Pretty new to python so go easy on me :). This code works below but I was wondering if there is a way to change the indcode parameter by doing a loop so I do not have to repeat the requests.get.
paraD = dict()
paraD["area"] = "123"
paraD["periodtype"] = "2"
paraD["indcode"] = "722"
paraD["$limit"]=1000
#Open URL and get data for business indcode 722
document_1 = requests.get(dataURL, params=paraD)
bizdata_1 = document_1.json()
#Open URL and get data for business indcode 445
paraD["indcode"] = "445"
document_2 = requests.get(dataURL, params=paraD)
bizdata_2 = document_2.json()
#Open URL and get data for business indcode 311
paraD["indcode"] = "311"
document_3 = requests.get(dataURL, params=paraD)
bizdata_3 = document_3.json()
#Combine the three lists
output = bizdata_1 + bizdata_2 + bizdata_3
Since indcode is the only parameter that changes for each request, we will put that in a list and make the web requests inside a loop.
data_url = ""
post_params = dict()
post_params["area"] = "123"
post_params["periodtype"] = "2"
post_params["$limit"]=1000
# The list of indcode values
ind_codes = ["722", "445", "311"]
output = []
# Loop on indcode values
for code in ind_codes:
# Change indcode parameter value in the loop
post_params["indcode"] = code
try:
response = requests.get(data_url, params=post_params)
data1 = response.json()
output.append(data1)
except:
print("web request failed")
# More error handling / retry if required
print(output)
Assuming you're using Python 3.9+ you can combine dictionaries using the | operator. However, you need to be sure that you understand exactly what this will do. It's more likely that the code to combine dictionaries will be more complex.
When using the requests module it is very important to check the HTTP status code returned from the function (HTTP verb) you're calling.
Here's an approach to the stated problem that may work (depending on how the dictionary merge is effected).
from urllib.error import HTTPError
from requests import get as GET
from requests.exceptions import Timeout, TooManyRedirects, RequestException
from sys import stderr
# base parameters
params = {'area': '123', 'periodtype': '2', '$limit': 1000}
# the indcodes
indcodes = ('722', '445', '311')
# gets the JSON response (as a Python dictionary)
def getjson(url, params):
try:
(r := GET(url, params, timeout=1.0)).raise_for_status()
return r.json() # all good
# if we get any of these exceptions, report to stderr and return an empty dictionary
except (HTTPError, ConnectionError, Timeout, TooManyRedirects, RequestException) as e:
print(e, file=stderr)
return {}
# any exception here is not associated with requests/urllib. Report and raise
except Exception as f:
print(f, file=stderr)
raise
# an empty dictionary
target = {}
# build the target dictionary
# May not produce desired results depending on how the dictionary merge should be carried out
for indcode in indcodes:
target |= getjson('https://httpbin.org/json', params | {'indcode' : indcode})

Integrating Progress Bar for API Call

Background: I have seen lots of examples of integrating a progress bar into a for loop, however nothing for my use case, and as such am looking for some advice.
For my use case, I am calling an API and testing if meta is in the response (meta = data I need). If meta is not in the API response, then the API returns a key pair value named percent_complete, which indicates the data I am trying to return is still aggregating, and provides a value on the progress of data aggregation.
Current code:
def api_call():
key, secret, url = ini_reader()
endpoint_url = endpoint_initializer()
while True:
response = requests.get(url = endpoint_url, auth = HTTPBasicAuth(key, secret), headers = {"vendor-firm": "111"})
api_response = json.loads(response.text)
if "meta" not in api_response:
id_value = "id"
res1 = [val[id_value] for key, val in api_response.items() if id_value in val]
id_value = "".join(res1)
percent_value = "percent_complete"
res2 = api_response["data"]["attributes"].get("percent_complete", '')*100
print(f' Your data request for: {id_value} is {res2}% complete!')
time.sleep(60)
elif "meta" in api_response:
return api_response
What I am trying to achieve: {res2} *100 gives the percentage, which I would like to use the measure of progress in a progress bar.
Can anyone suggest an appropriate dependency to use?
You can use the Enlighten library. You can keep your print statements and have multiple progress bars at the same time without making any other changes. Below is an example of how you might implement it.
Based on your example it looks like id_value changes, so I wrote the example like that. If it doesn't change you can just use it in the description. And if you have multiples, you'd probably want to create a progress bar for each. If you want to remove your progress bars after they complete, just add leave=False to manager.Counter().
The library is very customizable and the documentation has a lot of examples.
import enlighten
BAR_FORMAT = u'{id_value} {percentage:3.0f}%|{bar}| ' u'[{elapsed}<{eta}, {rate:.2f} %/s]'
manager = enlighten.get_manager()
def api_call():
pbar = manager.counter(total=100, bar_format=BAR_FORMAT)
...
while True:
...
if "meta" not in api_response:
...
pbar.count = res2
pbar.update(incr=0, id_value=id_value)
else:
...
pbar.count = 100
pbar.update(incr=0, id_value=id_value)
pbar.close()
return api_response
Thanks to Aviso, and for everyone's benefit, here is the completed function -
def api_call():
endpoint_url = endpoint_initializer()
key, secret, url = ini_reader()
BAR_FORMAT = u'{id_value} {percentage:3.0f}%|{bar}| ' u'[{elapsed}<{eta}, {rate:.2f} %/s]'
manager = enlighten.get_manager()
date = dt.datetime.today().strftime("%Y-%m-%d")
print("------------------------------------\n","API URL constructed for:", date, "\n------------------------------------")
print("-------------------------------------------------------------\n","Endpoint:", endpoint_url, "\n-------------------------------------------------------------")
pbar = manager.counter(total=100, bar_format=BAR_FORMAT)
while True:
response = requests.get(url = endpoint_url, auth = HTTPBasicAuth(key, secret), headers = {"vendor-firm": "381"})
api_response = json.loads(response.text)
if "meta" not in api_response:
id_value = "id"
res1 = [val[id_value] for key, val in api_response.items() if id_value in val]
id_value = "".join(res1)
percent_value = "percent_complete"
res2 = api_response["data"]["attributes"].get("percent_complete", '')*100
pbar.count = res2
pbar.update(incr=0, id_value=id_value)
time.sleep(60)
elif "meta" in api_response:
pbar.count = 100
pbar.update(incr=0, id_value=id_value)
pbar.close()
return api_response

Using a variable from a dictionary in a loop to attach to an API call

I'm calling a LinkedIn API with the code below and it does what I want.
However when I use almost identical code inside a loop it returns a type error.
it returns a type error:
File "C:\Users\pchmurzynski\OneDrive - Centiq Ltd\Documents\Python\mergedreqs.py", line 54, in <module>
auth_headers = headers(access_token)
TypeError: 'dict' object is not callable
It has a problem with this line (which again, works fine outside of the loop):
headers = headers(access_token)
I tried changing it to
headers = headers.get(access_token)
or
headers = headers[access_token]
EDIT:
I have also tried this, with the same error:
auth_headers = headers(access_token)
But it didn't help. What am I doing wrong? Why does the dictionary work fine outside of the loop, but not inside of it and what should I do to make it work?
What I am hoping to achieve is to get a list, which I can save as json with share statistics called for each ID from the "shids" list. That can be done with individual requests - one link for one ID,
(f'https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&ugcPosts=List(urn%3Ali%3AugcPost%3A{shid})
or a a request with a list of ids.
(f'https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&ugcPosts=List(urn%3Ali%3AugcPost%3A{shid},urn%3Ali%3AugcPost%3A{shid2},...,urn%3Ali%3AugcPost%3A{shidx})
Updated Code thanks to your comments.
shlink = ("https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&shares=List(urn%3Ali%3Ashare%3A{})")
#loop through the list of share ids and make an api request for each of them
shares = []
token = auth(credentials) # Authenticate the API
headers = fheaders(token) # Make the headers to attach to the API call.
for shid in shids:
#create a request link for each sh id
r = (shlink.format(shid))
#call the api
res = requests.get(r, headers = auth_headers)
share_stats = res.json()
#append the shares list with the responce
shares.append(share_stats["elements"])
works fine outside the loop
Because in the loop, you re-define the variable. Added print statments to show it
from liapiauth import auth, headers # one type
for ...:
...
print(type(headers))
headers = headers(access_token) # now set to another type
print(type(headers))
Lesson learned - don't overrwrite your imports
Some refactors - your auth token isn't changing, so don't put it in the loop; You can use one method for all LinkedIn API queries
from liapiauth import auth, headers
import requests
API_PREFIX = 'https://api.linkedin.com/v2'
SHARES_ENDPOINT_FMT = '/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&shares=List(urn%3Ali%3Ashare%3A{}'
def get_linkedin_response(endpoint, headers):
return requests.get(API_PREFIX + endpoint, headers=headers)
def main(access_token=None):
if access_token is None:
raise ValueError('Access-Token not defined')
auth_headers = headers(access_token)
shares = []
for shid in shids:
endpoint = SHARES_ENDPOINT_FMT.format(shid)
resp = get_linkedin_response(endpoint, auth_headers)
if resp.status_code // 100 == 2:
share_stats = resp.json()
shares.append(share_stats[1])
# TODO: extract your data here
idlist = [el["id"] for el in shares_list["elements"]]
if __name__ == '__main__':
credentials = 'credentials.json'
main(auth(credentials))

Flask loop takes long time to complete

I have this loop in my app.py. For some reason it extends the load time by over 3 seconds. Are there any solutions?
import dateutil.parser as dp
# Converts date from ISO-8601 string to formatted string and returns it
def dateConvert(date):
return dp.parse(date).strftime("%H:%M # %e/%b/%y")
def nameFromID(userID):
if userID is None:
return 'Unknown'
else:
response = requests.get("https://example2.org/" + str(userID), headers=headers)
return response.json()['firstName'] + ' ' + response.json()['lastName']
logs = []
response = requests.get("https://example.org", headers=headers)
for response in response.json():
logs.append([nameFromID(response['member']), dateConvert(response['createdAt'])])
It extends the load time by over 3 seconds because it does a lot of unnecessary work, that's why.
You're not using requests Sessions. Each request will require creating and tearing down an HTTPS connection. That's slow.
You're doing another HTTPS request for each name conversion. (See above.)
You're parsing the JSON you get in that function twice.
Whatever dp.parse() is (dateutil?), it's probably doing a lot of extra work parsing from a free-form string. If you know the input format, use strptime.
Here's a rework that should be significantly faster. Please see the TODO points first, of course.
Also, if you are at liberty to knowing the member id -> name mapping doesn't change, you can make name_cache a suitably named global variable too (but remember it may be persisted between requests).
import datetime
import requests
INPUT_DATE_FORMAT = "TODO_FILL_ME_IN" # TODO: FILL ME IN.
def dateConvert(date: str):
return datetime.datetime.strptime(date, INPUT_DATE_FORMAT).strftime(
"%H:%M # %e/%b/%y"
)
def nameFromID(sess: requests.Session, userID):
if userID is None:
return "Unknown"
response = sess.get(f"https://example2.org/{userID}")
response.raise_for_status()
data = response.json()
return "{firstName} {lastName}".format_map(data)
def do_thing():
headers = {} # TODO: fill me in
name_cache = {}
with requests.Session() as sess:
sess.headers.update(headers)
logs = []
response = sess.get("https://example.org")
for response in response.json():
member_id = response["member"]
name = name_cache.get(member_id)
if not name:
name = name_cache[member_id] = nameFromID(sess, member_id)
logs.append([name, dateConvert(response["createdAt"])])

Python(requests) How to parse only certain json data, How to make this chunk of code loop (not as important)

Figured it out thanks to Loki!
Every 1 second logs data from print(get_rates()) function to a .txt file, so I made an infinite while loop (suggested by loki)
import requests
from time import sleep
from datetime import datetime,date
old_print = print
def tstamped_print(*args, **kwargs):
old_print(datetime.now(), *args, **kwargs)
print = tstamped_print
# Getting rates
def get_rates():
PREEVURL = requests.get('http://preev.com/pulse/units:btc+usd/sources:bitstamp+kraken')
DATA = PREEVURL.json()
RESULT = {}
for key, value in DATA["btc"]["usd"].items():
RESULT[key] = (value['last'])
return RESULT
# Infinite while loop
a = 1
while a < 10:
print(get_rates(),file=open("btc_price1.txt", "a"))
print(get_rates())
sleep(2)
a = a - 0
else:
print("loop end")
If I understand well, you want to do two things:
Extract a bitcoin rate from this api response
Loop requests and store each result in an file to save the history
Let's see how to do this:
Call the api and store the result
If you call it in a python shell you can look at the data:
>>> import requests
>>>
>>> url_resp = requests.get('http://preev.com/pulse/units:btc+usd/sources:bitstamp+kraken') # (your url)
>>> data = url_resp.json()
>>> data
{'btc': {'usd': {'bitstamp': {'last': '9503.05', 'volume': '6.27734e+7'}, 'kraken': {'last': '9509.10', 'volume': '4.08549e+7'}}}, 'other': {'slot': None, 'ver': 'b'}}
To get the value really simply access each dictionnary item by its key:
# Convert the string to float number.
bitsamp_rate = float(data['btc']['usd']['bitstamp']['last'])
kraken_rate = float(data['btc']['usd']['kraken']['last'])
Let's loop over each exchange and put it in a function:
def get_rates():
url_resp = requests.get('http://preev.com/pulse/units:btc+usd/sources:bitstamp+kraken') # (your url)
data = url_resp.json()
result = {}
for exchange, rates in data['btc']['usd'].items():
result[exchange] = float(rates['last'])
return result
Use a while loop to call the function multiple times
I'm letting you decide how to store the data, you might want to record
the time when the function was called also.
The problem is that your initial data.json file is empty. The easiest way out is to place an empty dictionary or something in your data.json file. Just make sure data.json is not an empty file.
Figured it out thanks to Loki!
Every 1 second logs data from each print(get_rates()) function to a .txt file, so I made an infinite while loop (suggested by loki)
import time
import requests
import datetime
from time import sleep
from datetime import datetime,date
'''
print('Enter correct username and password combo to continue')
count = 0
username = '420'
while count < 10:
username = input('login code: ')
if username== '420':
print('Access granted')
count = 11
break
else:
print('Access denied. Try again.')
count =- 1
print('====> LOGGED IN','\n')
'''
today = time.strftime("(%Y-%m-%d %I:%M%p)")
old_print = print
def tstamped_print(*args, **kwargs):
old_print(today, *args, **kwargs)
print = tstamped_print
# Getting rates
def get_rates():
PREEVURL = requests.get('http://preev.com/pulse/units:btc+usd/sources:bitstamp+kraken')
DATA = PREEVURL.json()
RESULT = {}
for key, value in DATA["btc"]["usd"].items():
RESULT[key] = (value['last'])
return RESULT
# Infinite while loop
a = 1
PREEVURL = requests.get('http://preev.com/pulse/units:btc+usd/sources:bitstamp+kraken')
DATA = PREEVURL.json()
while a < 10:
print(get_rates(), file=open("btc_price1.txt", "a"))
print(get_rates())
sleep(1)
a = a - 0
else:
print("loop end")

Categories

Resources