-1

I am building a simple GUI using appJar for a library system I made for school. I'm trying to make a function that allows users to either upvote or downvote a book (books are stored in a sqlite3 database) given the book ID (one of the columns for each entry in the db). Here is my code:

def upvote(id):
    statement = f"SELECT review from books WHERE id={id}"
    cur.execute(statement)
    rating = cur.fetchall()

    newRating = rating[0][0] + 1
    statement2 = f"UPDATE books SET review={newRating} WHERE id={id}"
    cur.execute(statement2)
    con.commit()

def downvote(id):
    statement = f"SELECT review from books WHERE id={id}"
    cur.execute(statement)
    rating = cur.fetchall()

    newRating = rating[0][0] - 1
    statement2 = f"UPDATE books SET rating={newRating} WHERE id={id}"
    cur.execute(statement2)
    con.commit()

def giveRating():
    app.startSubWindow("Give Rating", modal=True)
    app.show()
    app.setFont(18)
    app.setBg("orange")
    app.setSize(400, 400)

    app.addLabelEntry("Book ID")
    bookId = app.getEntry("Book ID")
    app.addButton("Upvote", upvote(bookId))
    app.addButton("Downvote", downvote(bookId))
    app.stopSubWindow()

The reason for using subwindows is that the rating page is opened upon clicking a button from a dashboard page.

Anyone know why I am getting the following error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/tkinter/__init__.py", line 1948, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "/Users/guilhermelopes/Documents/terminal library sys/venv/lib/python3.11/site-packages/appJar/appjar.py", line 3781, in <lambda>
    return lambda *args: funcName()
                         ^^^^^^^^^^
  File "/Users/guilhermelopes/Documents/terminal library sys/app.py", line 83, in giveRating
    app.addButton("Upvote", upvote(bookId))
                            ^^^^^^^^^^^^^^
  File "/Users/guilhermelopes/Documents/terminal library sys/app.py", line 56, in upvote
    cur.execute(statement)
sqlite3.OperationalError: incomplete input

I feel like I am missing something obvious but I can't find anything relevant to my issue in the docs. Whilst the input label is created, I expected the two buttons to be created along with it, but instead I get the above error.

Thanks in advance.

x1n0_
  • 39
  • 5
  • `SELECT review from books WHERE id={id}` What is the type of the `id` column, and what type is the `id` python variable? – John Gordon Feb 11 '23 at 18:01
  • Both are integers. – x1n0_ Feb 11 '23 at 18:15
  • My only guess is that you're encountering a case where `id` actually is not an integer. As a debugging step, can you print `statement` before executing it, to see what it is actually trying to execute? – John Gordon Feb 11 '23 at 18:25

2 Answers2

0

Why are you selecting the column review? It is not the column that you want to update.
You should select the column rating.

But, why do you select anything?

You can do what you want with the UPDATE statement only:

def upvote(id):
    statement = "UPDATE books SET rating = COALESCE(rating, 0) + 1 WHERE id = ?"
    cur.execute(statement, (id,))
    con.commit()

def downvote(id):
    statement = "UPDATE books SET rating = COALESCE(rating, 0) - 1 WHERE id = ?"
    cur.execute(statement, (id,))
    con.commit()

I use COALESCE(rating, 0) just in case rating is nullable.
If it is not, change to just rating.

Also, from http://appjar.info/pythonWidgets/, you should pass only the function's name as a parameter to addButton:

app.addButton("Upvote", upvote)
app.addButton("Downvote", downvote)
forpas
  • 160,666
  • 10
  • 38
  • 76
  • Thank you for your response. This eliminates the error, but does not update the db. The statement generated is as follows: UPDATE books SET rating = COALESCE(rating, 0) - 1 WHERE id = ? The function runs before the button is clicked, and then once the button is actually clicked it doesn't run the function. Do you know what is going on? – x1n0_ Feb 11 '23 at 18:10
0

I think this may be the problem, or at least part of the problem:

app.addButton("Upvote", upvote(bookId))

The way you're referring to upvote (i.e. you put parentheses () after it), it is called right now, instead of later when the button is pressed.

In typical GUI apps like this, you would only pass the function name:

app.addButton("Upvote", upvote)

Which would give the button a reference to the upvote function that it can call later when the button is actually pressed. (Although in this specific case I don't know how you would do that and also be able to pass the bookId argument...)

And indeed, the appjar documentation for addButton() does it exactly that way. http://appjar.info/pythonWidgets/

You're using addButton() wrongly.

John Gordon
  • 29,573
  • 7
  • 33
  • 58
  • Yes, the problem was passing the argument. The way I fixed it was by assigning the bookId value inside the upvote and downvote functions independently. Thanks for the help! – x1n0_ Feb 11 '23 at 19:39