341 lines
15 KiB
Python
341 lines
15 KiB
Python
#! /usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# GUI module generated by PAGE version 5.4
|
|
# in conjunction with Tcl version 8.6
|
|
# Sep 30, 2020 01:32:51 PM CDT platform: Linux
|
|
|
|
import sys
|
|
from PIL import Image, ImageTk
|
|
import sqlite3
|
|
from datetime import datetime
|
|
from datetime import date
|
|
import os
|
|
import shutil
|
|
from tkinter import messagebox
|
|
from xlsxwriter.workbook import Workbook
|
|
from barcode import Code39
|
|
from barcode.writer import ImageWriter
|
|
import subprocess
|
|
import time
|
|
|
|
try:
|
|
import Tkinter as tk
|
|
except ImportError:
|
|
import tkinter as tk
|
|
|
|
try:
|
|
import ttk
|
|
py3 = False
|
|
except ImportError:
|
|
import tkinter.ttk as ttk
|
|
py3 = True
|
|
|
|
import pecos_support
|
|
|
|
# --- SET UP A TIMESTAMP FUNCTION THAT UPDATES PROPERLY ---
|
|
def timestamp():
|
|
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
def ReportTime():
|
|
return datetime.now().strftime('%H:%M:%S on %A, %B %d, %Y')
|
|
|
|
|
|
def vp_start_gui():
|
|
'''Starting point when module is the main routine.'''
|
|
global val, w, root
|
|
root = tk.Tk()
|
|
top = Toplevel1 (root)
|
|
pecos_support.init(root, top)
|
|
root.iconbitmap('PECOS.ico')
|
|
root.mainloop()
|
|
|
|
w = None
|
|
def create_Toplevel1(rt, *args, **kwargs):
|
|
'''Starting point when module is imported by another module.
|
|
Correct form of call: 'create_Toplevel1(root, *args, **kwargs)' .'''
|
|
global w, w_win, root
|
|
#rt = root
|
|
root = rt
|
|
w = tk.Toplevel (root)
|
|
top = Toplevel1 (w)
|
|
pecos_support.init(w, top, *args, **kwargs)
|
|
return (w, top)
|
|
|
|
def destroy_Toplevel1():
|
|
global w
|
|
w.destroy()
|
|
w = None
|
|
# Close database connection
|
|
conn.close()
|
|
|
|
class Toplevel1:
|
|
def __init__(self, top=None):
|
|
'''This class configures and populates the toplevel window.
|
|
top is the toplevel containing window.'''
|
|
_bgcolor = '#d9d9d9' # X11 color: 'gray85'
|
|
_fgcolor = '#000000' # X11 color: 'black'
|
|
_compcolor = '#d9d9d9' # X11 color: 'gray85'
|
|
_ana1color = '#d9d9d9' # X11 color: 'gray85'
|
|
_ana2color = '#ececec' # Closest X11 color: 'gray92'
|
|
|
|
top.geometry("600x500+650+150")
|
|
top.minsize(1, 1)
|
|
top.maxsize(1905, 1050)
|
|
top.resizable(0, 0)
|
|
top.title("ECOS - Emergency Check Out System 1.3b (Windows)")
|
|
|
|
# --- CHECKOUT ITEM AND UPDATE DATABASE ---
|
|
def CheckOutItem(event=None):
|
|
if len(self.item_entry.get()) == 0:
|
|
conn = sqlite3.connect('checkouts.db')
|
|
c = conn.cursor()
|
|
|
|
row = c.execute("SELECT BatchNumber FROM Batches ORDER BY BatchNumber DESC LIMIT 1").fetchone()
|
|
if row is not None:
|
|
GetLastBatchNumber = row[0]
|
|
CurrentBatchNumber = GetLastBatchNumber + 1
|
|
|
|
c.execute("INSERT INTO Batches VALUES (:BatchNumber, :PatronID)",
|
|
{
|
|
'BatchNumber': CurrentBatchNumber,
|
|
'PatronID': self.patron_entry.get()
|
|
}
|
|
)
|
|
# Commit changes
|
|
conn.commit()
|
|
|
|
self.patron_entry.delete(0, tk.END)
|
|
self.item_entry.delete(0, tk.END)
|
|
self.patron_entry.configure(state='normal')
|
|
self.patron_entry.delete(0, tk.END)
|
|
self.patron_entry.focus_set()
|
|
|
|
else:
|
|
# Set up database connections
|
|
conn = sqlite3.connect('checkouts.db')
|
|
c = conn.cursor()
|
|
|
|
row = c.execute("SELECT BatchNumber FROM Batches ORDER BY BatchNumber DESC LIMIT 1").fetchone()
|
|
if row is not None:
|
|
GetLastBatchNumber = row[0]
|
|
CurrentBatchNumber = GetLastBatchNumber + 1
|
|
|
|
c.execute("INSERT INTO CheckOuts VALUES (:PatronID, :ItemID, :TransactionDateTime, :ItemLink, :PatronLink, :BatchNumber)",
|
|
{
|
|
'PatronID': self.patron_entry.get(),
|
|
'ItemID': self.item_entry.get(),
|
|
'TransactionDateTime': timestamp(),
|
|
'ItemLink': ("item/" + self.item_entry.get()),
|
|
'PatronLink': ("patron/" + self.patron_entry.get()),
|
|
'BatchNumber': CurrentBatchNumber
|
|
}
|
|
)
|
|
# Commit changes
|
|
conn.commit()
|
|
|
|
#PatronBarcode = self.patron_entry.get()
|
|
PatCode = Code39(self.patron_entry.get(), add_checksum=False)
|
|
PatCode.save("patron/"+self.patron_entry.get())
|
|
|
|
ItemCode = Code39(self.item_entry.get(), add_checksum=False)
|
|
ItemCode.save("item/"+self.item_entry.get())
|
|
|
|
self.item_entry.delete(0, tk.END)
|
|
|
|
# --- PATRON SCANNED - MOVE TO ITEM FIELD ---
|
|
def ChangeFocus(event):
|
|
self.item_entry.focus_set()
|
|
self.patron_entry.configure(state='disabled')
|
|
|
|
|
|
# --- FINISH CHECKOUT AND CLEAR FIELDS ---
|
|
def CheckOutComplete():
|
|
conn = sqlite3.connect('checkouts.db')
|
|
c = conn.cursor()
|
|
|
|
row = c.execute("SELECT BatchNumber FROM Batches ORDER BY BatchNumber DESC LIMIT 1").fetchone()
|
|
if row is not None:
|
|
GetLastBatchNumber = row[0]
|
|
CurrentBatchNumber = GetLastBatchNumber + 1
|
|
|
|
c.execute("INSERT INTO Batches VALUES (:BatchNumber, :PatronID)",
|
|
{
|
|
'BatchNumber': CurrentBatchNumber,
|
|
'PatronID': self.patron_entry.get()
|
|
}
|
|
)
|
|
# Commit changes
|
|
conn.commit()
|
|
|
|
self.patron_entry.delete(0, tk.END)
|
|
self.item_entry.delete(0, tk.END)
|
|
self.patron_entry.configure(state='normal')
|
|
self.patron_entry.delete(0, tk.END)
|
|
self.patron_entry.focus_set()
|
|
|
|
# --- DELETE THE CURRENT DATABASE AND SUPPLANT WITH AN EMPTY ONE ---
|
|
def ClearDatabase():
|
|
self.ClearConfirm = tk.messagebox.askquestion ('ECOS - Clear Database','Are you sure you wish to clear the database? This cannot be undone!')
|
|
if self.ClearConfirm == 'yes':
|
|
# Next two lines commented out in Windows version - the connection commands work on macOS
|
|
#conn = sqlite3.connect('checkouts.db') # Re-establish connection to the database
|
|
#conn.close # Close out the current database
|
|
self.ConfirmClear = tk.messagebox.askquestion ('ECOS - Confim','Final answer: Are you sure you wish to clear the database?')
|
|
if self.ConfirmClear == 'yes':
|
|
|
|
ItemFolder = 'item/'
|
|
ItemBackup = 'itembackup/'
|
|
PatronFolder = 'patron/'
|
|
PatronBackup = 'patronbackup/'
|
|
DatabaseFile = 'checkouts.db'
|
|
dbBackup = 'checkouts-backup.db'
|
|
ExportFile = 'export.html'
|
|
|
|
if os.path.exists(DatabaseFile): # Delete the current database
|
|
os.remove(DatabaseFile)
|
|
if os.path.exists(dbBackup): # Delete the database backup
|
|
os.remove(dbBackup)
|
|
if os.path.exists(ExportFile): # Delete the export file
|
|
os.remove(ExportFile)
|
|
if os.path.exists(ItemFolder): # Delete the item folder
|
|
shutil.rmtree(ItemFolder)
|
|
if os.path.exists(PatronFolder): # Delete the patron folder
|
|
shutil.rmtree(PatronFolder)
|
|
if os.path.exists(ItemBackup): # Delete the backup item folder
|
|
shutil.rmtree(ItemBackup)
|
|
if os.path.exists(PatronBackup): # Delete the backup patron folder
|
|
shutil.rmtree(PatronBackup)
|
|
os.makedirs(ItemFolder) # Recreate the item folder
|
|
os.makedirs(PatronFolder) # Recreate the patron folder
|
|
os.makedirs(PatronBackup) # Recreate the backup patron folder
|
|
os.makedirs(ItemBackup) # Recreate the backup item folder
|
|
shutil.copy("emptybase.db", "checkouts.db") # Copy over an empty database
|
|
|
|
# Next two lines commented out in Windows version - the connection commands work on macOS
|
|
#conn = sqlite3.connect('checkouts.db') # Restablish connection to the database
|
|
#c = conn.cursor()
|
|
else:
|
|
messagebox.showinfo('ECOS - Database Retained','Database retained. No changes made.')
|
|
|
|
# --- EXPORT DATABASE TO HTML ---
|
|
def ExportDatabase():
|
|
if os.path.exists("export.html"):
|
|
os.remove("export.html")
|
|
conn = sqlite3.connect('checkouts.db')
|
|
c = conn.cursor()
|
|
with open("export.html", "w") as f:
|
|
f.write("<html>\n")
|
|
f.write("<head>\n")
|
|
f.write("<title>\n")
|
|
f.write("ECOS Export - " + ReportTime() + "\n")
|
|
f.write("</title>\n")
|
|
f.write("<h2>ECOS Export File – " + ReportTime() + "</h2>\n<hr>\n")
|
|
f.write("<style type=\"text/css\">\n")
|
|
f.write(".tg {border-collapse:collapse;border-spacing:0;}\n")
|
|
f.write(".tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px; overflow:hidden;padding:10px 5px;word-break:normal;}\n")
|
|
f.write(".tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n")
|
|
f.write(".tg .tg-cly1{text-align:left;vertical-align:middle}\n")
|
|
f.write("</style>\n")
|
|
f.write("<table class=\"tg\">\n")
|
|
f.write("<thead>\n")
|
|
f.write("<tr>\n")
|
|
f.write("<th class=\"tg-cly1\"><strong>Patron Library Card</strong></th>")
|
|
f.write("<th class=\"tg-cly1\"><strong>Batch Number</strong></th>")
|
|
f.write("<th class=\"tg-cly1\"><strong>Item Barcode</strong>\n")
|
|
f.write("</tr>\n")
|
|
for row in c.execute("SELECT BatchNumber, PatronLink, ItemLink FROM CheckOuts ORDER By BatchNumber"):
|
|
f.write("<tr>\n")
|
|
f.write("<th class=\"tg-cly1\"><img src=\"" + row[1] + ".svg\" width=200></th>")
|
|
f.write("<th class=\"tg-cly1\"><strong>Batch: </strong>" + str(row[0]) + "</th>")
|
|
f.write("<th class=\"tg-cly1\"><img src=\"" + row[2] + ".svg\" width=200>\n")
|
|
f.write("</tr>\n")
|
|
|
|
#subprocess.run(['open', 'export.html'], check=True) # Open export.html in the default web browser (macOS)
|
|
os.startfile('export.html') # Open export.html in the default web browser (Windows)
|
|
|
|
# --- BACKUP THE DATABASE ---
|
|
def BackupData():
|
|
# Set path variables for backup folders
|
|
ItemFolder = 'item/'
|
|
ItemBackup = 'itembackup/'
|
|
PatronFolder = 'patron/'
|
|
PatronBackup = 'patronbackup/'
|
|
DatabaseFile = 'checkouts.db'
|
|
dbBackup = 'checkouts-backup.db'
|
|
|
|
# Check to see if backup folders are there. If so, delete them.
|
|
if os.path.exists(ItemBackup):
|
|
shutil.rmtree(ItemBackup)
|
|
|
|
if os.path.exists(PatronBackup):
|
|
shutil.rmtree(PatronBackup)
|
|
|
|
if os.path.exists(dbBackup):
|
|
os.remove(dbBackup)
|
|
|
|
# Copy item, patron, and database folder to Backup
|
|
shutil.copytree(ItemFolder, ItemBackup)
|
|
shutil.copytree(PatronFolder, PatronBackup)
|
|
shutil.copy(DatabaseFile, dbBackup)
|
|
|
|
self.DatabaseDone = tk.messagebox.showinfo ('ECOS - Backup Database','Database backup complete.')
|
|
|
|
|
|
# --- END OF FUNCTIONS - BEGIN GUI ---
|
|
|
|
self.Canvas1 = tk.Canvas(top)
|
|
self.image = ImageTk.PhotoImage(Image.open("bg-01.png"))
|
|
self.Canvas1.create_image(0,0,anchor="nw",image=self.image)
|
|
self.Canvas1.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0)
|
|
self.Canvas1.configure(borderwidth="2")
|
|
self.Canvas1.configure(relief="ridge")
|
|
self.Canvas1.configure(selectbackground="blue")
|
|
self.Canvas1.configure(selectforeground="white")
|
|
|
|
self.Frame1 = tk.Frame(self.Canvas1)
|
|
self.Frame1.place(relx=0.05, rely=0.06, relheight=0.17, relwidth=0.908)
|
|
self.Frame1.configure(relief='sunken')
|
|
self.Frame1.configure(borderwidth="2")
|
|
self.Frame1.configure(relief="sunken")
|
|
|
|
self.patron_label = tk.Label(self.Canvas1)
|
|
self.patron_label.place(relx=0.067, rely=0.08, height=19, width=100)
|
|
self.patron_label.configure(text='''Patron barcode:''')
|
|
|
|
self.patron_entry = tk.Entry(self.Canvas1)
|
|
self.patron_entry.place(relx=0.25, rely=0.08,height=25, relwidth=0.377)
|
|
self.patron_entry.configure(background="white")
|
|
self.patron_entry.configure(font="TkFixedFont")
|
|
self.patron_entry.bind('<Return>', ChangeFocus)
|
|
|
|
self.item_label = tk.Label(self.Canvas1)
|
|
self.item_label.place(relx=0.067, rely=0.16, height=19, width=89)
|
|
self.item_label.configure(text='''Item barcode:''')
|
|
|
|
self.item_entry = tk.Entry(self.Canvas1)
|
|
self.item_entry.place(relx=0.25, rely=0.16,height=25, relwidth=0.377)
|
|
self.item_entry.configure(background="white")
|
|
self.item_entry.configure(font="TkFixedFont")
|
|
self.item_entry.bind('<Return>', CheckOutItem)
|
|
|
|
self.finish_button = tk.Button(self.Canvas1, command=BackupData)
|
|
self.finish_button.place(relx=0.7, rely=0.12, height=29, width=123)
|
|
self.finish_button.configure(text='''Backup Database''')
|
|
|
|
self.LowerFrame = tk.Frame(self.Canvas1)
|
|
self.LowerFrame.place(relx=0.05, rely=0.78, relheight=0.13, relwidth=0.908)
|
|
self.LowerFrame.configure(relief='sunken')
|
|
self.LowerFrame.configure(borderwidth="2")
|
|
self.LowerFrame.configure(relief="sunken")
|
|
|
|
self.export_button = tk.Button(self.LowerFrame, command=ExportDatabase)
|
|
self.export_button.place(relx=0.092, rely=0.308, height=29, width=130)
|
|
self.export_button.configure(text='''Export Checkouts''')
|
|
|
|
self.clear_button = tk.Button(self.LowerFrame, command=ClearDatabase)
|
|
self.clear_button.place(relx=0.679, rely=0.308, height=29, width=130)
|
|
self.clear_button.configure(text='''Clear Database''')
|
|
|
|
if __name__ == '__main__':
|
|
vp_start_gui() |