Match multiple keys values to database entry in TinyDB? - python

I am having a hard time finding out if I can check multiple key values at once with TinyDB. Currently I can check multiple fields by using and in an if statement like this:
def check_table(FNAME="NULL", LNAME="NULL", MNAME="NULL"):
if (HHK_STAFF.search(Query().FNAME == FNAME)) != [] \
and (HHK_STAFF.search(Query().MNAME == MNAME)) != [] \
and (HHK_STAFF.search(Query().LNAME == LNAME)) != []:
print(HHK_STAFF.search(Query().FNAME == FNAME)[0])
else:
print("user does not exist")
check_table(FNAME="Some", MNAME="Random", LNAME="Person")
It does what I want however it seams bulky. I am hoping there is something built into TinyDB that can perform the same function without the need for many and statements.
I am trying to do something akin to:
HHK_STAFF.search(where(("FNAME", "MNAME", "LNAME")) == (FNAME, MNAME, LNAME)))
Question:
Is anyone aware of a way to Query() the table for multiple key values instead of just one at a time?
Can you list a link to this information? I have had a hard time locating this bit of info if it exist.
Here is a simple version of what I am ding with TinyDB:
from tinydb import TinyDB, Query
#~~~~~~~~~~~~~~~~~~~~~~< CURRENT DBPATH >~~~~~~~~~~~~~~~~~~~~~~
DB = TinyDB('./DB_PATH/HHK_DB.json')
#~~~~~~~~~~~~~~~~~~~~~~< CURRENT TABLES >~~~~~~~~~~~~~~~~~~~~~~
HHK_STAFF = DB.table("HHK_STAFF")
HHK_STAFF.insert({'EMPLOYEE_ID':'00000001', 'FNAME': 'Some', 'LNAME':'Person', 'MNAME':'Random'})
def check_table(FNAME="NULL", LNAME="NULL", MNAME="NULL"):
if (HHK_STAFF.search(Query().FNAME == FNAME)) != [] \
and (HHK_STAFF.search(Query().MNAME == MNAME)) != [] \
and (HHK_STAFF.search(Query().LNAME == LNAME)) != []:
print(HHK_STAFF.search(Query().FNAME == FNAME)[0])
else:
print("user does not exist")
check_table(FNAME="Some", MNAME="Random", LNAME="Person")
Results:
{'EMPLOYEE_ID': '00000001', 'FNAME': 'Some', 'LNAME': 'Person', 'MNAME': 'Random'}

According to Advanced Usage — TinyDB 3.8.1.post1 documentation, a logical AND would look like this:
q = Query()
HHK_STAFF.search((q.FNAME == FNAME) & (q.MNAME == MNAME) & (q.LNAME == LNAME))
According to git blame of tinydb/queries.py, it's been available always, since the very first release.

You can use the python builtin all to achieve a shorter, more flexible work-alike to your sample code:
def check_table2(**query):
if all(HHK_STAFF.search(getattr(Query(), k) == v)
for k, v in query.items()):
print(HHK_STAFF.search(Query().FNAME == query['FNAME'])[0])
else:
print("user does not exist")
But you should be sure this is actually what you want. The English equivalent would be,
"If someone with this last name exists, and someone with this first
name exists, and someone with this middle name exists, regardless of
whether they're all the same someone, return the person with the matching first name."
In other words, If I now add another person to your database
HHK_STAFF.insert({'EMPLOYEE_ID':'00000002',
'FNAME': 'Anne',
'LNAME':'Person',
'MNAME':'Other'})
Your function above will return something for the query
check_table(FNAME="Some", MNAME="Other", LNAME="Person")
Even though Some's middle name is "Random" not "Other", just because somebody else exists on the system whose middle name is "Other"
You may instead want to take advantage of Query's overridden __and__ operator to find a single person that has all the names you're querying:
q = Query()
HHK_STAFF.search((q.FNAME=="Anne") &
(q.MNAME=="Other") &
(q.LNAME=="Person"))
Or, for something using key-value like I did above, using functools.reduce:
from functools import reduce
def user_matching_all(**query):
q = Query()
out = HHK_STAFF.search(reduce(lambda x, y: x & y,
[getattr(q, k) == v
for k, v in query.items()]))
if out:
print(out)
else:
print("user does not exist")

Related

Make SQLAlchemy "or_" operator return only the 1st match

