Why is Filename not a db that the shelve module can open? - python

import random
import sys
import shelve
def get_input_or_quit(prompt, quit="Q"):
prompt += " (Press '{}' to exit) : ".format(quit)
val = input(prompt).strip()
if val.upper() == quit:
sys.exit("Goodbye")
return val
def prompt_bool(prompt):
while True:
val = get_input_or_quit(prompt).lower()
if val == 'yes':
return True
elif val == 'no':
return False
else:
print ("Invalid input '{}', please try again".format(val))
def prompt_int_small(prompt='', choices=(1,2)):
while True:
val = get_input_or_quit(prompt)
try:
val = int(val)
if choices and val not in choices:
raise ValueError("{} is not in {}".format(val, choices))
return val
except (TypeError, ValueError) as e:
print(
"Not a valid number ({}), please try again".format(e)
)
def prompt_int_big(prompt='', choices=(1,2,3)):
while True:
val = get_input_or_quit(prompt)
try:
val = int(val)
if choices and val not in choices:
raise ValueError("{} is not in {}".format(val, choices))
return val
except (TypeError, ValueError) as e:
print(
"Not a valid number ({}), please try again".format(e)
)
role = prompt_int_small("Are you a teacher or student? Press 1 if you are a student or 2 if you are a teacher")
if role == 1:
score=0
name=input("What is your name?")
print ("Alright",name,"welcome to your maths quiz."
" Remember to round all answers to 5 decimal places.")
level_of_difficulty = prompt_int_big("What level of difficulty are you working at?\n"
"Press 1 for low, 2 for intermediate "
"or 3 for high\n")
if level_of_difficulty == 3:
ops = ['+', '-', '*', '/']
else:
ops = ['+', '-', '*']
for question_num in range(1, 11):
if level_of_difficulty == 1:
max_number = 10
else:
max_number = 20
number_1 = random.randrange(1, max_number)
number_2 = random.randrange(1, max_number)
operation = random.choice(ops)
maths = round(eval(str(number_1) + operation + str(number_2)),5)
print('\nQuestion number: {}'.format(question_num))
print ("The question is",number_1,operation,number_2)
answer = float(input("What is your answer: "))
if answer == maths:
print("Correct")
score = score + 1
else:
print ("Incorrect. The actual answer is",maths)
if score >5:
print("Well done you scored",score,"out of 10")
else:
print("Unfortunately you only scored",score,"out of 10. Better luck next time")
class_number = prompt_int_big("Before your score is saved ,are you in class 1, 2 or 3? Press the matching number")
filename = (str(class_number) + "txt")
with shelve.open(filename) as db: # This is the effected line
old_scores = db.get(name, [])
old_scores.extend(score)
db[name] =score[-3:]
if prompt_bool("Do you wish to view previous results for your class"):
for line in lines:
print (line)
else:
sys.exit("Thanks for taking part in the quiz, your teacher should discuss your score with you later")
if role == 2:
class_number = prompt_int_big("Which class' scores would you like to see? Press 1 for class 1, 2 for class 2 or 3 for class 3")
filename = (str(class_number) + "txt")
sort_or_not = int(input("Would youlike to sort these scores in any way? Press 1 if the answer is no or 2 if the answer is yes"))
if sort_or_not == 1:
f = open(filename, "r")
lines = [line for line in f if line.strip()]
lines.sort()
for line in lines:
print (line)
if sort_or_not == 2:
type_of_sort = int(input("How would you like to sort these scores? Press 1 for scores in alphabetical order with each student's highest score for the tests, 2 if you would like to see the students' highest scores sorted from highest to lowest and 3 if you like to see these student's average scores sorted from highest to lowest"))
if type_of_sort == 1:
with open(filename , 'r') as r:
for line in sorted(r):
print(line, end='')
if type_of_sort == 2:
file = open(filename, 'r')
lines = file.read().splitlines()
s = {lines[i]:[int(k) for k in lines[i+1:i+4]] for i in range(0,len(lines),4)}
for i in sorted(s.keys()):
print (i,max(s[i]))
avg_mark = lambda name:sum(s[name])/len(s[name])
for i in sorted(s.keys(),key=avg_mark,reverse=True):
print (i,avg_mark(i))
if type_of_sort == 3:
file = open(filename, 'r')
lines = file.read().splitlines()
s = {lines[i]:[int(k) for k in lines[i+1:i+4]] for i in range(0,len(lines),4)}
for i in sorted(s.keys()):
print (i,max(s[i]))
high_mark = lambda name:max(s[name])
for i in sorted(s.keys(),key=high_mark,reverse=True):
print (i,high_mark(i))
The shelve module part of my code is not working and I don't understand what the error is trying to say. I am trying to save the last three scores to a students name and then sort it in a variety of ways . The error comes up after the quiz is completed as a student.

