List in a dictionary, looping in Python - python

I have the following code:
TYPES = {'hotmail':{'type':'hotmail', 'lookup':'mixed', 'dkim': 'no', 'signatures':['|S|Return-Path: postmaster#hotmail.com','|R|^Return-Path:\s*[^#]+#(?:hot|msn)','^Received: from .*hotmail.com$']},
'gmail':{'type':'gmail', 'lookup':'mixed', 'dkim': 'yes', 'signatures':['|S|Subject: unsubscribe','','','']}
}
for type_key, type in TYPES.iteritems():
for sub_type_key, sub_type in type.iteritems():
for sig in sub_type['signatures']:
if ("|S|" in sig):
#String based matching
clean_sig = sig[3:len(sig)]
if (clean_sig in file_contents):
sig_match += 1
elif ("|R|" in sig):
clean_sig = sig[3:len(sig)]
#REGMATCH later
if (sig_match == sig.count):
return sub_type['type']
return None
However, it generates the error:
for sig in sub_type['signatures']:
TypeError: string indices must be integers, not str
I assume that it would see the list being pulled from dictionary element, and allow me to loop over that?
Python newbie is a newbie :(

for type_key, type in TYPES.iteritems():
for sub_type_key, sub_type in type.iteritems():
for sig in sub_type['signatures']:
should be:
for type_key, type in TYPES.iteritems():
for sig in type['signatures']:
But 'type' is a poor name choice in this case... you don't want to shadow a builtin.
Essentially, 'type_key' has the name (either 'hotmail' or 'gmail'), and 'type' has the dictionary that is the value associated with that key. So type['signatures'] is what you're wanting.
Also, you may not need to have 'gmail' inside the nested dictionary; just return 'type_key' instead of type['type'].
Bringing it all together, maybe this will work better: (Warning: untested)
providers = {
'hotmail':{
'type':'hotmail',
'lookup':'mixed',
'dkim': 'no',
'signatures':[
'|S|Return-Path: postmaster#hotmail.com',
'|R|^Return-Path:\s*[^#]+#(?:hot|msn)',
'^Received: from .*hotmail.com$']
},
'gmail':{
'type':'gmail',
'lookup':'mixed',
'dkim': 'yes',
'signatures':['|S|Subject: unsubscribe','','','']
}
}
for provider, provider_info in providers.iteritems():
for sig in provicer_info['signatures']:
if ("|S|" in sig):
#String based matching
clean_sig = sig[3:len(sig)]
if (clean_sig in file_contents):
sig_match += 1
elif ("|R|" in sig):
clean_sig = sig[3:len(sig)]
#REGMATCH later
if (sig_match == sig.count):
return provider
return None

[Posted as an answer instead of a comment because retracile beat me to the answer, but the formatting is still a point worth making.]
Laying out the data helps to visualize it:
TYPES = {
'hotmail': {
'type': 'hotmail',
'lookup': 'mixed',
'dkim': 'no',
'signatures': ['|S|Return-Path: postmaster#hotmail.com',
'|R|^Return-Path:\s*[^#]+#(?:hot|msn)',
'^Received: from .*hotmail.com$'],
},
'gmail': {
'type': 'gmail',
'lookup': 'mixed',
'dkim': 'yes',
'signatures': ['|S|Subject: unsubscribe', '', '', ''],
},
}
Note: You can have an ending comma after the last item in a dict, list, or tuple (used above only for the dicts—it's not always more clear), and you don't have to worry about screwing around with that comma, which is a Good Thing™.

Related

Checking value of a field while creating a Pandas data frame & populating it differently, based on type(value)

Good day. I'm using JIRA APIs to get data from JIRA about stories and put it in a dataframe/Excel. There is one particular field "issue.fields.aggregatetimeoriginalestimate" - which can have a "None" type or a "float" value in seconds. Is there a way to dynamically check for this and populate the appropriate value in the Pandas dataframe, using code while the population is going on in a for loop?
Here's what I'm trying to achieve:
jira_issues = jira.search_issues(jql,maxResults=0)
# JSON to pandas DataFrame
issues = pd.DataFrame()
for issue in jira_issues_ncr:
d = {
'Self': issue.self,
'Project': str(issue.fields.project),
'JIRA ID': issue.key,
'Summary': str(issue.fields.summary),
'Original Story Points': str(issue.fields.customfield_15972),
'Story Points': str(issue.fields.customfield_10010),
'Aggregate Orig Estimate (Hrs)': {
if type(issue.fields.aggregatetimeoriginalestimate) != None):
issue.fields.aggregatetimeoriginalestimate/(60.0*60.0)
else:
str(issue.fields.aggregatetimeoriginalestimate)
},
'Original Estimate': str(issue.fields.timeoriginalestimate),
'Remaining Estimate': str(issue.fields.timeestimate),
'Priority': str(issue.fields.priority.name),
# 'Severity': str(issue.fields.customfield_10120),
'Resolution': str(issue.fields.resolution),
'Status': str(issue.fields.status.name),
'Assignee': str(issue.fields.assignee),
'Creator' : str(issue.fields.creator),
'Reporter': str(issue.fields.reporter),
'Created' : str(issue.fields.created),
# 'Found by': str(issue.fields.customfield_11272),
# 'Root cause': str(issue.fields.customfield_10031),
# 'Earliest place to find': str(issue.fields.customfield_11380),
# 'Test Escape Classification': str(issue.fields.customfield_11387),
'Labels': str(issue.fields.labels),
'Components': str(issue.fields.components),
# 'Description': str(issue.fields.description),
# 'FixVersions': str(issue.fields.fixVersions),
'Issuetype': str(issue.fields.issuetype.name),
# 'Resolution_date': str(issue.fields.resolutiondate),
'Updated': str(issue.fields.updated),
# 'Versions': str(issue.fields.versions),
# 'Status_name': str(issue.fields.status.name),
# 'Watchcount': str(issue.fields.watches.watchCount),
}
issues = issues.append(d, ignore_index=True)
Please let me know how this can be achieved inside the for loop, such that:
if the value of the field is not "None", I want to do a calculation (value/(60.0*60.0) and then populate the field "Aggregate Orig Time Estimate (Hrs)" or if it is type "None", then just put the value as is "None" in the data frame? (I guess we could also put a 0.0, if None is found).
I'm a novice in Python so will appreciate any assistance.
When I tried to run this, I get:
d = {
^
SyntaxError: '{' was never closed
The curly brackets { and } are used in Python to define dictionaries. So this part of your code is not valid:
'Aggregate Orig Estimate (Hrs)': {
if type(issue.fields.aggregatetimeoriginalestimate) != None):
issue.fields.aggregatetimeoriginalestimate/(60.0*60.0)
else:
str(issue.fields.aggregatetimeoriginalestimate)
},
You can write it on one line though:
'Aggregate Orig Estimate (Hrs)': issue.fields.aggregatetimeoriginalestimate/(60.0*60.0) if issue.fields.aggregatetimeoriginalestimate else 0.0
From Python 3.8 you can shorten it further with an assigment :=:
'Aggregate Orig Estimate (Hrs)': agg/(60.0*60.0) if (agg:=issue.fields.aggregatetimeoriginalestimate) else 0.0

How to check each key separately from a list in a loop without creating multiple loops. Which may have a KeyError etc

I wrote a code that takes 9 keys from API.
The authors, isbn_one, isbn_two, thumbinail, page_count fields may not always be retrievable, and if any of them are missing, I would like it to be None. Unfortunately, if, or even nested, doesn't work. Because that leads to a lot of loops. I also tried try and except KeyError etc. because each key has a different error and it is not known which to assign none to. Here is an example of logic when a photo is missing:
th = result['volumeInfo'].get('imageLinks')
if th is not None:
book_exists_thumbinail = {
'thumbinail': result['volumeInfo']['imageLinks']['thumbnail']
}
dnew = {**book_data, **book_exists_thumbinail}
book_import.append(dnew)
else:
book_exists_thumbinail_n = {
'thumbinail': None
}
dnew_none = {**book_data, **book_exists_thumbinail_n}
book_import.append(dnew_none)
When I use logic, you know when one condition is met, e.g. for thumbinail, the rest is not even checked.
When I use try and except, it's similar. There's also an ISBN in the keys, but there's a list in the dictionary over there, and I need to use something like this:
isbn_zer = result['volumeInfo']['industryIdentifiers']
dic = collections.defaultdict(list)
for d in isbn_zer:
for k, v in d.items():
dic[k].append(v)
Output data: [{'type': 'ISBN_10', 'identifier': '8320717507'}, {'type': 'ISBN_13', 'identifier': '9788320717501'}]
I don't know what to use anymore to check each key separately and in the case of its absence or lack of one ISBN (identifier) assign the value None. I have already tried many ideas.
The rest of the code:
book_import = []
if request.method == 'POST':
filter_ch = BookFilterForm(request.POST)
if filter_ch.is_valid():
cd = filter_ch.cleaned_data
filter_choice = cd['choose_v']
filter_search = cd['search']
search_url = "https://www.googleapis.com/books/v1/volumes?"
params = {
'q': '{}{}'.format(filter_choice, filter_search),
'key': settings.BOOK_DATA_API_KEY,
'maxResults': 2,
'printType': 'books'
}
r = requests.get(search_url, params=params)
results = r.json()['items']
for result in results:
book_data = {
'title': result['volumeInfo']['title'],
'authors': result['volumeInfo']['authors'][0],
'publish_date': result['volumeInfo']['publishedDate'],
'isbn_one': result['volumeInfo']['industryIdentifiers'][0]['identifier'],
'isbn_two': result['volumeInfo']['industryIdentifiers'][1]['identifier'],
'page_count': result['volumeInfo']['pageCount'],
'thumbnail': result['volumeInfo']['imageLinks']['thumbnail'],
'country': result['saleInfo']['country']
}
book_import.append(book_data)
else:
filter_ch = BookFilterForm()
return render(request, "BookApp/book_import.html", {'book_import': book_import,
'filter_ch': filter_ch})```

allowing the user to input two parameters

i want to allow the user to be able to type {misc} of {nation} and specific information will be displayed based on the input. i have tired several different ways but seem to never find a way to make it work. any help is appreciated thanks!
(Sorry about title didnt really know what to title it)
def part1():
txt = "{} of {}"
info = input('''What do you want to know?: ''')
if info == "{} of {}":
print(txt.format(misc.get(info), nations.get(info)))
else:
print("i dont know what your talking about")
nations = {
'fiji': {
'location': 'Oceana',
'capital_city': 'Suva'},
'france': {
'location': 'Europe',
'capital_city': 'Paris'},
}
misc = {'population': {'fiji': '847,706',
'france': '60,495,540'},
'location': {'fiji': 'Oceana',
'france': 'Europe'},
}
part1()
Not sure exactly what you're trying to do, but there are a few issues with your code, try something like this:
def part1():
txt = "{} of {}"
info = input('''What do you want to know?: ''')
if ' of ' in info:
params = info.split(' of ')
else:
print("i dont know what your talking about")
return
print(params[0])
print(misc.get(params[0]).get(params[1]))
nations = {
'fiji': {
'location': 'Oceana',
'capital_city': 'Suva'},
'france': {
'location': 'Europe',
'capital_city': 'Paris'},
}
misc = {'population': {'fiji': '847,706',
'france': '60,495,540'},
'location': {'fiji': 'Oceana',
'france': 'Europe'},
}
part1()
Currently with this code there isnt really a way to know which object you're targeting (nations or misc). To remedy this I would ask the user an initial question to determine which type of info they want.
Additional improvements could be checking the data you're getting from the dictionaries. In the case you get nothing from the dictionaries (.get returns None) you could notify the user the info you have available).
A quick and dirty way to do this if you are not using much data, is to just put the info directly into the if statements and call the function. For instance:
def part1():
info = input("What do you want to know?: ")
if info == "location of fiji":
print("Oceana:")
elif info == "capital_city of fiji":
print("Suva")
elif info == "location of France":
print("Europe")
elif info == "capital_city of France":
print("Paris")
else:
print("i dont know what your talking about")
part1()
If you are using a lot of data points, then it is probably better to have a list/database like you have though. Here's the quick and dirty fix if you want it though.
(Also, if you use this method, you can convert the user input string into all lowercase using .lower I believe. This way, capitalization does not matter when inputting).
A good way to do this would be if else/elif statements that way the program can check for multiple inputs rather then trying to check for 2 different inputs at once.
Viktor Basharkevich has pretty much the right idea on what to do
You could also try a split method
input().split(x, y)
x, y = input("What do you want to know?").split()
This might work
Or you could try a list

logging recursive dictionary

Having an odd time getting this to work;
I have a dictionary that contains the information for several machines. Based on a parameter, the machine is selected.
I would like to write the selected information to the log. But my attempts at recursion don't seem to get me where I need to go. I get the key but the value fails.
This is the dictionary
CSTU_CFG = {'A07': {
'password': 'CastAIP', # default cast password( too lazy to use LDAP)
'host':'JSIPVWCASTD01',
'port':'2280', # Ports are assumed to be 2280 but can be any
'location': 'C:Users/WDI/Documents/CSTU/DMPRST', # use os.path to convert
'gzips': 'GZIPS', # location for zip files ( Backup )
'schematype':{'local', 'central', 'mngt'},
'logintv': 30,
'version': '0.9'
},
'A01': {
'machine': 'A01',
'password': 'CastAIP', # default cast password( too lazy to use LDAP)
'host':'JSIPVWCASTD01',
'port':'2280', # Ports are assumed to be 2280 but can be any
'location': 'C:Users/WDI/Documents/CSTU/DMPRST', # use os.path to convert
'gzips': 'GZIPS', # location for zip files ( Backup )
'schematype':{'local', 'central', 'mngt'},
'logintv': 30,
'version': '0.9'
},
'A02': {
'machine': 'A02',
'password': 'CastAIP', # default cast password( too lazy to use LDAP)
'host':'JSIPVWCASTD01',
'port':'2280', # Ports are assumed to be 2280 but can be any
'location': 'C:Users/WDI/Documents/CSTU/DMPRST', # use os.path to convert
'gzips': 'GZIPS', # location for zip files ( Backup )
'schematype':{'local', 'central', 'mngt'},
'logintv': 30,
'version': '0.9'
}
}
logname = 'CSTU_'+timestr+'_'+ schemaname + '.CLOG'
logging.basicConfig(filename=logname,filemode='a',format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', datefmt='%H:%M:%S',level=logging.DEBUG)
logging.debug("Starting CSTU_DUMP")
# print the CSTU_CFG file into the log
for key,value in CSTU_CFG:
logging.debug(key + " => " + value)
I'm obviously not getting the point on the logging. I've tried a few of the suggested fixes and I either get nothing, or various errors. I Can hard code it obviously but thats not the intent.
Thanks
You can iterate over dictionary to get key and that key's value:
for k, v in CSTU_CFG.iteritems():
logging.debug(k, v)
To iterate dictionary items you need to access the items first, not iterate the dictionary directly.
You need to change your code (for key,value in CSTU_CFG) as follows.
For Python 2.x:
for key, value in CSTU_CFG.iteritems():
logging.debug(key + " => " + str(value))
For Python 3.x:
for key, value in CSTU_CFG.items():
logging.debug(key + " => " + str(value))
By the way, you said you're getting errors. It may help to include the exact error trace next time in your question.

Checking if epic issue exists and if not, make a new epic issue

So the problem is an IndexError, which makes sense considering there isn't supposed to be any results for that jql query.
epic_search = 'project = "EXM" and type = Epic and summary ~ "summaryx" '
esearch = jira.search_issues(epic_search)
if esearch[0].key == None:
epic_dict = {
'project': {'key': 'EXM'},
'customfield_12345': 'summaryx',
'summary': 'summaryx',
'issuetype': {'name': 'Epic'},
}
new_epic = jira.create_issue(fields=epic_dict)
print (new_epic.key)
Is there a way I can check the jql results and if empty, create an epic?
Probably something like
if (count(esearch) > 0):
I assume this is python. I don't do python but there must be something like a count() or maybe esearch.length to tell you how many items are in there.

Categories

Resources