Python SQL score reader - python

I have a function within my "students' test" program.
This function reads out either the "highest score", "lowest score" or "average score" when you enter the student's name and class number.
The first two parts work but i can't seem to get the average working, when i read a value from a database it never returns like i would prefer it to, it returns it like: [(6,) , (3,)].
This is the function below:
def readScore():
selection = input("What score would you like the read ? \n 1 - Lowest score \n 2 - Highest score \n 3 - Average score... ")
name = input("Please enter the name of the pupil...")
classNo = input("Please enter the class of the pupil...")
myFile = sqlite3.connect("scores.db")
c = myFile.cursor()
c.execute('SELECT score FROM scores WHERE name = (?) AND classNo = (?)', (name, classNo,))
row = c.fetchall()
print(row)
try:
if selection == "1":
print (min(row))
if selection == "2":
print (max(row))
if selection == "3":
print (sum(row) /len(row))
except:
print("No scores for that student")
myFile.commit()
myFile.close()

You could compute the average in your program, but it would be a better idea to avoid fetching all rows, and to let the database compute these values:
c.execute('SELECT MIN(score) FROM ...') # or
c.execute('SELECT MAX(score) FROM ...') # or
c.execute('SELECT AVG(score) FROM ...')
row = c.fetchone()
print(row[0])

Related

Python not outputting results of a sql query

I am trying to write a program that asks for a user input of either a first name and last name or a customer ID in a sql database. The first part if the user inputs a customer ID, I seem to have figured out, and the program runs and outputs desired output. However, if the user inputs the choice to do it by name, the program works up until when it is supposed to output the results of the sql query. At which point the program just ends. I can't seem to figure out why it is not outputting the sql query for the lift of me. Here is my code:
while (True): # Begins a while loop to validate the user inputs a correct value.
ui = input("Would you like to lookup the customer by Customer ID (1) or Name (2)? >")
try: # Tries to convert the users input into an integer if possible
ui = int(ui)
except: # If not possible it informs the user their input was invalid and tries again.
print("Invalid selection.")
if (ui == 1): # Breaks the while loop if a valid input is detected.
break
elif (ui == 2): # Breaks the while loop if a valid input is detected.
break
else:
print("Please enter either 1 or 2 to make a selection.")
if(ui == 1):
while(True):
cid = input("What is the Customer ID? >")
try: # Attempts to turn the input into an integer.
cid = int(cid)
cursor.execute("SELECT C.CustID, C.Fname, C.Lname, C.Gender, C.CustState, U.Population FROM Customer C INNER JOIN USState U ON C.CustState = U.StateID WHERE C.CustID = %d" % (cid))
for x in cursor: # If a valid integer is entered for customer ID, a SQL Query is outputted with the results matching the CustID.
print("Customer ID: %d, First Name: %s, Last Name: %s, Gender: %s, State: %s, State Population: %s." %(x[0],x[1],x[2],x[3],x[4],int(x[5])))
break # The while loop is broken.
except:
print("Please enter an integer.")
elif(ui == 2):
cid = input("What is the customer's first name? (press enter for blank) >")
xid = input("What is the customer's last name?")
cursor.execute("SELECT C.CustID, C.Fname, C.Lname, C.Gender, C.CustState, U.Population FROM Customer C INNER JOIN USState U ON C.CustState = U.StateID WHERE C.Fname = '%s' AND C.Lname = '%s'" % (cid, xid))
for x in cursor:
print("Customer ID: %d, First Name: %s, Last Name: %s, Gender: %s, State: %s, State Population: %s." %(x[0],x[1],x[2],x[3],x[4],int(x[5])))

How to interate over an output list from fetchone() in mysql database

