0

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.

  • 1
    And tag `php` here does what? – u_mulder Dec 12 '15 at 16:05
  • sorry id didnt mean to add that –  Dec 16 '15 at 19:04
  • I deleted my post because it wasn't correct. As I was saying in the comments, though. Just run this in a Python environment `"ID" in "Class1"`... It will return `False`. And so does `"score" in "Score1"` and `"score" in "Score2"`... Your problem is that **none** of your if statements are being entered. Unless you mention what you expect those lines to do, no one can help. – OneCricketeer Dec 16 '15 at 19:07
  • what i want this to do is to add a score to my database .for example i have created this peice of code that will input the user id ,name and first score to the database.But i need a peice of code that will add a second score to the user .so it will show their first score and their second score and third.I do not know how to code this.I put the if statements there because i thought that "if score1 was filled then input sccore into score2" but this did not work. –  Dec 16 '15 at 20:08
  • Alright. Now that I understand, forget the database for a second. Are you able to do what you just said without it? – OneCricketeer Dec 16 '15 at 20:11
  • yes i can add the user name,id and first score to the database –  Dec 16 '15 at 20:13
  • iv made a pice of code that willl ask the user whitch attempt it is at the quiz but i dont know how to tell the code to search for a paticular name or id to input the score into. –  Dec 16 '15 at 20:18
  • how can i tell mysql to search for a paticular row called the "name of user" using python code? –  Dec 16 '15 at 20:32
  • http://dev.mysql.com/doc/connector-python/en/connector-python-example-cursor-select.html – OneCricketeer Dec 17 '15 at 02:39

2 Answers2