Try deleting the file at filename and running the script again, so that the script is responsible for creating the database file in the correct format. If you ask it to open an empty file or a file that was created by some other means (e.g. CSV, JSON, etc.) then it doesn't know how to handle it.

Related

Write list to 2 different text files

Reading in the data text file,
Running loops to check criteria for valid and invalid student numbers.
Then trying to write 2 text files, one for the invalid student numbers and one for the valid student numbers
This far everything works accept no text files written and no data written to text files at the end
Here is the Script so far
inputList = []
outputList = []
def FileOpen(myList):
#try:
count=0
INFILE=open("data.txt", "r")
for line in INFILE:
myList.append(line.rstrip())
count+=1
INFILE.close()
return count
#except:
# print("File could not be found")
# exit()
FileOpen(inputList)
print(FileOpen(inputList),"number of lines read from file")
def AnalyseStudents(rawStudents,totalStudents):
for entry in rawStudents:
amountOfDigits = len(entry)
testOfDigits = entry.isdigit()
def inValid1(val1,val2):
answ = val1 != 8 and val2 != True
return answ
inValidResult1=(inValid1(amountOfDigits,testOfDigits))
def Valid1(val1,val2):
answ = val1 == 8 and val2 == True
return answ
validResult1=(Valid1(amountOfDigits,testOfDigits))
if inValidResult1:
print(entry, " is an INVALID student number")
elif validResult1:
a=entry[0]
b=entry[1]
c=entry[2]
d=entry[3]
e=entry[4]
f=entry[5]
g=entry[6]
h=entry[7]
sum = float((a*8)+(b*7)+(c*6)+(d*5)+(e*4)+(f*3)+(g*2)+(h*1))
result = sum%11
def inValid2 (val):
answ = val != 0
return answ
inValidResult2=(inValid2(result))
def Valid2 (val):
answ = val == 0
return answ
validResult2=(Valid2(result))
if validResult2:
print(entry, " is an VALID student number")
elif inValidResult2:
print(entry, " is an INVALID student number")
totalStudents
AnalyseStudents(inputList,outputList)
def Write(outList):
if outList == (" is an VALID student number"):
OUTFILE1=open("ValidNumbers.txt","w")
for validResult in outList:
OUTFILE1.write(validResult+"\n")
OUTFILE1.close()
elif outList == (" is an INVALID student number"):
OUTFILE2=open("InvalidNumbers.txt","w")
for inValidResults in outList:
OUTFILE2.write(inValidResults+"\n")
OUTFILE2.close()
Write(outputList)
print("See output files for more details")
Your equality statements are wrong
if outList == (" is an VALID student number"):
...
elif outList == (" is an INVALID student number"):
The outList is a list but you are comparing it to strings. Put a debug breakpoint at those if statements and decide what you want.
I'm still working my way through this, but for the first function, you can use list comprehension and a couple of other Python functions. Commentary is included.
inputList = []
outputList = []
# Change parameter to the name of the file you want to open.
# Here, we can just make the parameter optional and pass your
# text file name by default.
def FileOpen(filename="data.txt"):
# Using with keeps the open file in context until it's finished
# it will then close automatically.
with open(filename, "r") as INFILE:
# Reach all the data at once and then try to decode it.
data = INFILE.read()
try:
data = data.decode("utf-8")
# If data can't be decoded, then it doesn't have that attribute.
# Catch that exception
except AttributeError:
pass
# Python 3 has a string method called .splitlines() that makes
# splitting lines a little simpler.
myList = [line for line in data.splitlines()]
# We'll return two values: The length (or count) of the list
# and the actual list.
return len(myList), myList
# We set up two variables for intake of our count and list results.
count, myList = FileOpen()
print("{} number of lines read from file".format(count))
EDIT: I don't know what the last part of your code is aiming to do. Maybe compare student count to valid entries? Here's the middle part. I moved the function outside and made it evaluate either valid or invalid in one fell swoop.
def is_valid(entry_value):
"""Validate current entry."""
if len(entry_value) != 8 and entry_value.isnumeric():
return False
return True
def AnalyseStudents(rawStudents, totalStudents):
for entry in rawStudents:
# Our new function will return True if the entry is valid
# So we can rework our function to run through the True condition
# first.
if is_valid(entry):
entry_sum = entry[0] * 8
entry_sum += entry[1] * 7
entry_sum += entry[2] * 6
entry_sum += entry[3] * 5
entry_sum += entry[4] * 4
entry_sum += entry[5] * 3
entry_sum += entry[6] * 2
entry_sum += entry[7] * 1
if entry_sum % 11 == 0:
print(entry, " is an VALID student number")
else:
print(entry, " is an INVALID student number")
else:
print(entry, " is an INVALID student number")
AnalyseStudents(inputList, outputList)