guys! I have this one query, written with SQLAlchemy:
query = query.filter(
CollectionCardLink.collection_id == card_id,
CollectionCardLink.position.in_([0, count-1]),
or_(CardStyle.user_id == referrer_id, CardStyle.user_id == ANYONE_USER_ID)
).all()
My issue is: this => or_(PostStyle.user_id == current_user_id, PostStyle.user_id == DEFAULT_USER_ID) returns me two records, cause there are records for current_user_id and for DEFAULT_USER_ID in table too.
But I want or_ to work like it works in Python: stop checking after 1st True. So, I need to get PostStyle for DEFAULT_USER_ID only if there's no PostStyle for current_user_id.
Can someone help, please, with that issue?
Also I tried to code: (PostStyle.user_id == current_user_id or PostStyle.user_id == DEFAULT_USER_ID) but it gives me a record for DEFAULT_USER_ID firstly and that's incorrect

Python nested dictionary not keeping elements in order

I've been trying to create an input library for Selenium using the nested dictionary data type, and while at first it was working perfectly I am now realizing I have gotten myself into a position where I cannot be assured that my elements will stay in order (which is very necessary for this library).
Here is an example of how I am trying to structure this code:
qlib = {
'code_xp':
{'keywords':
{'javascript':0,
'web design':1,
'python':0},
'answer':
{'4',
'yes'}}
}
for prompt, info in qlib.items()
for t, i in enumerate(list(info['answer'])):
if t == 0:
try:
print(i)
except:
pass
If you run this yourself, you will soon realize that after a few runs it will have rearranged the output from the list ['4', 'yes'], switching between ['4'] to ['yes']. Given that I depend on only referencing the first element for certain inputs ('4'), I can't allow this.
As for the 'keywords' section, I have used the structure i.e. 'javascript':0 as a necessary tag element for data processing. While this is not relevant for this problem, any solution would have to account for this. Here is my full data processing engine for those that would like to see the original context. Please note this comes before the 'for' loop listed above:
trs = 'translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")'
input_type = ['numeric', 'text', 'multipleChoice', 'multipleChoice']
element_type = ['label', 'label', 'label', 'legend']
for index, item in enumerate(input_type):
print(f"Current input: {item}")
form_number = driver.find_elements(By.XPATH,'//' +element_type[index]+ '[starts-with(#for, "urn:li:fs_easyApplyFormElement")][contains(#for, "' +item+ '")]')
if form_number:
print(item)
for link in form_number:
for prompt, info in qlib.items():
keywords = info['keywords']
full_path = []
for i, word in enumerate(keywords):
path = 'contains(' +trs+ ', "' +word+ '")'
if i < len(keywords) - 1:
if keywords[word] == 1:
path += " and"
elif keywords[word] == 0:
path += " or"
full_path.append(path)
full_string = ' '.join(full_path)
answer = ' '.join(info['answer'])
I've been trying to find the right datatype for this code for a while now, and while this almost works perfectly the problem I'm facing makes it unusable. I've considered an OrderedDict as well, however I am not confident I can keep the structures that I depend on. Looking for anything that will work. Thank you so much for any help!

How to save results from a loop separately?

I need my results to be saved separately so that I can run the rest of my code.
Basically, I have to do a project on loans and each type of loan would have a different monthly repayment amount. The problem now is that each user might have a different number of different loans. For example, one user might only have home loan (HL) while one user might have 3 different loans. In my codes, I need to include this which would return the monthly repayment amount for that particular type of loan:
def meet_criteria(account_no):
if getsaving(account_no) in ["HL"]:
HLmonthly()
else:
if getsaving(account_no) in ["RL"]:
RLmonthly()
else:
if getsaving(account_no) in ["EL"]:
ELmonthly()
I also need to sum up the repayment amount like this:
total_monthly_debt = repay1 + repay2 + repay3
However, right now, I can't to proceed with above codes as I am unable to separate the results to get the 'HL', 'RL' and 'EL' that I need to call the meet criteria(). In order to get all the results to show, I am using a loop like so:
def getsaving(account_no):
query = """\
select SUBSTRING(LoanID, 1, 2)
from Account_Delinquency
where Account_Number = '{}'
""" .format(account_no)
return (execute_read_query (conn, query))
a = getsaving(account_no)
mylist = []
for i in a:
mylist.append(i)
I tried to put it in a list so that I could separate it but obviously, it doesn't work that way. This was a method I learnt to check if a list is empty but as expected, it can't check if parts of the list is empty and would return error. It also involves a little hardcoding which is not ideal.
if mylist[1][0]:
loan1 = mylist[1][0]
else:
pass
Hence, I am asking to see if there are any other methods I can use so that I can get a result of, for example, Loan1 = HL, Loan2 = nothing, Loan3 = RL etc. I would also appreciate any suggestions for changes to my code as I know it is not the most ideal. Thank you!
meet_criteria seems incorect (no return value)
def meet_criteria(account_no):
if getsaving(account_no) in ["HL"]:
HLmonthly()
else:
if getsaving(account_no) in ["RL"]:
RLmonthly()
else:
if getsaving(account_no) in ["EL"]:
ELmonthly()
could be
def meet_criteria(account_no):
if getsaving(account_no) == "HL":
HLmonthly()
elif getsaving(account_no) == "RL":
RLmonthly()
elif getsaving(account_no) == "EL":
ELmonthly()
Btw. I haven't fully understood your question, but I think that you want meet_criteria to return something, (I would know for sure if you provided how you use this function).
So instead do
def meet_criteria(account_no):
if getsaving(account_no) == "HL":
return HLmonthly()
elif getsaving(account_no) == "RL":
return RLmonthly()
elif getsaving(account_no) == "EL":
return ELmonthly()
Further improvements:
if mylist[1][0]:
loan1 = mylist[1][0]
else:
pass
The else branch doesn't do anything. Also, if you haven't defined loan1 before and you are using it later, you would get NameError: name 'loan1' is not defined.
To remove else branch:
if mylist[1][0]:
loan1 = mylist[1][0]
To not get NameError
if mylist[1][0]:
loan1 = mylist[1][0]
else:
loan1 = 0 # if it is suposed to be integer, maybe you want None of False

