The Wine Library



This program implements an object oriented management system for a wine library. It incorprates a user friendly GUI to facilitate user interaction. A detailed documentation can be found at:

Overview of Wine Library Program



In [22]:
# Ex 3.3 Wine Library

####################################################
# IMPORT PACKAGES TO SUPPORT THE PROGRAM
####################################################

import csv
from tkinter import *
from tkinter import ttk

####################################################
# DEFINE THE BOTTLE CLASS
####################################################

class Bottle():  
    def __init__(self,bottle_id,winery,varietal,vintage,cat,comment,bin_no,irow,icol):
        self.bottle_id=bottle_id
        self.winery=winery
        self.varietal=varietal
        self.vintage=vintage
        self.cat=cat
        self.comment=comment
        self.bin_no=bin_no
        self.irow=irow
        self.icol=icol
    def update_bottle(self,bid):
        bottles[bid].winery=bot_winery_entry.get() 
        bottles[bid].varietal=bot_var_entry.get()
        bottles[bid].vintage=bot_vin_entry.get()
        bottles[bid].cat=bot_cat_entry.get()
        bottles[bid].comment=bot_com_entry.get()
        bottles[bid].bin_no=bot_bin_entry.get()
        bottles[bid].irow=bot_row_entry.get()
        bottles[bid].icol=bot_col_entry.get()
    def display_bottles(self,bottle_id):
        bottle_tree.insert('', 'end', values=(bottles[bottle_id].bottle_id,
                                              bottles[bottle_id].winery, 
                                              bottles[bottle_id].varietal,
                                              bottles[bottle_id].vintage,
                                              bottles[bottle_id].cat,
                                              bottles[bottle_id].comment,
                                              bottles[bottle_id].bin_no,
                                              bottles[bottle_id].irow,
                                              bottles[bottle_id].icol))

#################################################### 
# INSTANTIATE BOTTLE OBJECTS FROM Wine-data.csv
####################################################

bottles=[] # holds the bottle objects; the index is the bottle number
nbottles=0 # number of bottles in cellar
varietal_list=["Select Varietal"]
cat_list=["select category","all"]
winery_list=["Select Winery"]
rack_list=["select rack"]

with open('Data/Wine-data.csv') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            line_count += 1
        else:
            bottle_id=int(row[0])
            winery=row[1]
            varietal=row[2]
            vintage=row[3]
            cat=row[4]
            comment=row[5]
            bin_no=int(row[6])
            irow=int(row[7])
            icol=int(row[8])
            new_bottle=Bottle(bottle_id,winery,varietal,vintage,cat,comment,bin_no,irow,icol)
            bottles.append(new_bottle)
            if(winery not in winery_list):winery_list.append(winery)
            if(cat not in cat_list):cat_list.append(cat)
            if(varietal not in varietal_list):varietal_list.append(varietal)
            if(bin_no not in rack_list):rack_list.append(bin_no)
            nbottles+=1

####################################################            
# CREATE THE GUI INTERFACE
#################################################### 
root = Tk()
root.geometry("1000x800")

select_cat = StringVar(root)
select_var = StringVar(root)
select_winery = StringVar(root)
select_rack = StringVar(root)


####################################################
# SELECT FRAME
####################################################

#-----Define functions used in Select Frame  
def update_cat(event):
    global select_cat
    #Clear the treeview list items
    for item in bottle_tree.get_children():
        bottle_tree.delete(item)
    select=str(select_cat.get())
    for iid in range(nbottles): 
        if(select=="all") : 
            bottles[iid].display_bottles(iid)
        else:
            if(bottles[iid].cat==select and bottles[iid].bin_no != 0) : 
                bottles[iid].display_bottles(iid)  

def update_var(event):
    global select_var
    #Clear the treeview list items
    for item in bottle_tree.get_children():
        bottle_tree.delete(item)
    select=str(select_var.get())
    for iid in range(nbottles):
        if(bottles[iid].varietal==select and bottles[iid].bin_no != 0):
            bottles[iid].display_bottles(iid)

def update_winery(event):
    global select_winery
    #Clear the treeview list items
    for item in bottle_tree.get_children():
        bottle_tree.delete(item)
    select=str(select_winery.get())
    for iid in range(nbottles):
        if(bottles[iid].winery==select and bottles[iid].bin_no != 0): bottles[iid].display_bottles(iid)
            
