Matching dict key to text file and returning Test Pass/fail - dictionary

I'm a novice at Python, and am currently working on a small test case assignment where I am to find and match the dictionary keys to a small text file, and see if the keys are present in the text file.
As follows, the dictionary goes:
dict = {"description, translation": "test_translation(serial,",
"unit": "test_unit(",}
The text in text file, henceforth called "requirement.txt" as follows:
The description shall display the translation of XXX.
The unit shall be hidden.
The value is read from the file "version.txt".
To the key, I am to find and match if they are present or absent - a match should return a "test pass", no match would return a skip.
Keys from dictionary are to be sorted to a list, then iterated and matched to text. (Values from dictionary are to be sorted to a seperate list and iterated over a seperate file, to which I shall not delve into it here.)
This is the code that I currently have (and stuck):
list = sorted(key_words.keys(), key=lambda d: d[0])
with open('C:/Users-------/requirement.txt', 'r') as outfile:
lines = outfile.readlines()
for line in lines:
line = line.strip()
if line == '':
continue
line_strings = line.split(' ')
for word in list:
if word in line:
print("Test Pass")
print(word)
break
else:
print("Test Fail")
print(line + "\n")
Result currently obtained:
Test Fail
Test Pass
display
The description shall display the translation of XXX.
Test Fail
Test Fail
Test Fail
Test Pass
unit
The unit shall be hidden.
Test Fail
Test Fail
Test Fail
Test Fail
The value is read from the file "version.txt".
Using the current code which I have, (and I am stuck), running the code returned multiple times of "Test pass" and "Test fail", suggesting that the keys are iterated multiple times over each line and the results returned for each multiple iteration.
I am stuck at two fronts:
After seperating the key into a list, how to order them in the sequence of "description, translation", "unit)?
How to modify the code so as to ensure that result is returned once as "Test pass" or "test fail"
Results should ideally return in the following format:
Ideal outcome:
('Text:', "The description shall display the translation of XXX.
('Key:', 'description, translation')
Test Pass
('Text:', 'The unit shall be hidden.')
('Key:', 'unit')
Test Pass
('Text:', 'The value is read from the file "version.txt".')
('Key:', (none))
Test Fail
For your kind enlightenment please, thank you!

Try with this:
list = sorted(key_words.keys(), key=lambda d: d[0])
with open('C:/Users-------/requirement.txt', 'r') as outfile:
lines = outfile.readlines()
for line in lines:
line = line.strip()
if line == '':
continue
# Create an empty list which will contain all the word that match
words_found = []
for word in list:
# if the word match then add it to the list words_found
if word in line:
words_found.append(word)
print("(\'Text:\',\"{}\"")' ".format(line))
print("(\'Keys:\',\"{}\"")' ".format(words_found))
# if the list of words found it's not empty then the test passed
if(words_found):
print("Test Passed")
else:
print("Test Failed")
the idea is to create a list of the words founds and then print them all
I'm using the format Operation and you can find a guide on how to use it here. And the line if(words_found): check if the list is empty.
Additional Notes
In this case, you won't need it but if you wanted to solve only the second point you can use the for else statement as explained in the docs
4.4 break and continue Statements, and else Clauses on Loops
Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement.
Reducing by one tab the indentation the else of your if statement it became the else of the for statement so it will be executed only if the for never had a break the problem is solved.
list = sorted(key_words.keys(), key=lambda d: d[0])
with open('C:/Users-------/requirement.txt', 'r') as outfile:
lines = outfile.readlines()
for line in lines:
line = line.strip()
if line == '':
continue
line_strings = line.split(' ')
for word in list:
if word in line:
print(word)
print("Test Pass")
break
else:
print("Test Fail")
print(line + "\n")
Edit
To split the key into description and translation we just have to split the two word at the comma with the builtin function split
list = sorted(key_words.keys(), key=lambda d: d[0])
with open('C:/Users-------/requirement.txt', 'r') as outfile:
lines = outfile.readlines()
for line in lines:
line = line.strip()
if line == '':
continue
# Create an empty list which will contain all the word that match
words_found = []
for word in list:
description, translation = word.split(",")
# if the word match then add it to the list words_found
if description in line:
words_found.append(description)
print("(\'Text:\',\"{}\"")' ".format(line))
print("(\'Keys:\',\"{}\"")' ".format(words_found))
# if the list of words found it's not empty then the test passed
if(words_found):
print("Test Passed")
else:
print("Test Failed")

Related

Received MethodError when cleaning String

I have data in a .txt file that looks like this:
04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]
25:31 Heatbeat & Quilla – Secret (Original Mix) [ARMADA CAPTIVATING]
All of them have this pattern:
00:00 artist - title [studio]
I want to remove the time stamp and the studio, so the output looks like this:
1. Yuri Kane feat Jeza – Love Comes (Original Mix)
Here is what I tried:
function remove_time_from(str::String)
return last(split(str,"0 "))
end
function remove_url(str::String)
return first(rsplit(str,"["))
end
function main()
tracks = String[]
local number = 0
for line in eachline("track-list.txt")
number += 1
removed_time = remove_time_from(line)
cleaned = remove_url(removed_time)
push!(tracks,"$number.$cleaned")
end
open("track-list-cleaned.txt", "w") do io
for line in tracks
write(io, "$line\n")
end
end
end
main()
but it returns:
MethodError: no method matching remove_url(::SubString{String})
When you use the function remove_time_from() it uses first() which returns a SubString{String}:
track = "04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]"
println(typeof(remove_time_from(track))) # Output: SubString{String}
You have 2 ways to fix it:
Have both remove_time_from() and remove_url() convert the SubString to String before returning it. This way, no matter which function you use first, you'll get a String:
return convert(String,last(split(str,"0 ")))
Use AbstractString instead of String as the function parameter, because SubString is a subtype of AbstractString:
println(SubString <: AbstractString) # Output: true
This way, no matter which function you use first, it would accept a String (the variable type of line) or SubString (the type you end up with after using one of the functions).
Suggestions:
Using split(str,"0 ") won't remove the time stamp:
last(split("04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]", "0 "))
Output: 04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]
What you need is chop() and you can specify how many characters to ignore from the head, so in this case 5 (includes the leading whitespace).
chop(str, head = 5)
You don't need to read in the lines, clean it, and then store it in a Vector to write later. You can clean it (do it in one line), and write it out to the file:
open("track-list-cleaned.txt", "w") do io
for line in eachline("track-list.txt")
number += 1
cleaned = (remove_url(remove_time_from(line)))
write(io, "$number.$cleaned\n")
end
end
Use enumerate() to number the lines as you're reading them in:
for (number,line) in enumerate(eachline("track-list.txt"))
Code:
# Using the assignment form because each function has only one line.
remove_time_from(str::AbstractString) = chop(str, head = 5)
remove_url(str::AbstractString) = first(rsplit(str," https"))
function main()
open("track-list-cleaned.txt", "w") do io
for (number,line) in enumerate(eachline("track-list.txt"))
cleaned = strip(remove_url(remove_time_from(line)))
write(io,"$number.$cleaned\n")
end
end
end
main()

