GUI-displayed timestamps in python program not refreshing? - python

Ok so I've recently written a python-based punchclock program for my company to replace their old paper-and-stamp punchcard system.
The idea is that everyone has access key cards to get in the doors in the morning, so we're using an RFID reader connected to a Surface Pro just inside the door to clock in with. The reader is programmed so it'll read the card and issue a 'RETURN' event. When it hits, it'll trigger the numbers it just input(EmpID) to be run against a SQLite table(Emps) that returns the corresponding name(EmpName). Then, EmpName is used as input to a different table(Swipes), along with the theDate and theTime. The EmpName and theTime values are also displayed in the text beneath the Input box with an .Update() command.
It.. mostly works. It'll switch names when different cards are run over the reader, but it won't change time. When I check the DB with SQLiteStudio, my entrys have been made, but they all share the same timestamp.
Here's a snip of the frontend code:
import pysimplegui as sg
import datetime
import TimeclockBackEnd as be
layout = [[sg.Text('Swipe Your Card')],
[sg.InputText(size=(6,1), key='IN', focus=True)],
[sg.Text('', key='theName', size=(45,1))],
[sg.Text('', key='theTime', size=(45,1))],
[sg.Button('', key='RETURN', visible=False, bind_return_key=True)],
window = sg.Window('Clock!', layout)
rightNow = datetime.datetime.now()
theTime = rightNow.strftime("%H:%M:%S")
theDate = rightNow.strftime("%Y-%m-%d")
while True :
event, value = window.Read()
cardNumber = value['IN']
if event is None:
break
elif event is 'RETURN':
nameOfSwiper = be.Swipe(cardNumber)
be.submitToDB(nameOfSwiper, theDate, theTime)
window['theName'].Update(nameOfSwiper)
window['theTime'].Update(theTime)
window['IN'].Update('')
and here's the code it calls on in the backend(be):
def Swipe(EmpID):
con = sqlite3.connect('Timeclock.db')
cur = con.cursor()
cur.execute("SELECT EmpName FROM Emps WHERE EmpID=?", (EmpID,))
returnedValue = cur.fetchall()
con.commit()
con.close()
delisted = ''.join(map(str, returnedValue))
stripped = str(delisted).strip('()')
strippedAgain = str(stripped).strip(',')
swiperName = str(strippedAgain).strip("''")
return swiperName
def submitToDB(EmpName, theDate, theTime):
con = sqlite3.connect('Timeclock.db')
cur = con.cursor()
cur.execute("INSERT INTO Swipes VALUES (?, ?, ?)", (EmpName, theDate, theTime))
con.commit()
con.close()
Again, it writes to the DB just fine, the only thing I'm having an issue with is how theTime doesn't change from the value that was set on its initial swipe. This is the main working parts of the code but if you don't see anything wrong here, feel free to check my github, I've got the full thing there.
Thanks for helping out!

Related

Simple system to track hours worked

I am a total beginner and I think I underestimated my little project.
I am trying to implement a simple attendance system that will give me the hours worked per day and later per month.
I am using a Raspberry pi 3 b+ and RC522 Rfid reader plus 16x2 lcd display.
The data ist stored in a database using MariaDB.
The idea is to use it for student workers in a restaurant to clock in their hours.
The employees still write down their hours, but if it works it could replace the paperwork, we will see.
I know that there will be some concerns about legality, but thats up for the lawyers once I am done.
However my issue is, that right now I am unable to clock in and clock out multiple users.
It does work for one user.
If I clock in User 1 it waits until there is a new clock out info, doesn't matter which user it is.
So user 1 clock in, user 2 wants to clock in but he is registered to clock out and only then the database entry is transferred. I think it somehow would need to update the entries instantly.
I think you can get the idea from the picture
phpMyAdminScreenshot
I think I need to get more info from the database and compare it to what I have.
But I hit a wall now and I am unable to find a solution for my problem.
The code I have right now:
'#!/usr/bin/env python
import time
import datetime
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
import mysql.connector
import drivers
db = mysql.connector.connect(
host="localhost",
user="xxx",
passwd="xxx",
database="attendancesystem"
)
cursor = db.cursor()
reader = SimpleMFRC522()
display = drivers.Lcd()
sign_in = 0
sign_out= 1
try:
while True:
display.lcd_clear()
display.lcd_display_string("Transponder", 1)
display.lcd_display_string("platzieren", 2)
id, text = reader.read()
ts = time.time()
timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
cursor.execute("Select id, name FROM users WHERE rfid_uid="+str(id))
result = cursor.fetchone()
display.lcd_clear()
#if cursor.rowcount >= 1:
#display.lcd_display_string("Willkommen " + result[1], 2)
#cursor.execute("INSERT INTO attendance (user_id) VALUES (%s)", (result[0],) )
if sign_in == 0:
sign_in = (sign_in +1) % 2
sign_out = (sign_out +1) % 2
cursor.execute(f"INSERT INTO attendance (user_id, clock_in, signed_in) VALUES (%s, %s, %s)", (result[0], timestamp, sign_in) )
display.lcd_display_string(f"Angemeldet " + result[1], 1)
elif sign_in == 1:
sign_out = (sign_out +1) % 2
sign_in = (sign_in +1) % 2
cursor.execute(f"INSERT INTO attendance (user_id, clock_out, signed_in) VALUES (%s, %s, %s)", (result[0], timestamp, sign_in) )
display.lcd_display_string (f"Abgemeldet " + result[1], 1)
db.commit()
else:
display.lcd_display_string("Existiert nicht.", 1)
time.sleep(2)
finally:
GPIO.cleanup()'
My idea was to get one more database entry called signed_in and either have it as 0 or as 1.
The signed_in status does update how I want it to, but I don't know how to continue from here.
And I fail to be able to update the table I want to.
My idea was to get user_id and check for this id the last status of the signed_in row, if it is 1 the timestamp will update the clock_out row and signed_in to 0.
If it is 0 and clock_out is not NULL it will start a new row with the clock_in timestamp and switch signed_in to 1.
I didn't have any luck with updating any database values, so I reverted back to INSERT.
I would expect the database would only store events. These events would be from when they touched the RFID. The card would have an ID/employee number.
Processing the database information would allow the calculation of who is on shift and the length of shifts worked etc.
If there was an odd number of entries for an employee then you could assume they are on shift. Even number of entries would mean they have completed the shift.
I don't have your hardware or database so I've created random RFID read events and used sqlite3.
import datetime
import random
import sqlite3
import time
class SimpleMFRC522:
#staticmethod
def read():
while random.randint(0, 30) != 10: # Create some delay/blocking
time.sleep(1)
employee_id = random.randint(0, 5)
return employee_id, f'employee_{employee_id}'
class MyDatabase:
def __init__(self):
self.db = sqlite3.connect('/tmp/my-test.db')
with self.db:
self.db.execute("""
CREATE TABLE IF NOT EXISTS workers (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
empl_no INTEGER,
name TEXT,
tap_event timestamp);
""")
self.db.commit()
def insert_event(self, empl_no, name, timestamp):
sqlite_insert_with_param = """INSERT INTO 'workers'
('empl_no', 'name', 'tap_event')
VALUES (?, ?, ?);"""
self.db.cursor()
self.db.execute(sqlite_insert_with_param, (empl_no, name, timestamp))
self.db.commit()
def worker_in(self, empl_no):
sql_cmd = f"SELECT COUNT(*) FROM workers WHERE empl_no = {empl_no};"
cursor = self.db.execute(sql_cmd)
event_count, = cursor.fetchone()
return event_count % 2 == 1 and event_count > 0
def last_shift(self, empl_no):
sql_cmd = f"""SELECT tap_event FROM
(SELECT * FROM workers WHERE empl_no = {empl_no} ORDER BY tap_event DESC LIMiT 2)
ORDER BY tap_event ASC ;"""
cursor = self.db.execute(sql_cmd)
tap_in, tap_out = cursor.fetchall()
start = datetime.datetime.fromtimestamp(tap_in[0])
end = datetime.datetime.fromtimestamp(tap_out[0])
return end - start
def main():
db = MyDatabase()
reader = SimpleMFRC522()
while True:
empl_no, name = reader.read()
print(f"adding tap event for {name}")
db.insert_event(empl_no, name, datetime.datetime.timestamp(datetime.datetime.now()))
if db.worker_in(empl_no):
print(f"\t{name} has started their shift")
else:
time_worked = db.last_shift(empl_no)
print(f"\t{name} has left the building after {time_worked}")
if __name__ == '__main__':
main()
This gave me a transcript as follows
adding tap event for employee_5
employee_5 has started their shift
adding tap event for employee_0
employee_0 has left the building after 0:02:10.154697
adding tap event for employee_3
employee_3 has left the building after 0:07:02.465903
adding tap event for employee_3
employee_3 has started their shift
adding tap event for employee_2
employee_2 has left the building after 0:06:27.403874
adding tap event for employee_2
employee_2 has started their shift

How to delete from Mysql Database an item selected in a Listbox Python Tkinter

i have made a database with 3 columns(id, name, price) and since i can't display all the columns in the Listbox i am displaying only the column "name", so i want to be able to delete an item in the database based on the name of the product i am clicking on the Listbox.
here is the code, i hope it helps:
import tkinter
from tkinter import *
import mysql.connector
root = Tk()
root.geometry('700x500')
con = mysql.connector.connect(user = '', password = '', host = '127.0.0.1', database = 'anime')
concursor = con.cursor()
concursor.execute('select * from prod')
query = concursor.fetchall()
l = Listbox(root)
for x,y,z in query:
l.insert(x+1, y)
l.pack(anchor='w')
#function to delete prod
def delp():
sel_items = l.curselection()
for item in sel_items:
concursor.execute("delete from prod where name = ('{}')".format(item))
con.commit()
button1 = Button(root, text = 'prod del', command = delp)
button1.pack(anchor='w')
root.mainloop()
and this is the database picture:
the python code works only if i replace "name" with "id" in the query in the "delp" function, however when i select the 1st item in the listbox, it deletes the 2nd item int the database, and i want to be able to delete based on the name.
so i figured it out, so heres the answer for ones whom are/will be looking for this.
first we wont need the curselection()
we need to get the active item or Anchor and "string it" and then use it in the query with format.
heres the code to make sense
def delp():
sel_items = l.get(ACTIVE)
string_item = str(sel_items)
for item in sel_items:
concursor.execute("delete from prod where name = ('{}')".format(string_item))
con.commit()
this code will delete the items in the database based on the name select in the listbox.

simple CLI todo manager with sqlite3 database storing problem

I am currently trying to make a command line todo manager that will allow the user to input a task(s), remove it and list the task(s) out. From what I tried visualizing it didn't do as I thought it would, it's my first time using sqlite3.
What I am trying to achieve:
Storing the task(s) in the database which will automatically add an incrementing ID to it.
Example:
python todo.py -add do the laundry on Sunday
[in the database]
Id Task
1 do the laundry on Sunday
My code.
import sqlite3
import argparse
def parse_args():
desc = 'Todo manager for storing and removing tasks'
parser = argparse.ArgumentParser(description=desc)
parser.add_argument("-a", "--add", "-add", help="To add a new item to the list",
type=str, nargs="+")
parser.add_argument("-r", "-remove", "--remove", help="To remove an item from the list",
type=int)
parser.add_argument("-l", "-list", "--list", help="displays the tasks or task in the list",
nargs="*")
args = parser.parse_args()
return args
#staticmethod
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def get_todo_list():
database_connection.row_factory = dict_factory
cursor = database_connection.cursor()
cursor.execute("select rowid, * FROM todo_list")
return cursor.fetchall()
def add_to_todo_list(num,task):
cursor = database_connection.cursor()
cursor.execute("INSERT INTO todo_list VALUES (?)", (str(task),))
database_connection.commit()
def remove_from_todo_list(rowid):
cursor = database_connection.cursor()
cursor.execute("DELETE FROM todo_list WHERE rowid = ?", (rowid,))
database_connection.commit()
if __name__ == '__main__':
commands = parse_args()
# Creating table for database using sqlite
database_connection = sqlite3.connect('todo_list.db')
cursor = database_connection.cursor()
cursor.execute('''CREATE TABLE if not exists todo_list(
description TEXT);''')
database_connection.commit()
if commands.add:
# Stops accepting tasks when there is a blank task as input.
if not commands.add == ' ':
add_to_todo_list(commands.add)
elif commands.remove:
remove_from_todo_list(commands.remove)
elif commands.list:
get_todo_list()
However, my database is not accepting any values when I am trying to store data. By putting Id as Id INTEGER PRIMARY KEY when creating the table i.e.
cursor.execute('''CREATE TABLE if not exists todo_list(
Id INTEGER PRIMARY KEY
description TEXT);''')
Will the Id increment as I add data to the database?
sqlite3.InterfaceError: Error binding parameter 1 - probably unsupported type.
Your inputs from argparse are coming in as str, yet you defined your column ID as an INTEGER in your db. Fix is:
cursor.execute("INSERT INTO todo_list VALUES (?,?)", (int(num), task,))
Storing the task(s) in the database which will automatically add an incrementing ID to it.
According to the sqlite docs here, defining a INTEGER PRIMARYKEY will auto-increment. Simply pass a null value to it, and sqlite takes care of the rest for you.
You have a few issues on your code when it comes to displaying and adding the tasks. First, initializing the DB:
cursor.execute(
"""CREATE TABLE if not exists todo_list(
id INTEGER PRIMARY KEY,
description TEXT);"""
)
How you had it before your post edit was fine. Then, the add_to_todo_list:
def add_to_todo_list(task):
cursor = database_connection.cursor()
cursor.execute("INSERT INTO todo_list VALUES (?,?)", (None, str(task)))
database_connection.commit()
Notice the removal of num from the functions input, and the passing of None for the column ID. Within the get_todo_list() you can fetch it more easily as so:
def get_todo_list():
cursor = database_connection.cursor()
cursor.execute("select * FROM todo_list")
return cursor.fetchall()
A fix is also needed in the way you parse your args; for commands.list you need to do the following:
elif commands.list is not None:
print(get_todo_list())
This is since commands.list will be a [] when you do app.py -list, which Python evaluates to False (empty lists are falsey). You also ought to print the contents of the function to terminal -- so don't forget that. With the edits above I can do on my terminal:
python test.py -add Random Task!
python test.py -add Hello World!
python test.py -list
[(1, "['Random', 'Task!']"), (2, "['Hello', 'World!']")]

how to fetch all the details and show in Tkinter GUI by using roll number menttion in two tables in the database?

I am new to tkinter , I am searching a way so that i can easily show my two tables Student and Issue Book table in database using roll number which is common in both ( and it is a primary key ) to the tkinter GUI Student Detail window. The idea is when i enter the roll number it shows the table record in student details as i mentioned the specific frames in the Gui and So how to make a function that display my database data to tkinter Gui .I am giving my Program ,please help me to give a clarify solution.
Students_details.py
The code for fetching data from database through an id and display it into two tables in the GUI is :
I made two functions one for fetching first table data .
def details():
rollnum = rnum.get()
print(rollnum)
conn = pymysql.connect(host='localhost', user='root', password='', db='data')
a = conn.cursor()
a.execute("select * from student where rollno='"+rollnum+"'")
results = a.fetchall()
count = a.rowcount
print(results)
print(count)
if count > 0:
for row in results:
rnum1.set(row[0])
sname.set(row[1])
fanm.set(row[2])
mtnm.set(row[3])
dateob.set(row[4])
bran.set(row[5])
else:
messagebox.showinfo("record not found")
conn.close()
and second for fetching another table from database
def bookdetails():
rollnum = rnum.get()
print(rollnum)
conn = pymysql.connect(host='localhost', user='root', password='', db='data')
b = conn.cursor()
b.execute("select * from issuebook where rollno='" + rollnum + "'")
resultb = b.fetchall()
countb = b.rowcount
print(resultb)
print(countb)
if countb > 0:
for row in resultb:
booknm.set(row[1])
booknum.set(row[2])
dateofiss.set(row[3])
lstdate.set(row[4])
else:
messagebox.showinfo("record not found")
conn.close()
I created another function for calling these two function
def details_and_bookdetails():
details()
bookdetails()
Then I passed the function details_and_bookdetails in the button through command.

Python - Continue Code Execution After Popping Up MsgBox?

I'm fairly new to Python. Here's a script I have that gathers info from our MySQL server hosting our Helpdesk tickets, and will pop up a message box (using EasyGUI's "msgbox()" function) whenever a new ticket arrives.
The issue is that I want my program to continue processing after the popup, regardless of whether the user clicks "OK" or not, even if that means message boxes could keep popping up over each other and must be dismissed one by one; that would be fine with me.
I looked into threading, and either it doesn't work or I did something wrong and need a good guide. Here's my code:
import MySQLdb
import time
from easygui import *
# Connect
db = MySQLdb.connect(host="MySQL.MyDomain.com", user="user", passwd="pass", db="db")
cursor = db.cursor()
# Before-and-after arrays to compare; A change means a new ticket arrived
IDarray = ([0,0,0])
IDarray_prev = ([0,0,0])
# Compare the latest 3 tickets since more than 1 may arrive in my time interval
cursor.execute("SELECT id FROM Tickets ORDER BY id DESC limit 3;")
numrows = int(cursor.rowcount)
for x in range(0,numrows):
row = cursor.fetchone()
for num in row:
IDarray_prev[x] = int(num)
cursor.close()
db.commit()
while 1:
cursor = db.cursor()
cursor.execute("SELECT id FROM Tickets ORDER BY id DESC limit 3;")
numrows = int(cursor.rowcount)
for x in range(0,numrows):
row = cursor.fetchone()
for num in row:
IDarray[x] = int(num)
if(IDarray != IDarray_prev):
cursor.execute("SELECT Subject FROM Tickets ORDER BY id DESC limit 1;")
subject = cursor.fetchone()
for line in subject:
# -----------------------------------------
# STACKOVERFLOW, HERE IS THE MSGBOX LINE!!!
# -----------------------------------------
msgbox("A new ticket has arrived:\n"+line)
# My time interval -- Checks the database every 8 seconds:
time.sleep(8)
IDarray_prev = IDarray[:]
cursor.close()
db.commit()
You can use Python GTK+
It offers non-modal using
set_modal(False)

Categories

Resources