Reading ahead through a SQL query while looping in Python

The script executes a SQL query and returns a result like below:
subtract,'a','wrong'
subtract,'b','wrong'
add,a,'wrong'
add,b,'wrong'
add,c,'correct'
add,d,'wrong'
subtract,'a','wrong'
subtract,'b','wrong'
I loop through the results to read it line by line and store each element in a variable, but this is where I have no clue what to do next.
flag = 0
for rec in allRecords:
operation = rec[0]
value = rec[1]
answer = rec[2]
#if flag == 1:
#pass
#else:
if operation == 'add':
#start an inside loop to 'read ahead' and continue if operation == 'add' and stop when operation != 'add'
#find 'c' inside this loop and get the 'correct' element which is next to it and store in a new variable.
#break the loop to go back to main loop
#getVar = 'correct'
#print(getVar)
#flag = 1
else:
flag = 0
#after breaking out of the loop above, continue to the next records
print(rec)
Desired Output:
correct
add,a,'wrong'
add,b,'wrong'
add,c,'correct'
add,d,'wrong'
Why am I doing this?
I want to display the correct answer first and then list the rest of the options. Also practicing programming.
Why am I asking here?
I've exhausted all resource and I'm really stuck and in need of guidance. I googled all the errors I received from all of the trial and error I did and could not find an answer.
Tried my best to explain. I'm quite new to programming and just learning python. Appreciate your help.
One way is to traverse the records twice:
for operation, value, answer in allRecords:
if answer == 'correct':
print 'correct'
for operation, value, answer in allRecords:
if answer != 'correct':
print (operation, value, answer)
You could first make a subset of all additions, and then get the correct answer from that subset.
# Filter all additions from the results
additions = [[o, v, a] for o, v, a in results if o == 'add']
# Filter the addition with the correct answer
correct = [v for o, v, a in additions if a == 'correct']
# Output information
print "Correct : {}".format(correct)
for addition in additions:
print "{}, {}, {}".format(addition[0], addition[1], addition[2])
The code above outputs,
Correct : ['c']
add, a, wrong
add, b, wrong
add, c, correct
add, d, wrong

Iterating data store entities conveniently

Please educate me on how to do this the right way, as I feel my current way is long-winded.
I know iterating over all entities in App Engine is not quite how it is designed to be used, but sometimes I want to gather statistics about my entities, for example how many users are female. In reality the criteria might be something more complicated, but in any case something that requires examining each entity.
Here is some pseudoish code on how I am iterating over entities:
def handle_count_female_users(cursor = None, counter = 0):
q = User.all()
if cursor:
q.with_cursor(cursor)
MAX_FETCH = 100
users = q.fetch(MAX_FETCH)
count_of_female_users = len(filter(lambda user:user.gender == 'female', users))
total_count = counter + count_of_female_users
if len(users) == MAX_FETCH:
Task(
url = "/count_female_users",
params = {
'counter' : str(total_count),
'cursor' : q.cursor()
}
).add()
else:
# Now finally have the result
logging.info("We have %s female users in total." % total_count)
I have routing code that automatically maps GET /foo to be handled by handle_foo, something that I've found convenient. As you can see, even with that I have a lot of stuff supporting the looping, having almost nothing to do with what I actually want to accomplish.
What I would really want to do is something like:
tally_entities(
entity_class = User,
filter_criteria = lambda user:user.gender == 'female',
result = lambda count:logging.info("We have %s female users in total" % count)
)
Any ideas how to get closer to this ideal, or is there some even better way?
Sounds like a good use case for mapreduce:
http://code.google.com/p/appengine-mapreduce/wiki/GettingStartedInPython

Categories