How could I use user_int_count to add years to the print function. Like if input the year 2000 and int count 5 it would print years 2000-2005

def open_file():
data=open("data_full.txt")
return data
def process_file(data):
out_file = input("Enter a name for the output file: ")
output_file= open(out_file, "w")
user_year = int(input("Enter a year: "))
user_int_count= int(input("Enter a integer count: "))
cnt = 0
data.readline()
for line in data:
cnt+= 1
field = line.strip().split()
line_list = [int(n) for n in field]
total = sum(line_list[1:])
year = line_list[0]
avg = int(round((total/12),0))
if user_year == year:
output_file.write("{:<d} {:<d}".format(year, avg))
print()
print( "{:<d} {:<d}".format(year, avg))
output_file.close()
def main():
while True:
try:
f=open_file()
break
except FileNotFoundError:
print("Invalid file name. Try Again.")
process_file(f)
f.close()
main()
this should print a range from the year entered through the incremented value assuming that's what your asking. If not please rephrase your question as it is a bit hard to interpret.
for x in range(user_year, user_year + count):
print x
I'm basing this answer on a more literal interpretation of the question.
def year_range(year_date, number_of_years): #assuming these are of type int.
output = '{}-{}'.format(year_date, year_date+number_of_years)
print(output)
so year_range(2000, 5)
gives you "2000-2005"

Division by zero error when adding rogue value without any data