SQLite3 with Flask Insert only allowing 1 character

I'm using Flask and trying to insert names (text) into an SQLite3 DB. The DB has one table (guests) and one column (name). However, it will only accept 1 character during the insert; anything more is failing.
If I hardcode 'nm' as a value, it still fails, so I don't think it's the Html template. I can manually add a value of varying length to the DB with the 'DB Browser' app, so I'm really at a loss here. Here's a code snippet.
#app.route('/addrec', methods=['POST', 'GET'])
def addrec():
msg = "msg"
if request.method == 'POST':
try:
nm = request.form['nm']
with sqlite3.connect("database.db") as con:
cur = con.cursor()
cur.execute('''Insert into guests values (?)''', nm)
con.commit()
msg = "Record successfully added"
Second argument to cur.execute should be a tuple:
cur.execute('''Insert into guests values (?)''', (nm,))
Why? If, for example, you call that argument arg then arg[0] is the string value you require (nm): the first item in that tuple.
Otherwise (when passing nm instead of the tuple) it will read nm[0] which is the first character of the string nm.

How to error check a function containing a SQL query with the placeholder %s

I am working on a project for school and run into a crossroad. I have a bunch of queries in a python script that a user can search when running the script. A couple of these function are using %s as a placeholder so that the user can enter a value.
However, I want to check that what they enter is actually in my database. For instance, if you ask a user for a movie genre, it should produce an error if they enter something that is not in my tables.
I have spent a few days trying to find a way to do it, with no luck. I am not the greatest with functions, and I get things mixed up some times.
To simplify things I copied one of my queries requiring user input for testing. I thought I had come up with a solution on how to error check, but no luck. I have pasted the code below. If you know what I am missing, your help would be much appreciated.
#!/usr/bin/python36
#Modules
############################
import psycopg2
import sys
import os
#Open Database
############################
global cursor
#def OpenDatabase():
try:
connection = psycopg2.connect(database='Blockbuster36', user='dbadmin')
cursor = connection.cursor()
except psycopg2.DatabaseError:
print("No connection to database.")
sys.exit(1)
#Functions
###########################
def Show_Tuples():
tuple = cursor.fetchone()
while tuple != None:
print(tuple)
tuple = cursor.fetchone()
#3) Display all films of a certain genre
def Qry3_Film_of_Genre(Genre_Choice):
Qry3 = '''select Film_Title, Genre_Name from Film
join Film_Genre
on Film.Film_ID = Film_Genre.Film_ID
join Genre
on Film_Genre.Genre_ID = Genre.Genre_ID
where Genre_Name = %s;'''
cursor.execute(Qry3, (Genre_Choice,))
Show_Tuples()
def Qry3_Error_Check(Genre_Choice):
try:
Qry3_ec = "select Genre_ID, Genre_Name from Genre where Genre_Name = %s;"
cursor.execute(Qry3_ec, (Genre_Choice,))
results = cursor.fetchone()
if results[0] > 0:
print()
Qry3_Film_of_Genre(Genre_Choice)
# else:
elif results[0] == None:
print("This Genre does not exist")
Genre_Choice = input("Please enter the Genre name to filter movies by: ")
except psycopg2.Error as query_issue:
print("Something wrong with the query", query_issue)
except Exception as unkown:
print("Error: Something else went wrong")
print("Here is the issue: ", unkown)
#Main Code
##########################
Genre_Choice = input("Please enter the Genre name to filter movies by: ")
Check_Genre = Qry3_Error_Check(Genre_Choice)
#print("Function Return: ", Check_Genre)
#print()
#print(Check_Genre)
#if Check_Genre == None:
# print("This Genre does not exist")
# Genre_Choice = input("Please enter the Genre name to filter movies by: ")
# Check_Genre = Qry3_Error_Check(Genre_Choice)
#elif Check_Genre != None:
# Qry3_Film_of_Genre(Genre_Choice)
#while Check_Genre != Genre_Choice:
# print("This Genre does not exist")
# Genre_Choice = input("Please enter the Genre name to filter movies by: ")
# Check_Genre = Qry3_Error_Check(Genre_Choice)
#if Check_Genre == None:
# Qry3_Film_of_Genre(Genre_Choice)
#Close Cursor and Database
###################################
cursor.close()
connection.close()
Essentially I want the error message to keep printing, along with entering another genre name, until a valid genre is entered. Right now it keeps saying it is wrong even if enter a valid genre name and get output with the Qry3_Error_Check function.
Once the user has entered a valid genre name, based on the error-checking function query, then the original query will appear.
I have made some progress, entering a valid genre now works. However, when entering an invalid genre name it jumps to the general except and prints the error "NoneType object is not subscriptable." Obviously, there are no rows that match, thus the NoneType error. However, it should re-prompt the user in the elif statement above. What do enter as an elif statement so that the user is re-prompted for a valid genre name?
Note: I commented out the bulk of my main code for now.
If you want to check specifically for SQL errors, you can have a separate except block for them. Also, its usually (but not always) a bad idea to use exceptions for control flow.
Something like below (I have not tested this) is probably close to what you need.
try:
Qry3_ec = "select Genre_ID, Genre_Name from Genre where Genre_Name = %s;"
cursor.execute(Qry3_ec, (Genre_Choice,))
results = cursor.fetchall()
if results.count > 0:
print(results)
else:
print("Nothing found!")
except psycopg2.Error as e:
print("Something wrong with the query")
print(e)
except Exception as e
print("Something else went wrong")
print(e)
Good luck with your project. :)