I am currently creating a main menu that acts like a bank account.
def main_menu():
print("Main Menu")
print("0 - Quit")
print("1 - Open Account")
print("2 - Check Balance")
print("3 - Close Account")
loop = True
while loop:
main_menu()
choice = input("Enter your choice: ")
choice = int(choice)
if choice == 0:
exit(loop)
elif choice == 1:
name_on_account = input("Name on account: ")
balance = float(input("Enter Initial Balance: "))
print("---Account successfully created---")
print("Account number:", account_no())
print("Name on account:", name_on_account)
print("Balance:", balance)
print("Account opened on:", now)
cur.execute("""
INSERT INTO account(name_on_account, balance) VALUES
("%s", "%s");""", (name_on_account, balance))
connection.commit()
elif choice == 2:
print("Checking account balance")
account_number = input("Enter account number: ")
print("---Checking account balance---")
print("Account number:", account_number)
cur.execute("""SELECT * from account;
""")
account_no1 = cur.fetchone()
for i in account_no1[0]:
if account_number == i:
cur.execute("""select name_on_account from account where account_no = "%s";
""", (account_number,))
name1 = cur.fetchone()
print(name1)
name2 = ''.join(map(str,name1))
name3 = int(name2)
print("Name on account:", name3)
cur.execute("""select balance from account where account_no = "%s";
""", account_number)
balance1 = cur.fetchone()
balance2 = ''.join(map(str,balance1))
balance3 = int(balance2)
print("Balance:", balance3)
cur.expecute("""select account_open_date from account where account no = "%s";
""", account_number)
date1 = cur.fetchone()
date2 = ''.join(map(str, date1))
date3 = int(date2)
print("Account opened on:", date3)
connection.commit()
else:
print("Error: Invalid account number")
I'm not worried about option 3 as of right now, but I am having trouble with option 2.
When a person pick option 1, they will input their name and the amount of money deposited in their bank account.
This information will be stored in the mysql table account(account_no, name_on_account, balance, account_open_date, account_status).
This means that account_no is auto-incremented, account_open_date is default as curdate(), and account_status is default to be "open").
In option 2 however, when a person input their account number; it should return back all of their information how it is displayed in the option 1.
What I am having trouble with is, how do you efficiently iterate over the person's information using fetchone() and be able to get the specific column information with (account_no = account_number) (if you do have a better suggestion on a better way to implement this, please comment below)
This is the error message that I get:
Traceback (most recent call last):
File "(deleted for privacy purposes)"
for i in account_no1[0]:
TypeError: 'int' object is not iterable
Thank you for the help!
pymysql (and most other python mysql clients) is returning a tuple when you invoke fetchone(), and each entry in the tuple matches up to the datatypes defined in the table that you're querying. So let's look at your code:
elif choice == 2:
print("Checking account balance")
account_number = input("Enter account number: ")
print("---Checking account balance---")
print("Account number:", account_number)
cur.execute("""SELECT * from account""")
account_no1 = cur.fetchone()
for i in account_no1[0]:
Here you're querying all columns and all rows from the account table, reading the first row, and then iterating over whatever is in the first column of that row. But that's probably just an id column, which is probably why you're getting the error that you're getting: integers are not iterable. That'd be like writing for i in 10, Python doesn't understand what that means
If you want to iterate over all of the returned rows, you can do that like this:
cur.execute("""SELECT * from account;
""")
for row i in cur.fetchall():
# Each iteration "row" is a tuple of values corresponding to the table's columns
That's kind of silly though. You'd returning all of the rows from the database and then iterating over them and looking for a specific account number. Why not just do that as part of your query?
nrows = cur.execute("SELECT * from account where account_no=%s", (account_number,))
if nrows == 0:
# Account number not found, do something
else:
row = curs.fetchone()
# row now contains all of the values from the discovered
Note that you don't need to put quotation marks around %s placeholders, and you don't need semicolons. The client does all of this conversion for you.
Also note that you should not select * from tables. What if the table definition changes to include more columns? Just select the handful of columns that you actually need, then if more columns are added to the table later your code won't need to be changed at all and you won't be requesting more data than you need. So more like:
nrows = cur.execute("SELECT name, balance, date_opened from account where account_no=%s", (account_number,))
if nrows > 0:
name, balance, date_opened = curs.fetchone()

How can I successfully pass a variable into my sqlite SELECT statement?