Hi having trouble trying to fix an error that occurs when I put just a '#' or rogue value in case someone doesn't want to add any data. I don't know how to fix it and I'm hoping to just end the code just like I would with data.
#Gets Data Input
def getData():
fullList = []
inputText = checkInput("Enter the students first name, last name, first mark, and second mark (# to exit): ")
while inputText != "#":
nameList = []
nameList2 = []
nameList = inputText.split()
nameList2.extend((nameList[0],nameList[1]))
nameList2.append((float(nameList[2]) + float(nameList [3]))/2)
fullList.append(nameList2)
inputText = checkInput("Enter the students first name, last name, first mark, and second mark (# to exit): ")
print("\n")
return fullList
#Calculates Group Average
def calc1(fullList):
total = 0
for x in fullList:
total = total + x[2]
groupAverage = total/(len(fullList))
return(groupAverage)
#Finds Highest Average
def calc2(fullList):
HighestAverage = 0
nameHighAverage = ""
for x in fullList:
if x[2] > HighestAverage:
HighestAverage = x[2]
nameHighAverage = x[0] + " " + x[1]
return (HighestAverage, nameHighAverage)
#Returns Marks above average
def results1(groupAverage,r1FullList):
r1FullList.sort()
print("List of students with their final mark above the group average")
print("--------------------------------------------------------------")
print("{:<20} {:<12}".format("Name","Mark"))
for x in r1FullList:
if x[2] > groupAverage:
name = x[0] + " " + x[1]
print("{:<20} {:<12.2f}".format(name,x[2]))
def calc3(x):
if x[2] >= 80:
return 'A'
elif x[2] >= 65:
return 'B'
elif x[2] >= 50:
return 'C'
elif x[2] < 50:
return 'D'
else:
return 'ERROR'
def results2(fullList):
print("List of Studens with their Final Marks and Grades")
print("-------------------------------------------------")
print("{:<20} {:<12} {:<12}".format("Name","Mark","Grade"))
for x in fullList:
grade = calc3(x)
name = x[0] + " " + x[1]
print("{:<20} {:<12.2f} {:<12}".format(name,x[2],grade))
#Checks for boundary and invalid data
def checkInput(question):
while True:
textInput = input(question)
if textInput == "#":
return textInput
splitList = textInput.split()
if len(splitList) !=4:
print("Invalid Format, Please Try Again")
continue
try:
a = float(splitList[2])
a = float(splitList[3])
if float(splitList[2]) < 0 or float(splitList[2]) > 100:
print("Invalid Format, Please Try Again")
continue
if float(splitList[3]) < 0 or float(splitList[3]) > 100:
print("Invalid Format, Please Try Again")
continue
return(textInput)
except ValueError:
print("Invalid Input, Please Try Again")
continue
#Main Program
#Input Data
fullList = getData()
#Process Data
groupAverage = calc1(fullList)
HighestAverage, nameHighAverage = calc2(fullList)
#Display Results
print("The group average was %.2f" % groupAverage)
print("The student with the highest mark was: %s %0.2f" %(nameHighAverage,HighestAverage))
results1(groupAverage,fullList)
print("\n")
results2(fullList)
Your program works OK for me, unless you enter a # as the first entry, in which case fullList is [] and has length 0. Hence, DivisionByZero at this line: groupAverage = total/(len(fullList)).
You could modify your code to check for this and exit:
import sys
fullList = getData()
if not fullList:
print('No Data!')
sys.exit()

Error when sorting scores of quiz numerically