Update dictionary key inside list using map function -Python

I have a dictionary of phone numbers where number is Key and country is value. I want to update the key and add country code based on value country. I tried to use the map function for this:
print('**Exmaple: Update phone book to add Country code using map function** ')
user=[{'952-201-3787':'US'},{'952-201-5984':'US'},{'9871299':'BD'},{'01632 960513':'UK'}]
#A function that takes a dictionary as arg, not list. List is the outer part
def add_Country_Code(aDict):
for k,v in aDict.items():
if(v == 'US'):
aDict[( '1+'+k)]=aDict.pop(k)
if(v == 'UK'):
aDict[( '044+'+k)]=aDict.pop(k)
if (v == 'BD'):
aDict[('001+'+k)] =aDict.pop(k)
return aDict
new_user=list(map(add_Country_Code,user))
print(new_user)
This works partially when I run, output below :
[{'1+952-201-3787': 'US'}, {'1+1+1+952-201-5984': 'US'}, {'001+9871299': 'BD'}, {'044+01632 960513': 'UK'}]
Notice the 2nd US number has 2 additional 1s'. What is causing that?How to fix? Thanks a lot.
Issue
You are mutating a dict while iterating it. Don't do this. The Pythonic convention would be:
Make a new_dict = {}
While iterating the input a_dict, assign new items to new_dict.
Return the new_dict
IOW, create new things, rather than change old things - likely the source of your woes.
Some notes
Use lowercase with underscores when defining variable names (see PEP 8).
Lookup values rather than change the input dict, e.g. a_dict[k] vs. a_dict.pop(k)
Indent the correct number of spaces (see PEP 8)

scanString end location: why it is end_index+1?

python/pyparsing
When I use scanString method, it is giving the start and end location of the matched token, in the text.
e.g.
line = "cat bat"
pat = Word(alphas)
for i in pat.scanString(line):
print i
I get the following:
((['cat'], {}), 0, 3)
((['bat'], {}), 4, 7)
But cat end location should be "2" right? Why it is reporting the next location as the end location?
This is consistent with Python's [begin:end] slicing conventions, where the "end" is the index of the next character. By putting the end as the next location, it is very straightforward to extract the matching substring using the returned values:
for t,start,end in pat.scanString(line):
print line[start:end]
You can see how this is used if you look in the pyparsing source code for the implementation of transformString.

Resources