I am writing a small program that lets me search a sqlite database, assets by asset number, serial number, etc. The problem is that I'm not sure has to pass user input, a variable assetNum, into my sqlite SELECT statement. I would like it to take the user-queried assetNum and return that row from the sqlite table.
First, I defined my function:
def search_asset_num(conn):
cur = conn.cursor()
cur.execute("SELECT * FROM assets WHERE AssetNumber = '{}'".format(assetNum))
rows = cur.fetchall()
for row in rows:
print(row)
In the program I utilize user-input to move through menus, so later on in the program when I get to the "search by asset menu" I have:
elif selection == '2':
menu2 = {}
menu2[' 1'] = "Search Assets by Name"
menu2[' 2'] = "Search Assets by Asset Number"
menu2[' 3'] = "Search Assets by Serial Number"
menu2[' 4'] = "Search Assets by Ownership"
menu2[' 5'] = "Go back!"
choices2 = menu2.keys()
for entry in choices2:
print(entry, menu2[entry])
selection2 = input("Enter number: ")
if selection2 == '2':
assetNum = input("Enter Asset Number: ")
search_asset_num(conn)
The problem, from what I can tell, is that assetNum is present in the search_asset_num() function above where it is actually defined in the if statement as assetNum = input("Enter Asset Number: "). I'm not sure how to work around this. I have tried making assetNum global at the very top of my code and setting it to equal 0 as a placeholder, but that did not work. It just outputs 0 to the terminal.
Add assetNum as parameter to your function,
def search_asset_num(conn, assetNum):
cur = conn.cursor()
cur.execute("SELECT * FROM assets WHERE AssetNumber = ?", (assetNum,))
rows = cur.fetchall()
for row in rows:
print(row)
in your menu, you can change as below:
if selection2 == '2':
assetNum = input("Enter Asset Number: ")
search_asset_num(conn, assetNum)
also use "Parameter Substitution" as stated in Python sqlite3 - DB-API documentation

Problems with connection database

when I run this program, it doesn't change the value in my database. Is it because something is wrong with the options section or is there the other problem? I don"t know what it is, I hope someone can help me with this.
import sqlite3
def product_kopen(crsr):
print ("Which product would you like to buy?")
product = str(input(""))
print ("And how many?")
wanted_amount = int(input(""))
crsr.execute("SELECT * FROM emp")
rows = crsr.fetchall()
for row in rows:
if row[0] == product:
actual_amount = int(row[1])
fetched_amount = actual_amount - wanted_amount
kopen = "UPDATE emp SET Amount = {amount} WHERE Product = '{name}' ".format(name=product,
amount=fetched_amount)
crsr.execute(kopen)
print("You have succesfully bought your product!", "\n")
def product_verkopen(crsr):
print ("Which product would you like to sell?")
sold_product = str(input(""))
print("And how many?")
wanted_sold_amount = int(input(""))
crsr.execute("SELECT * FROM emp")
rows = crsr.fetchall()
for row in rows:
if row[0] == sold_product:
actual_amount = int(row[1])
fetched_amount = actual_amount + wanted_sold_amount
kopen = "UPDATE emp SET Amount = {amount} WHERE Product = '{name}' ".format(name=sold_product,
amount=fetched_amount)
crsr.execute(kopen)
print("You have succesfully sold your product")
connection = sqlite3.connect("Stock.db", timeout=10)
crsr = connection.cursor()
connection.commit()
while True:
print("Welcome to this shop! choose your option", "\n",
"1. Buy a product.", "\n", "2. Sell a product")
option = int(input(""))
if option == 1:
product_kopen(crsr)
elif option == 2:
product_verkopen(crsr)
else:
print("This isn't a valid option", "\n")
continue
connection.close()
After you make changes to the database, you must commit them. sqlite3 does not commit changes by default. Every time you do cursor.execute(...), follow it up with a cursor.commit()
crsr.execute(kopen)
crsr.commit()
See here for more information https://docs.python.org/3/library/sqlite3.html

storing 3 values in MYSQL