def update_rack(event):
    global select_rack

    Nrows=[1,6,4,3]
    Ncols=[100,9,2,6]
    #Clear the treeview list items
    for item in bottle_tree.get_children():
        bottle_tree.delete(item)
    select=str(select_rack.get())
    for iid in range(nbottles): bottles[iid].display_bottles(iid)
    if(select!="0"):
        iselect=int(select)
        rack = Tk()
        rack.geometry("1600x1800+100+100")
        rack.title("Rack"+ select)
        rack.configure(background='burlywood4')
        textvar=StringVar(rack)
        textvar="EMPTY"
        nlabel=0
        
        empty=Label(rack,text="",width=2, height=2,font=("Arial Bold", 16),bg='burlywood4',justify=CENTER)
        empty.grid(row=0, column=0,padx=10,pady=10)
        #----Create column headers       
        for ic in range (Ncols[iselect]):
            chdr=Label(rack,text=str(ic),width=2, height=2,font=("Arial Bold", 16),bg='burlywood4',justify=CENTER)
            chdr.grid(row=0,column=(1+ic),padx=10,pady=10)
            
        #-----Create row headers
        for ir in range (Nrows[iselect]):
            rhdr=Label(rack,text=str(ir),width=2, height=2,font=("Arial Bold", 16),bg='burlywood4',justify=CENTER)
            rhdr.grid(row=(1+ir),column=0,padx=10,pady=10)

        #-----Create Body
        for rack_row in range (0,Nrows[iselect]):
            for rack_col in range (0,Ncols[iselect]):
                new_label=Label(rack,text=textvar,width=15,height=6, justify=CENTER) 
                for i in range(nbottles):             
                    if(bottles[i].bin_no==iselect and bottles[i].irow==rack_row and bottles[i].icol==rack_col):
                        newtext=str("id="+str(bottles[i].bottle_id)+"   vtg="+str(bottles[i].vintage) +"\n"+str(bottles[i].winery)+"\n"+ str(bottles[i].varietal))
                        new_label.config(text=newtext)
                new_label.grid(row=rack_row+1,column=1+rack_col,padx=10,pady=10)

#-----Create and pack select_frame into root               
select_frame=Frame(root, width=800, height=300)
select_frame.pack()

#-----Create option menu (cat) and grid in select frame
cat_menu=ttk.OptionMenu(select_frame,select_cat,*cat_list,command=update_cat)
cat_menu.config(width=10)
cat_menu.grid(column=0, row=0,padx=10,pady=10)

#-----Create option menu (varietal) and grid in select frame
varietal_menu=ttk.OptionMenu(select_frame, select_var,
                             *varietal_list,command=update_var)
varietal_menu.config(width=10)
varietal_menu.grid(column=1, row=0,padx=10,pady=10)

#-----Create option menu (winery) and grid in select frame
winery_menu=ttk.OptionMenu(select_frame, select_winery,*winery_list,command=update_winery)
winery_menu.config(width=10)
winery_menu.grid(column=2, row=0,padx=10,pady=10)

#-----Create option menu (rack) and grid in select frame
rack_menu=ttk.OptionMenu(select_frame, select_rack,*rack_list,command=update_rack)
rack_menu.config(width=10)
rack_menu.grid(column=3, row=0,padx=10,pady=10)    

####################################################
# BOTTLE FRAME
####################################################
bottle_frame=Frame(root)
bottle_frame.pack(pady=20)

#--- BOTTLE FRAME FUNCTIONS
def select_record(event):
    global r_vals
    # clear entry box
    bot_id_entry.delete(0,END)
    bot_winery_entry.delete(0,END)
    bot_var_entry.delete(0,END)
    bot_vin_entry.delete(0,END)
    bot_cat_entry.delete(0,END)
    bot_com_entry.delete(0,END)    
    bot_bin_entry.delete(0,END)
    bot_row_entry.delete(0,END)
    bot_col_entry.delete(0,END)
    
    # Grab record and values
    r_select=bottle_tree.focus()
    r_vals=bottle_tree.item(r_select,'values')
    # Enter r_vals into entry boxes
    bot_id_entry.insert(0,r_vals[0])
    bot_winery_entry.insert(0,r_vals[1])
    bot_var_entry.insert(0,r_vals[2])
    bot_vin_entry.insert(0,r_vals[3])
    bot_cat_entry.insert(0,r_vals[4])
    bot_com_entry.insert(0,r_vals[5])    
    bot_bin_entry.insert(0,r_vals[6])
    bot_row_entry.insert(0,r_vals[7])
    bot_col_entry.insert(0,r_vals[8])