For my coursework, I have been trying to sort the score file numerically and print it to python
I have tried to implement other answers from stackoverflow in to my code, but to no success.
This is the code I have:
if sortmethod == ("n"):
with open(filename) as f:
f = {}
scores=[]
for name, v in f.items():
scores.append((name, score))
for name, score in sorted(scores, key=lambda a: a[1], reverse=True):
print(name, scores)
Thanks for the help.
Full code if needed:
import random
import operator
OPERATIONS = [
(operator.add, "+"),
(operator.mul, "*"),
(operator.sub, "-")
]
NB_QUESTIONS = 10
def get_int_input(prompt=''):
while True:
try:
return int(input(prompt))
except ValueError:
print("Not a valid input (integer is expected)")
def get_bool_input(prompt=''):
while True:
val = input(prompt).lower()
if val == 'yes':
return True
elif val == 'no':
return False
else:
print("Not a valid input (yes/no is expected)")
if __name__ == '__main__':
name = input("What is your name?").title()
class_name = input("Which class do you wish to input results for? ")
print(name, ", Welcome to the OCR Controlled Assessment Maths Test")
score = 0
for _ in range(NB_QUESTIONS):
num1 = random.randint(1,25)
num2 = random.randint(1,25)
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)
filename = class_name + ".txt"
with open(filename, 'a') as f:
f.write(str(name) + str(score) + '\n')
sortmethod = input("How do you wish to sort the scores. A = Alphabetically N = Numerically").lower()
if sortmethod == ("a"):
with open(filename, 'a') as f:
f = open(filename, "r")
lines = [line for line in f if line.strip()]
f.close()
lines.sort()
if sortmethod == ("n"):
with open(filename) as f:
f = {}
scores=[]
for name, v in f.items():
scores.append((name, score))
for name, score in sorted(scores, key=lambda a: a[1], reverse=True):
print(name, scores)
You are openning the file and putting the reference to it in f and then you are changing f to point to an empty dictionary. (In the following lines )-
with open(filename) as f:
f = {}
So scores would always be empty and you would never get anything printed.
I believe you should change the name of the diciotnary variable to something else, and then after openning the file, you should read it and put the values in the dictionary. We do not know how your file is structured so we cannot help you in that.
Also, you are doing similar things in other places , you should avoid doing such kinds of things -
with open(filename, 'a') as f:
f = open(filename, "r")
Also, another suggestion , when you are writing to your file in the following line -
f.write(str(name) + str(score) + '\n')
You are not putting any delimiter between your name and score, you should put some kind of delimiter there so that when reading back you can easily make out the name and scores. A Standard delimiters to use would be comma - ',' .

How can i speed up this python code?

So I recently coded this as a little challenge to see how quick I could do it. Now since its working an such I want to speed it up. It finds all the proper devisors of a number, the highest proper devisor and times how long it all takes. The problem is with number like 5000 it takes 0.05 secs but with numbers like 99999999999 it takes 1567.98 secs.
this the old code I have made a new and improved version below
import time
def clearfile(name):
file = open(name + ".txt", "r")
filedata = file.read()
file.close()
text_file = open(name + ".txt", "w")
text_file.write("")
text_file.close()
def start():
num = input("Enter your Number: ")
check(num)
def check(num):
try:
intnum = int(num)
except ValueError:
error(error = "NON VALID NUMBER")
if(intnum < 0):
error(error = "POSITIVE NUMBERS ONLY")
else:
finddivisor(intnum)
def finddivisor(intnum):
starttimer = time.time()
i = 1
print("\nThe divisors of your number are:"),
while i <= intnum:
if (intnum % i) == 0:
print(i)
file = open("numbers.txt", "r")
filedata = file.read()
file.close()
text_file = open("numbers.txt", "w")
text_file.write(str(i) +"\n"+ filedata)
text_file.close()
i += 1
properdivisor(starttimer)
def properdivisor(starttimer):
file = open("numbers.txt", "r")
highest = file.readlines()
print("\nThe Highest Proper Divisor Is\n--------\n" + highest[1] + "--------" + "\nIt took" ,round(time.time() - starttimer, 2) ,"seconds to finish finding the divisors.\n")
restart(errorrestart = "false")
def restart(errorrestart):
if errorrestart == "false":
input("Do You Want Restart?\nPress Enter To Restart Or Close The Programe To Leave")
start()
elif errorrestart == "true":
input("\nThere Was An Error Detected.\nPress Enter To Restart Or Close The Programe To Leave")
start()
def error(error):
print("\n----------------------------------\nERROR - " + error + "\n----------------------------------")
restart(errorrestart = "true")
clearfile(name = "numbers")
start()
Can someone speed it up for me
EDIT 1
so after looking over it I have now edited it to be moving it away from a file to an array
import time
from array import *
def programme():
num = input("Enter your Number: ")
try:
intnum = int(num)
except ValueError:
error("NOT VALID NUMBER")
if(intnum < 0):
error("POSITIVE NUMBERS ONLY")
else:
numbers = array("i",[])
starttimer = time.time()
i = 1
print("\nThe divisors of your number are:"),
while i <= intnum:
if (intnum % i) == 0:
numbers.insert(0,i)
print(i)
i += 1
print("\nThe Highest Proper Divisor Is\n--------\n" + str(numbers[1]) + "\n--------" + "\n\nIt took" ,round(time.time() - starttimer, 2) ,"seconds to finish finding the divisors.\n")
def error(error):
print("\n----------------------------------\nERROR - " + error + "\n----------------------------------\n")
running = True
while(running == True):
programme()
print("----------------------------------")
restart = input("Do You Want Restart?")
restart = restart.lower()
if restart in ("yes", "y", "ok", "sure", ""):
print("Restarting\n----------------------------------")
else:
print("closing Down")
running = False
New Edit
import time, math
from array import *
def programme():
num = input("Enter your Number: ")
try:
intnum = int(num)
if(intnum < 0):
error("POSITIVE NUMBERS ONLY")
else:
numbers = array("i",[])
starttimer = time.time()
i = 1
print("\nThe divisors of your number are:"),
while i <= math.sqrt(intnum):
if (intnum % i) == 0:
numbers.insert(0,i)
numbers.insert(0,int(intnum/i))
print(i,":", int(intnum/i))
i += 1
numbers = sorted(numbers, reverse = True)
print("The Highest Proper Divisor Is\n--------\n",str(numbers[1]) , "\n--------\nIt took" ,round(time.time() - starttimer, 2) ,"seconds to finish finding the divisors." )
except ValueError:
error("NOT VALID NUMBER")
except OverflowError:
error("NUMBER IS TO LARGE")
except:
error("UNKNOWN ERROR")
def error(error):
print("\n----------------------------------\nERROR - " + error + "\n----------------------------------\n")
running = True
while(running):
programme()
print("----------------------------------")
restart = input("Do You Want Restart?")
restart = restart.lower()
if restart in ("yes", "y", "ok", "sure", ""):
print("Restarting\n----------------------------------")
else:
print("closing Down")
running = False
If you have divisor a of number n then you can tell one more divisor of n b = n / a. Moreover if a <= sqrt(n) then b >= sqrt(n) and vice versa. It means in your finddivisor function you can iterate while i * i <= n and print both divisors i and n / i.
By the way, you shouldn't open, read and close files in cycle. Open it once before cycle and close after if you need to read/write several times.
You don't need to read and rewrite the entire file every time you want to put a single entry into it. You can just do it once when you know what change you want. Also you could just append to it. Maybe something like this:
def finddivisor(intnum):
starttimer = time.time()
print("\nThe divisors of your number are:")
divs = set()
for i in range(1, int(math.sqrt(intnum)) +1):
if intnum % i == 0:
print(i)
divs.add(i)
divs.add(intnum // i)
with open("numbers.txt", "a") as file:
file.writelines("{}\n".format(ln) for ln in sorted(divs, reverse=True))
also your program flow is building a very deep stack. Try and flatten it with something like
def start():
clearfile()
while True:
n = get_number()
starttimer = time.time()
finddivisor(n)
properdivisor(starttimer)
input("Press Enter To Restart Or Close The Programe To Leave")
in properdivisor you dont need to read the whole file either, you just need the first line. so maybe something like:
def properdivisor(starttimer):
with open(FILENAME, "r") as file:
highest = file.readline().strip()
print "\nThe Highest Proper Divisor Is"
print "--------%s--------" % highest
print "It took %0.2f seconds to finish finding the divisors.\n" % (time.time() - starttimer)
AFTER EDIT
Something like this is how I would do it, and it runs less then a second on my box:
import math
def get_divisors(n):
yield 1
sqroot = int(math.sqrt(n))
for x in xrange(2, sqroot):
if n % x == 0:
yield x
yield n / x
if sqroot**2 == n:
yield sqroot
divisors = sorted(get_divisors(999999999999))
print divisors

Categories