0

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()
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • This did not work.I recieve no error but my database did not get the second input of the score –  Dec 12 '15 at 22:42
  • When you do if "score" in "score1", you are testing if a string is part of another string. You could just post your full code. – OneCricketeer Dec 12 '15 at 23:20
  • what do you mean and i am realy stuck on a solution for this. –  Dec 12 '15 at 23:38
  • For example, "score" is **not in** "Score1" because "score" is not a substring of "Score1". Similarly "ID" is not a substring of "Class1". Those if statements shouldn't be entered at all. Is that really what you have in written in your code? – OneCricketeer Dec 13 '15 at 02:23
  • yes because i am quiet new to all of this and i am still learning .sorry.Do you know a solution? –  Dec 13 '15 at 11:06
  • please? is there a way i can fix this –  Dec 16 '15 at 18:54
  • @HarshPatel - I have written a complete answer to walk you through the solution. Let me know if you don't understand anything. Also note, that I left out `get_int_input`, `OPERATIONS`, and `NB_QUESTIONS`. You can define those above `def quiz()` if you wish. – OneCricketeer Dec 17 '15 at 04:19
  • i use mysql so do u know how to make this compatable to that? –  Dec 17 '15 at 16:20
  • i find sqlite very complex compared to mysql so i would realy appreciate it if u could adapt this code to work in that. –  Dec 17 '15 at 16:39
  • my teacher said i cant use your code for the project.I think i have a solution i have created a if statement that will ask what attempt at the code thay are doing and accourding to that it will input their score into the diffrent colums.Do you know how to look for a paticular row in mysql.For example if bob is having a second attempt then his score will be input into the row with the name bob and in the column called score2. –  Dec 17 '15 at 16:47
  • @HarshPatel - Do you know SQL? `select name, score1, score2, score3 from Class1 where name='bob'` will return all rows where the name is bob. And far as I can tell, sqlite is not anymore confusing/complicated that using the [mysql connector](https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html) – OneCricketeer Dec 17 '15 at 17:03
  • i do not know SQL am just a kid doing a project and will my solution not work? –  Dec 17 '15 at 17:07
  • is it not possible for my to tell sql to look for a name and input the score in a paticulat column? –  Dec 17 '15 at 17:09
  • @HarshPatel - Use an [UPDATE statement](http://www.tutorialspoint.com/mysql/mysql-update-query.htm). If you do not understand transactional database operations or basic Python syntax (completely unrelated to MySQL), then I'm afraid your teacher is asking too much of you... – OneCricketeer Dec 17 '15 at 17:11
  • do you know what code i can add to pyhton to make the database look for a name? so the score2 and 3 can be added.if i can do this then the project is complete. –  Dec 17 '15 at 17:15
  • It is a multiple step process... It is not just one line. If you look at my code, I search for a user by using an id, that could easily be changed to use a name. You then need to know which score to add. You could ask the user to input a number called `course`, then do `if course == 1: ([SQL] UPDATE Class1 SET score1=score WHERE name='bob') ... elif course == 2: (UPDATE...SET score2=score...)`. Make sense? – OneCricketeer Dec 17 '15 at 17:27
  • could you adapt your code to suit mysql and make it work with my databse i have created please.id is pk in my database . –  Dec 17 '15 at 17:29
  • ID varchar(5) PK NN,Name varchar(10),Score1 int,Score2 int, Score3 int ,Average int –  Dec 17 '15 at 18:24
  • @HarshPatel - changed. – OneCricketeer Dec 18 '15 at 18:09
  • i got this error Traceback (most recent call last): File "C:\Users\HarshPatel\Desktop\Work\Computer Science\A453 Programming\test enviro\pro code.py", line 149, in main() File "C:\Users\HarshPatel\Desktop\Work\Computer Science\A453 Programming\test enviro\pro code.py", line 129, in main user_id = get_int_input("Enter your ID: ") NameError: name 'get_int_input' is not defined –  Dec 18 '15 at 22:03
  • do u know if i have to keep that and if not how do i overcome this error? –  Dec 19 '15 at 17:06
  • I said in my answer **along with your other methods**. You have to have that method defined (along with `OPERATIONS`, and `NB_QUESTIONS`) in order for the code to work. – OneCricketeer Dec 19 '15 at 17:17
  • is it possible for me to adapt ur code so it works with mine? i removes the if statements that u said dont work. –  Dec 19 '15 at 17:32
  • if it not possible to make my code work or do i need to vastly change it to make it do what i want it to do? –  Dec 19 '15 at 17:38
  • I really can't help you if you don't understand each piece of the solution I shown. Here is the full code - http://pastebin.com/V9NfNfyG – OneCricketeer Dec 19 '15 at 17:45
  • This code will update the score in score 1 it does not store any values in score 2 and 3 –  Dec 19 '15 at 17:53
  • Works for me just fine when I enter 2 for which class I want to update. I've already done your work. If something doesn't work, debug it yourself. – OneCricketeer Dec 20 '15 at 06:56
  • thanks im sorry for constant questions an still quiet new to this.I wont ask any more now :) –  Dec 20 '15 at 17:44
  • 1 little question.what does def from_tuple(tup, min_elems=2, max_elems=6): section of the code do.What does that whole part of the code do? –  Jan 02 '16 at 12:34
  • A tuple is a datatype in python. A user object requires a minimum of 2 arguments for the id and name. An additional 3 arguments are for the scores. The sixth one is the average, but the User object doesn't hold that information. Basically, it converts a row from MySQL to a python User object. – OneCricketeer Jan 02 '16 at 13:05
  • so all it does is that it allows python to understand the sql table and all of the columns ? is that what that whole section of the code do that whole part until "return user" –  Jan 02 '16 at 18:19
  • I wouldn't say *understand*. But, in a way, yes, it *converts* a single row from sql to a User. I think tuples are immutable (you can't change their values), so by converting it to a User object, you can freely update the fields and call additional methods like `add_score` – OneCricketeer Jan 02 '16 at 18:28
  • so that WHOLE section of the code updates and lets python understand the table?sorry could be explain to me like am an alian what tht section does –  Jan 02 '16 at 18:30
  • Python knows nothing about the table. *You* know the table and *you* tell python how to search for stuff from the table. That "section" (otherwise called a method) that you are referring to does no updating, it simply makes a User object from a single row in the table as I explained. – OneCricketeer Jan 02 '16 at 18:36
  • For example, these are valid tuples. `(1,'test')`, `(1,'test', 4)`, `(1,'test', 5,6,6)`. No values are looked at past 5 elements since there are only 5 fields in the User class. Any less than 2 elements are not allowed since a User **requires** both an id and a name. – OneCricketeer Jan 02 '16 at 18:40
  • i am starting to understand now –  Jan 02 '16 at 18:50
  • what does the _main_: do at the end? –  Jan 05 '16 at 19:39
  • It calls the defined main method. Read this: http://stackoverflow.com/questions/419163/what-does-if-name-main-do – OneCricketeer Jan 05 '16 at 21:00
0

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()

  • This did not work.I receive no error but my database did not get the second input in the column. –  Dec 12 '15 at 22:48