bottle_frame=Frame(root)
bottle_frame.pack(pady=20)
# create Treeview Scrollbar within bottle_frame
bottle_scroll=Scrollbar(bottle_frame)
bottle_scroll.pack(side=RIGHT,fill=Y)

style=ttk.Style()
style.theme_use("default")

# Create an object of Style widget
style.configure("Treeview", background= "#D3D3D3", foreground="black",rowheight=25, fieldbackground= "#D3D3D3")
style.map("Treeview", background=[('selected','blue')])

# create Treeview
bottle_tree=ttk.Treeview(bottle_frame,yscrollcommand=bottle_scroll.set)
bottle_tree.pack()

#Configure the scrollbar
bottle_scroll.config(command=bottle_tree.yview)

bottle_scroll.config(command=bottle_tree.yview)
# Add a Treeview widget

bottle_tree['columns']=("Bottle_id", "winery","varietal","vintage",
                        "category","comment","bin_no","row","col")
bottle_tree.column("#0", width=0)
bottle_tree.heading("#0",text="", anchor=W)
bottle_tree.column("#1", width=60, anchor=CENTER)
bottle_tree.heading("#1", text="Bottle_id")
bottle_tree.column("#2",width=100, anchor=CENTER)
bottle_tree.heading("#2", text="winery")
bottle_tree.column("#3",width=100, anchor=CENTER)
bottle_tree.heading("#3", text="varietal")
bottle_tree.column("#4",width=40, anchor=CENTER)
bottle_tree.heading("#4", text="vintage")
bottle_tree.column("#5",width=60, anchor=CENTER)
bottle_tree.heading("#5", text="category")
bottle_tree.column("#6",width=150, anchor=CENTER)
bottle_tree.heading("#6", text="comment")
bottle_tree.column("#7",width=40,  anchor=CENTER)
bottle_tree.heading("#7",text="bin_no")
bottle_tree.column("# 8",width=40,  anchor=CENTER)
bottle_tree.heading("#8", text="row")
bottle_tree.column("#9",width=40,  anchor=CENTER)
bottle_tree.heading("#9", text="col")

bottle_tree.bind("<ButtonRelease-1>", select_record)

####################################################
# EDIT FRAME
####################################################
edit_frame=LabelFrame(root, text="Edit")
edit_frame.pack(fill="x", padx=10, pady=10)

bot_id_label=Label(edit_frame, text="Bottle ID")
bot_id_label.grid(row=0, column=0, padx=10, pady=10)
bot_id_entry=Entry(edit_frame)
bot_id_entry.grid(row=0, column=1, padx=10, pady=10)

bot_winery_label=Label(edit_frame, text="Winery")
bot_winery_label.grid(row=0, column=2, padx=10, pady=10)
bot_winery_entry=Entry(edit_frame,bg='lightcyan')
bot_winery_entry.grid(row=0, column=3, padx=10, pady=10)

bot_var_label=Label(edit_frame, text="Varietal")
bot_var_label.grid(row=0, column=4, padx=10, pady=10)
bot_var_entry=Entry(edit_frame,bg='lightcyan')
bot_var_entry.grid(row=0, column=5, padx=10, pady=10)

bot_vin_label=Label(edit_frame, text="Vintage")
bot_vin_label.grid(row=1, column=0, padx=10, pady=10)
bot_vin_entry=Entry(edit_frame,bg='lightcyan')
bot_vin_entry.grid(row=1, column=1, padx=10, pady=10)

bot_cat_label=Label(edit_frame, text="Category")
bot_cat_label.grid(row=1, column=2, padx=10, pady=10)
bot_cat_entry=Entry(edit_frame,bg='lightcyan')
bot_cat_entry.grid(row=1, column=3, padx=10, pady=10)

bot_com_label=Label(edit_frame, text="Comment")
bot_com_label.grid(row=1, column=4, padx=10, pady=10)
bot_com_entry=Entry(edit_frame,bg='lightcyan')
bot_com_entry.grid(row=1, column=5, padx=10, pady=10)