My problem is that i have created a maths quiz that will ask the user 10 random questions and then it will output their score out of 10 at the end.I have done this art of the task but i struggle with the last part.The last part is that i need to create something that will store the last 3 scores for each user.I decided to use mysql but my code will only let me store 1 score for each user.I use python 3.4.Here is the code.
import random
import operator
import mysql.connector
cnx = mysql.connector.connect(user='root', password='password',
host='localhost',
database='mydb')
cursor = cnx.cursor()
ID = input("what is your ID?")
OPERATIONS = [
(operator.add, "+"),
(operator.mul, "x"),
(operator.sub, "-")
]
NB_QUESTIONS = 10
def get_int_input(prompt=''):
while True:
try:
return int(input(prompt))
except ValueError:
print("Sorry,but we need a number")
if __name__ == '__main__':
name = input("What is your name?").title()
Class=input("Which class do you wish to input results for 1,2 or 3?")
print(name, ", Welcome to the Maths Test")
score = 0
for _ in range(NB_QUESTIONS):
num1 = random.randint(1,10)
num2 = random.randint(1,10)
op, symbol = random.choice(OPERATIONS)
print("What is", num1, symbol, num2)
if get_int_input() == op(num1, num2):
print("Correct")
score += 1
else:
print("Incorrect")
print("Well done", name, "you scored", score, "/", NB_QUESTIONS)
print ("Thank you for doing this mathamatical quiz , goodbye ")
if "ID" in "Class1":
if "score" in "Score1":
add_record = ("INSERT INTO Class1"
"(Score2)"
"VALUES(%s)")
data_record = (score)
if "score" in "Score2":
add_record = ("INSERT INTO Class1"
"(Score3)"
"VALUES(%s)")
data_record = (score)
else:
add_record = ("INSERT INTO Class1"
"(ID, Name, Score1) "
"VALUES (%s, %s, %s)")
data_record = (ID, name, score)
cursor.execute(add_record, data_record)
cnx.commit()
cursor.close()
cnx.close()
In my databse i have the columns ID,name,score1,score2,score3
when i complete the quiz the score,name and ID will be input into the table.But once the user with the same ID does the quiz there is a error.I want the code to store 3 scores for each user but there is a error.The error is:
cursor.execute(add_record, data_record)
NameError: name 'add_record' is not defined
Thank you for reading this and thank you also for the help.I look forward to hearing the replies.
if "ID" in "Class1":
if "score" in "Score1":
add_record = ("INSERT INTO Class1"
"(Score2)"
"VALUES(%s)")
data_record = (score)
if "score" in "Score2":
add_record = ("INSERT INTO Class1"
"(Score3)"
"VALUES(%s)")
data_record = (score)
else:
add_record = ("INSERT INTO Class1"
"(ID, Name, Score1) "
"VALUES (%s, %s, %s)")
data_record = (ID, name, score)
cursor.execute(add_record, data_record)
cnx.commit()
cursor.close()
cnx.close()
Alright, we are going to step through my solution in the smallest steps possible to reach your solution. Note: All this code in a single file works for me.
First I create a table in the database from Python. I'm not sure why your Average column is an INT type, so I changed that. Also, my ID is an INT just for simplicity.
import mysql.connector
cnx = mysql.connector.connect(user='root', password='password',
host='localhost',
database='mydb')
cursor = cnx.cursor()
# cursor.execute("DROP TABLE IF EXISTS Class1")
cursor.execute('''
CREATE TABLE IF NOT EXISTS Class1
( ID INT PRIMARY KEY
, Name VARCHAR(10) NOT NULL
, Score1 INT
, Score2 INT
, Score3 INT
, Average DECIMAL(9, 5)
);
''')
cnx.commit()
Next, I create a User class in order to hold all the important information and contain the logic to go to & from the database. This way, you only require a single User object and a single method to go each way. This approach is preferred over your multiple INSERT queries.
class User:
def __init__(self, _id, name, score1=None, score2=None, score3=None):
self._id = _id
self.name = name
self.score1 = score1
self.score2 = score2
self.score3 = score3
''' set the score of the given or next available class '''
def add_score(self, score, Class=None):
if not Class or (Class < 0 or Class > 3):
if all((self.score1, self.score2, self.score3)):
return # can't update
elif all((self.score1, self.score2)):
Class = 3
elif self.score1:
Class = 2
else:
Class = 1
if Class and 0 < Class <= 3: # if a position is given and valid
setattr(self, 'score' + str(Class), score)
def to_tuple(self):
return (self._id, self.name, self.score1, self.score2, self.score3)
''' make it possible to see this object when printed '''
def __repr__(self):
return self.__class__.__name__+ str(self.to_tuple())
''' insert or update this user object in the database '''
def insert_to_db(self, db):
crsr = db.cursor()
data = list(self.to_tuple())
data.append(self.get_average_score())
if User.get_by_id(self._id):
data = data[1:]
data.append(self._id)
crsr.execute('''
UPDATE Class1 SET
Name = %s,
Score1 = %s,
Score2 = %s,
Score3 = %s,
Average = %s
WHERE ID = %s;
''', data)
else:
crsr.execute("INSERT INTO Class1 VALUES (%s,%s,%s,%s,%s,%s)", data)
db.commit()
crsr.close()
#staticmethod
def get_by_id(_id):
cursor.execute("SELECT * FROM Class1 WHERE ID = %s", [_id])
row = cursor.fetchone()
return User.from_tuple(row)
#staticmethod
def get_by_name(name):
cursor.execute("SELECT * FROM Class1 WHERE Name = %s", [name])
row = cursor.fetchone()
return User.from_tuple(row)
''' Get the average score from the object. No need to query the database '''
def get_average_score(self):
from statistics import mean
scores = list(self.to_tuple())[2:4]
scores = list(filter(None.__ne__, scores))
return mean(scores) if len(scores) > 0 else 0
#staticmethod
def from_tuple(tup, min_elems=2, max_elems=6):
user = None
if tup:
num_elems = len(tup)
if num_elems < min_elems or num_elems > max_elems:
raise Exception('invalid tuple given', tup)
# know there is at least 2 elements here
user = User(tup[0], tup[1])
if num_elems >= 3:
user.score1 = tup[2]
if num_elems >= 4:
user.score2 = tup[3]
if num_elems >= 5:
user.score3 = tup[4]
return user
#staticmethod
def from_cursor(cursor):
if cursor:
return (User.from_tuple(row) for row in cursor.fetchall())
return iter(()) # Return empty generator if cursor == None
Next, a quiz method is defined that returns the score and name of the person taking the quiz. The parameters are optional and have default values. It is a good habit to define many small methods in order to test your code and logic.
def quiz(num_questions=10, name=None):
if not name:
name = input("Enter your name: ").title()
print(name, ", Welcome to the Maths Test")
score = 0
for _ in range(num_questions):
num1 = random.randint(1,10)
num2 = random.randint(1,10)
op, symbol = random.choice(OPERATIONS)
print("What is", num1, symbol, num2)
if get_int_input() == op(num1, num2):
print("Correct")
score += 1
else:
print("Incorrect")
return name, score
And finally (along with your other methods), this is the main method that will run with the program. This prompts for an ID, tries to find it in the database, then quizzes either an existing user and updates their score, or creates a new user, then inserts the user into the database.
def main():
user_id = get_int_input("Enter your ID: ")
Class = get_int_input("Which class do you wish to input results for 1, 2, or 3? ")
user = User.get_by_id(user_id)
if not user:
print("User with id %d not found" % user_id)
print("Creating new user")
name, score = quiz(NB_QUESTIONS)
user = User(user_id, name)
else:
print("Found user %s" % user.name)
_, score = quiz(NB_QUESTIONS, user)
user.add_score(score, Class)
print("\nWell done", user.name, "you scored", score, "/", NB_QUESTIONS)
print("Thank you for doing this mathamatical quiz , goodbye ")
user.insert_to_db(cnx) # Remember to update the user in the database
cnx.close()
if __name__ == '__main__':
main()

Categories

Resources