bot_bin_label=Label(edit_frame, text="Bin No.")
bot_bin_label.grid(row=2, column=0, padx=10, pady=10)
bot_bin_entry=Entry(edit_frame,bg='lightcyan')
bot_bin_entry.grid(row=2, column=1, padx=10, pady=10)

bot_row_label=Label(edit_frame, text="Row No.")
bot_row_label.grid(row=2, column=2, padx=10, pady=10)
bot_row_entry=Entry(edit_frame,bg='lightcyan')
bot_row_entry.grid(row=2, column=3, padx=10, pady=10)

bot_col_label=Label(edit_frame, text="Column No.")
bot_col_label.grid(row=2, column=4, padx=10, pady=10)
bot_col_entry=Entry(edit_frame,bg='lightcyan')
bot_col_entry.grid(row=2, column=5, padx=10, pady=10)



####################################################
# COMMAND FRAME AND COMMAND BUTTONS
####################################################

com_frame=LabelFrame(root, text="Commands")
com_frame.pack(pady=10)
def edit_bottle():
    #Get the record 
    selected=bottle_tree.focus()
    bottle_tree.item(selected, text="", values=[bot_id_entry.get(),
                                                 bot_winery_entry.get(),
                                                 bot_var_entry.get(),
                                                 bot_vin_entry.get(),
                                                 bot_cat_entry.get(),
                                                 bot_com_entry.get(),
                                                 bot_bin_entry.get(),
                                                 bot_row_entry.get(),
                                                 bot_col_entry.get()])
# Update bottle object
    bid=int(bot_id_entry.get())
    bottles[bid].update_bottle(bid)

#---------------------------------------------------
# save function: saves all edits to csv file
#---------------------------------------------------

def savecsv():

    header = ['bottle_no','winery','varietal','vintage','class','comments','bin_no','row_no','col_no']
    with open('Data/Wine-data.csv', 'w', encoding='UTF8') as f:
        writer = csv.writer(f)
    # write the header
    
        writer.writerow(header)   

    # write the data from Bottle ojects
    
        for iid in range (nbottles):
            data=[bottles[iid].bottle_id,bottles[iid].winery,bottles[iid].varietal,bottles[iid].vintage,
                      bottles[iid].cat,bottles[iid].comment,bottles[iid].bin_no,bottles[iid].irow,
                      bottles[iid].icol]
            writer.writerow(data)
def add_bottle():   
    global nbottles
    nbottles+=1
    new_bottle=Bottle(nbottles,bot_winery_entry.get(),bot_var_entry.get(),
                      bot_vin_entry.get(),bot_cat_entry.get(),bot_com_entry.get(),bot_bin_entry.get(),
                      bot_row_entry.get(),bot_col_entry.get())   
    bottles.append(new_bottle)    
    bot_id_entry.delete(0,END)
    bot_winery_entry.delete(0,END)
    bot_var_entry.delete(0,END)
    bot_vin_entry.delete(0,END)
    bot_cat_entry.delete(0,END)
    bot_com_entry.delete(0,END)    
    bot_bin_entry.delete(0,END)
    bot_row_entry.delete(0,END)
    bot_col_entry.delete(0,END)
    
def consume():
    bottles[int(r_vals[0])].bin_no=0
    bottles[int(r_vals[0])].irow=0
    bottles[int(r_vals[0])].icol=0
    
#---------------------------------------------------
# Buttons in com_frame
#---------------------------------------------------
                     
update_button=Button(com_frame,text="Edit Bottle", command=edit_bottle)
update_button.grid(row=0,column=0, padx=10, pady=10)

save_button=Button(com_frame,text="Save Edited Data",command=savecsv)
save_button.grid(row=0,column=1, padx=10, pady=10)

new_button=Button(com_frame,text="New Bottle",command=add_bottle)
new_button.grid(row=0,column=2, padx=10, pady=10)

consume_button=Button(com_frame,text="Consumed Wine",command=consume)
consume_button.grid(row=0,column=3, padx=10, pady=10)




####################################################
# START TKINTER EVENT MONITORING
####################################################
root.mainloop()

####################################################
# © Donald R. Falkenburg 2023
####################################################
In [ ]:
 
In [ ]: