3
\$\begingroup\$

Below there is a full code for my app Staff Manager. The app does allow basic staff management, like adding, removing or changing details of employees. As it is one of my first GUI apps and I am self-taught (I have been learning for about 6 months now, a couple of hours every day after work) I would like to see what you guys think about my code. Would like to see the opinion of other people. Any advice on improvements (to my work and code) is welcome. Won't get upset if I will get criticized. Here to learn and improve.

from json.decoder import JSONDecodeError
import tkinter as tk
from tkinter import StringVar, Toplevel, ttk
import os, json
import copy
class Staff_Manager(tk.Tk):
 def __init__(self) -> None:
 super().__init__()
 self.check_save_file()
 self.geometry("1520x600")
 self.title("Staff Manager")
 self.iconbitmap("icon.ico")
 
 self.buttons()
 self.data_view()
 self.update
 self.delete
 self.is_retired
 # check if data save file exists and if not create one.
 def check_save_file(self):
 current_path = os.path.dirname(__file__)
 json_file = f'{current_path}\\emp_data.json'
 if os.path.isfile(json_file) == True and os.stat(json_file).st_size != 0:
 pass
 else:
 with open(json_file, 'w+') as f:
 data = {"people": []}
 json.dump(data, f)
 
 def buttons(self):
 # managing buttons, main window
 add = ttk.Button(self, text= 'Add', command= Add_New_Emp)
 add.place(x= 10, y= 10, width= 75)
 
 amend = ttk.Button(self, text= 'Amend', command= self.amend)
 amend.place(x= 10, y= 45, width= 75)
 refresh = ttk.Button(self, text= 'Refresh', command= self.update)
 refresh.place(x= 10, y= 80, width= 75)
 
 retire = ttk.Button(self, text= 'Retire', command= self.is_retired)
 retire.place(x= 10, y= 135, width= 75, height= 40)
 
 remove = ttk.Button(self, text= 'Remove', command= self.delete)
 remove.place(x= 10, y= 230, width= 75, height= 40)
 exit_button = ttk.Button(self, text= 'EXIT', command= self.destroy)
 exit_button.place(x= 10, y= 538, width= 75, height= 40) 
 def data_view(self):
 #create columns for tree view
 columns = ('#1', '#2', '#3', '#4', '#5', '#6', '#7')
 self.view_panel = ttk.Treeview(self, columns= columns, show= 'headings', height= 27)
 self.view_panel.place(x= 90, y= 10)
 # headings
 self.view_panel.heading('#1', text= 'Name')
 self.view_panel.heading('#2', text= 'Surname')
 self.view_panel.heading('#3', text= 'Position')
 self.view_panel.heading('#4', text= 'DoB')
 self.view_panel.heading('#5', text= 'Start Date')
 self.view_panel.heading('#6', text= 'End Date')
 self.view_panel.heading('#7', text= 'Retired?')
 # set up the scrollbar
 scrollbar = ttk.Scrollbar(self, orient= tk.VERTICAL, command= self.view_panel.yview)
 self.view_panel.configure(yscrollcommand= scrollbar.set)
 scrollbar.place(x= 1495, y= 10, height= 575)
 # insert data
 with open('emp_data.json', 'r') as f:
 empty_file = False
 try: 
 data = json.load(f)
 except JSONDecodeError:
 empty_file = True
 pass
 employees = []
 if empty_file == False:
 
 for item in data["people"]:
 name = item["name"]
 surname = item["surname"]
 position = item["position"]
 dob = item["dob"]
 start = item["start"]
 end = item["end"]
 retired = item["retired"]
 employees.append((name, surname, position, dob, start, end, retired))
 # insert all the data to the view panel
 for emp in employees:
 self.view_panel.insert('', tk.END, values= emp)
 # update information on the view panel
 def update(self):
 self.data_view()
 # amend emp data
 def amend(self):
 amend_win = tk.Tk()
 amend_win.geometry('350x250')
 amend_win.title('Amend Employee Details')
 amend_win.iconbitmap('icon.ico')
 
 # cancel button
 def is_cancel():
 amend_win.destroy()
 
 # change button
 def is_amended(to_change, new_personal_data):
 current_item = self.view_panel.focus()
 info = self.view_panel.item(current_item)
 details = info["values"]
 to_remove = {
 "name": str(details[0]),
 "surname": str(details[1]),
 "position": str(details[2]),
 "dob": str(details[3]),
 "start": str(details[4]),
 "end": str(details[5]),
 "retired": str(details[6])
 }
 to_remove = copy.deepcopy(to_remove)
 details_dict = {
 "name": str(details[0]),
 "surname": str(details[1]),
 "position": str(details[2]),
 "dob": str(details[3]),
 "start": str(details[4]),
 "end": str(details[5]),
 "retired": str(details[6])
 }
 
 
 for key, value in details_dict.items():
 if key == to_change:
 details_dict[f"{to_change}"] = new_personal_data
 
 with open('emp_data.json', 'r') as f:
 data = json.load(f)
 new_data = {
 "people": []
 }
 for emp_dict in data["people"]:
 if to_remove != emp_dict:
 new_data["people"].append(emp_dict)
 new_data["people"].append(details_dict)
 
 with open('emp_data.json', 'w') as f:
 json.dump(new_data, f, indent= 4)
 amend_win.destroy()
 
 # check if data is not empty, if is: n/a
 def is_empty(data):
 if len(data) == 0:
 return 'n/a'
 else:
 return data
 main_choice_lbl = ttk.Label(amend_win, text= 'What would you like to change?', font= ('Arial', 12))
 main_choice_lbl.place(x= 10, y= 10)
 self.data_options = ('name', 'surname', 'position', 'dob', 'start', 'end')
 self.choice = StringVar()
 options = ttk.OptionMenu(amend_win,
 self.choice,
 self.data_options[0],
 *self.data_options)
 options.place(x= 10, y= 47)
 options.config(width= 15)
 new_emp_data = ttk.Label(amend_win, text= 'New data:', font= ('Arial', 12))
 new_emp_data.place(x= 10, y= 90)
 main_entry = ttk.Entry(amend_win, justify= 'right')
 main_entry.place(x= 10, y= 130)
 ok_button = ttk.Button(amend_win, text= 'CHANGE', command= lambda: is_amended(self.choice.get(), is_empty(main_entry.get())))
 ok_button.place(x= 60, y= 200)
 cancel_button = ttk.Button(amend_win, text= 'CANCEL', command= is_cancel)
 cancel_button.place(x= 150, y= 200)
 
 def delete(self):
 # get the "clicked" emp
 current_item = self.view_panel.focus()
 # get the info of "clicked" emp : dict
 info = self.view_panel.item(current_item)
 # get details of "clicked" emp, "values" are stored emp data : list
 details = info["values"]
 # set the data to json format (need to convert some int to str as program is creating all data as strings)
 details_dict = {
 "name": str(details[0]),
 "surname": str(details[1]),
 "position": str(details[2]),
 "dob": str(details[3]),
 "start": str(details[4]),
 "end": str(details[5]),
 "retired": str(details[6])
 }
 with open('emp_data.json', 'r') as f:
 data = json.load(f)
 
 new_data = {
 "people": []
 }
 for emp_dict in data["people"]:
 if details_dict != emp_dict:
 new_data['people'].append(emp_dict)
 with open('emp_data.json', 'w') as f:
 json.dump(new_data, f, indent= 4)
 
 def is_retired(self):
 retired_win = tk.Tk()
 retired_win.geometry('280x130')
 # cancel button
 def is_cancel():
 retired_win.destroy()
 def is_ok():
 current_item = self.view_panel.focus()
 info = self.view_panel.item(current_item)
 details = info["values"]
 to_remove = {
 "name": str(details[0]),
 "surname": str(details[1]),
 "position": str(details[2]),
 "dob": str(details[3]),
 "start": str(details[4]),
 "end": str(details[5]),
 "retired": str(details[6])
 }
 to_remove = copy.deepcopy(to_remove)
 details_dict = {
 "name": str(details[0]),
 "surname": str(details[1]),
 "position": str(details[2]),
 "dob": str(details[3]),
 "start": str(details[4]),
 "end": str(details[5]),
 "retired": str(details[6])
 }
 
 
 # for key, value in details_dict.items():
 details_dict["retired"] = "Yes"
 
 with open('emp_data.json', 'r') as f:
 data = json.load(f)
 new_data = {
 "people": []
 }
 for emp_dict in data["people"]:
 if to_remove != emp_dict:
 new_data["people"].append(emp_dict)
 new_data["people"].append(details_dict)
 
 with open('emp_data.json', 'w') as f:
 json.dump(new_data, f, indent= 4)
 retired_win.destroy()
 ret_lbl = ttk.Label(retired_win, text= "Are you sure you want to RETIRE your employee?")
 ret_lbl.place(x= 10, y= 20)
 ok_button = ttk.Button(retired_win, text= 'RETIRE', command= is_ok)
 ok_button.place(x= 40, y= 60)
 cancel_button = ttk.Button(retired_win, text= 'CANCEL', command= is_cancel)
 cancel_button.place(x= 150, y= 60)
 
class Add_New_Emp(Toplevel):
 def __init__(self) -> None:
 super().__init__()
 self.geometry('380x400')
 self.title('Employee Data')
 self.iconbitmap('icon.ico')
 self.labels()
 self.data_entry()
 # set labels
 def labels(self):
 name_lbl = ttk.Label(self, text= 'Name:', font= ('Arial', 12))
 name_lbl.place(x= 10, y= 10)
 
 surname_lbl = ttk.Label(self, text= 'Surname:', font= ('Arial', 12))
 surname_lbl.place(x= 10, y= 50)
 
 position_lbl = ttk.Label(self, text= 'Position:', font= ('Arial', 12))
 position_lbl.place(x= 10, y= 90)
 
 dob_lbl = ttk.Label(self, text= 'Date of Birth:', font= ('Arial', 12))
 dob_lbl.place(x= 10, y= 130)
 
 start_lbl = ttk.Label(self, text= 'Start Date:', font= ('Arial', 12))
 start_lbl.place(x= 10, y= 170)
 
 end_lbl = ttk.Label(self, text= 'End Date:', font= ('Arial', 12))
 end_lbl.place(x= 10, y= 210)
 
 retired_lbl = ttk.Label(self, text= 'Retired?', font= ('Arial', 12))
 retired_lbl.place(x= 10, y= 250)
 # set data entry points
 def data_entry(self):
 def is_empty(data):
 if len(data) == 0:
 return 'n/a'
 else:
 return data
 # accepting details, pressing ok button
 def is_ok():
 # get all the data
 name = is_empty(name_ent.get())
 surname = is_empty(surname_ent.get())
 position = is_empty(position_ent.get())
 dob = is_empty(dob_ent.get())
 start = is_empty(start_ent.get())
 end = is_empty(end_ent.get())
 retired = retired_var.get()
 # append row to the employee data file
 with open('emp_data.json', 'r+') as f:
 data = json.load(f)
 
 new_emp = {
 "name": name,
 "surname": surname,
 "position": position,
 "dob": dob,
 "start": start,
 "end": end,
 "retired": retired
 }
 
 data["people"].append(new_emp)
 with open('emp_data.json', 'w') as f: 
 json.dump(data, f, indent= 4)
 
 self.destroy()
 
 # cancelation of adding new emp
 def is_cancel():
 self.destroy()
 name_ent = ttk.Entry(self, justify= 'right')
 name_ent.place(x= 215, y= 10)
 
 surname_ent = ttk.Entry(self, justify= 'right')
 surname_ent.place(x= 215, y= 50)
 
 position_ent = ttk.Entry(self, justify= 'right')
 position_ent.place(x= 215, y= 90)
 
 dob_ent = ttk.Entry(self, justify= 'right')
 dob_ent.place(x= 215, y= 130)
 
 start_ent = ttk.Entry(self, justify= 'right')
 start_ent.place(x= 215, y= 170)
 
 end_ent = ttk.Entry(self, justify= 'right')
 end_ent.place(x= 215, y= 210)
 retired_var = StringVar(value= 'No')
 retired_chk = ttk.Checkbutton(
 self,
 text= 'Yes',
 onvalue= 'Yes',
 offvalue= 'No',
 variable= retired_var)
 retired_chk.place(x= 215, y= 250)
 ok_button = ttk.Button(self, text= 'ADD', command= is_ok)
 ok_button.place(x= 100, y= 350)
 cancel_button = ttk.Button(self, text= 'CANCEL', command= is_cancel)
 cancel_button.place(x= 200, y= 350)
 
if __name__ == "__main__":
 Staff_Manager().mainloop()
AJNeufeld
35.2k5 gold badges41 silver badges103 bronze badges
asked Sep 29, 2021 at 15:50
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

PEP 8

You deviate from the Style Guide for Python Code in a couple of areas:

  • Keyword arguments should not have spaces around the equals. For example
    add = ttk.Button(self, text= 'Add', command= Add_New_Emp)
    
    should be
    add = ttk.Button(self, text='Add', command=Add_New_Emp)
    
  • Class names should be CapitalizedWords, not Capitalized_Words_With_Underscores. So Staff_Manager and Add_New_Emp should be StaffManager and AddNewEmp.

Type Hints

The only place I see type hints used is def __init__(self) -> None:. Clearly you've just started using them, but you need to use them more, and run your could through a checker (mypy, pylint, ...)

Dead code

 self.buttons()
 self.data_view()
 self.update
 self.delete
 self.is_retired

The last three "statements" do nothing, and should be deleted.

Poor naming

Functions named is_xxx() look like functions which do not modify any state and return a True or False result. Your functions change the state of the program, and return nothing. Find better names, like do_cancel() and perform_amend.

Reuse Components

Each time .data_view() is called, a new ttk.Treeview is created, and placed on top of where the previous one was.

Create the ttk.Treeview once, and refresh its contents when information changes.

PathLib

os.path.isfile(file) and os.stat(file) are old-school functions. You should start using the newer pathlib. Eg)

from pathlib import Path
...
 json_file = Path(__file__).parent / 'emp_data.json'
 if json_file.is_file() and json_file.stat().st_size != 0:
 ...

Consistent Filename

Sometimes you use f'{current_path}\\emp_data.json' to specify the database file, other times you use with open('emp_data.json', 'w') as f: which might be writing to a completely different location!

Data Access Object

I'm seeing a lot of duplicate code for reading and writing to the emp_data.json database. You should create and use a Data Access Object to manage the your database.

For example:

class EmployeeDB:
 def __init__(self, json_file: Path) -> None:
 self._file = json_file
 # Create an empty database, if file doesn't exist or is empty
 if not json_file.is_file() or json_file.stat().st_size == 0:
 self.save([])
 def load(self) -> list:
 employees = []
 with open(self._file, 'r') as f:
 data = json.load(f)
 for item in data['people']:
 ...
 return employees
 def save(self, employees: list) -> None:
 with open(self._file, 'w') as f:
 data = {"people": employees}
 json.dump(data, f, indent=4)
class StaffManager(tk.Tk):
 def __init__(self, database: EmployeeDB) -> None:
 self._db = database
 ...
 ...
...
if __name__ == '__main__':
 db = EmployeeDB(Path('emp_data.json'))
 StaffManager(db).mainloop()

Dataclass

Each employee is a dictionary.

You should create an actual Employee dataclass to hold the information. From that, you can add additional methods, like .age() which could return a calculated value based on .dob

from dataclasses import dataclass
@dataclass
class Employee:
 name: str
 surname: str
 position: str
 dob: str
 start: str
 end: str
 retired: str

The EmployeeDB could now use list[Employee] as type-hints, instead of a bare list.


There are many more changes I'd make, but this is a good start. I look forward to reviewing the next revision.

answered Sep 29, 2021 at 19:25
\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much for your extensive answer. A lot to learn from it for me. This is what I wanted so I can improve. Thank You very much. Dead code and Class Names are already corrected. \$\endgroup\$ Commented Sep 29, 2021 at 19:49
1
\$\begingroup\$

Not sure if I should create a new post or answer in this one. Here is a revised version of my Staff Manager APP.

Improved:

  • Class Names
  • Function Names
  • Type Hints (yes previously I thought I only have to do only int, str etc.)
  • PeP 8 (still learning and improving this one all the time)
  • Dead Code removed
  • Created Data Access Object (Thank you, that was a brilliant idea)
  • Filename (specified in one place, will always have to be the same)
  • Tried to reuse components more (view panel is being closed and created now if there are any changes to display)

Not happened yet:

  • Pathlib, yes still used os as I never used Pathlib in the past, currently learning that module as well.
  • Dataclass, haven't done it in this version. Actually planning now to create a "proper" app with more functionality where I will use dataclass as it is a brilliant idea, although this app is meant to be very simple.

Any advice, comments will be appreciated. A few small changes happened and Thanks to AJNeufeld also 290 lines of code went down to 225 lines while the program does exactly the same thing and everything is working as it is supposed. The data file is still in human-readable form (although it would be easier to store data as lists and just correctly display them in the app).

import json
from os import path
import tkinter as tk
from tkinter import StringVar, ttk
import os
from tkinter.constants import CENTER, NO
from ttkbootstrap import Style
class Window(tk.Tk):
 def __init__(self, database: object) -> None:
 super().__init__()
 self._db = database
 self.geometry('1000x500')
 self.title('Staff Manager')
 self.iconbitmap('icon.ico')
 self.buttons()
 self.data_view()
 
 # set theme for the app (from ttkbootstrap)
 style = Style(theme='darkly')
 self = style.master
 def buttons(self) -> None:
 # managing buttons, main window
 add = ttk.Button(self, text='Add', command=self.add_new_emp)
 add.place(x=10, y=10, width=75, height=40)
 amend = ttk.Button(self, text='Amend', command=self.amend_emp)
 amend.place(x=10, y=55, width=75, height=40)
 retire = ttk.Button(self, text='Retire', command=self.retire_emp)
 retire.place(x=10, y=100, width=75, height=40)
 remove = ttk.Button(self, text='Remove', command=self.delete_emp)
 remove.place(x=10, y=180, width=75, height=40)
 exit_button = ttk.Button(self, text= 'EXIT', command=self.destroy)
 exit_button.place(x=10, y=450, width=75, height=40)
 
 def data_view(self) -> None:
 # create columns
 columns = ('#1', '#2', '#3', '#4', '#5', '#6', '#7')
 self.view_panel = ttk.Treeview(self, columns=columns, show='headings', height=27, selectmode='browse')
 self.view_panel.place(x=90, y=10, width=890, height=480)
 # headings
 self.view_panel.heading('#1', text='Name')
 self.view_panel.heading('#2', text='Surname')
 self.view_panel.heading('#3', text='Position')
 self.view_panel.heading('#4', text='DoB')
 self.view_panel.heading('#5', text='Start Date')
 self.view_panel.heading('#6', text='End Date')
 self.view_panel.heading('#7', text='Retired?')
 
 # set colums properties
 self.view_panel.column('#1', anchor=CENTER, stretch=NO, width=135)
 self.view_panel.column('#2', anchor=CENTER, stretch=NO, width=135)
 self.view_panel.column('#3', anchor=CENTER, stretch=NO, width=135)
 self.view_panel.column('#4', anchor=CENTER, stretch=NO, width=135)
 self.view_panel.column('#5', anchor=CENTER, stretch=NO, width=135)
 self.view_panel.column('#6', anchor=CENTER, stretch=NO, width=135)
 self.view_panel.column('#7', anchor=CENTER, stretch=NO, width=80)
 
 # set scrollbal for view panel
 scrollbar = ttk.Scrollbar(self, orient= tk.VERTICAL, command= self.view_panel.yview)
 self.view_panel.configure(yscrollcommand=scrollbar.set)
 scrollbar.place(x=980, y=10, width=20, height=480)
 for emp in EmployeeDatabase.load(self._db):
 self.view_panel.insert('', tk.END, values=emp)
 def refresh_view_panel(self) -> None:
 self.view_panel.destroy()
 self.data_view()
 def add_new_emp(self) -> None:
 new_emp_win = tk.Toplevel(self)
 new_emp_win.geometry('380x400')
 new_emp_win.title('Add New Employee')
 new_emp_win.iconbitmap('icon.ico')
 def check_empty_entry(data: str) -> str:
 if len(data) == 0:
 return 'n/a'
 else:
 return data
 def accept_new_emp() -> None:
 # get all the data
 name = check_empty_entry(name_ent.get())
 surname = check_empty_entry(surname_ent.get())
 position = check_empty_entry(position_ent.get())
 dob = check_empty_entry(dob_ent.get())
 start = check_empty_entry(start_ent.get())
 end = check_empty_entry(end_ent.get())
 retired = retired_var.get()
 new_emp = [name, surname, position, dob, start, end, retired]
 EmployeeDatabase.save(self._db, employees=new_emp)
 
 new_emp_win.destroy()
 self.refresh_view_panel()
 
 # set labels for Add New Emp window
 name_lbl = ttk.Label(new_emp_win, text='Name:', font=('Arial', 12))
 name_lbl.place(x=10, y=10)
 surname_lbl = ttk.Label(new_emp_win, text='Surname:', font=('Arial', 12))
 surname_lbl.place(x=10, y=50)
 position_lbl = ttk.Label(new_emp_win, text='Position:', font=('Arial', 12))
 position_lbl.place(x=10, y=90)
 dob_lbl = ttk.Label(new_emp_win, text='Date of Birth:', font=('Arial', 12))
 dob_lbl.place(x=10, y=130)
 start_lbl = ttk.Label(new_emp_win, text='Start Date:', font=('Arial', 12))
 start_lbl.place(x=10, y=170)
 end_lbl = ttk.Label(new_emp_win, text='End Date:', font=('Arial', 12))
 end_lbl.place(x=10, y=210)
 retired_lbl = ttk.Label(new_emp_win, text='Retired?', font=('Arial', 12))
 retired_lbl.place(x=10, y=250)
 # set entry points for data
 name_ent = ttk.Entry(new_emp_win, justify='right')
 name_ent.place(x=215, y=10)
 
 surname_ent = ttk.Entry(new_emp_win, justify='right')
 surname_ent.place(x=215, y=50)
 
 position_ent = ttk.Entry(new_emp_win, justify='right')
 position_ent.place(x=215, y=90)
 
 dob_ent = ttk.Entry(new_emp_win, justify='right')
 dob_ent.place(x=215, y=130)
 
 start_ent = ttk.Entry(new_emp_win, justify='right')
 start_ent.place(x=215, y=170)
 
 end_ent = ttk.Entry(new_emp_win, justify='right')
 end_ent.place(x=215, y=210)
 retired_var = StringVar(value='No')
 retired_chk = ttk.Checkbutton(
 new_emp_win,
 text='Yes',
 onvalue='Yes',
 offvalue='No',
 variable=retired_var)
 retired_chk.place(x=215, y=250)
 ok_button = ttk.Button(new_emp_win, text='ADD', command=accept_new_emp)
 ok_button.place(x=100, y=350)
 cancel_button = ttk.Button(new_emp_win, text='CANCEL', command=new_emp_win.destroy)
 cancel_button.place(x=200, y=350)
 def delete_emp(self) -> None:
 # get "clicked" emp
 current_emp = self.view_panel.focus()
 emp_info = self.view_panel.item(current_emp)
 emp_details = emp_info["values"]
 # need it as tuple for 'for loop' comparison
 emp_details = tuple(emp_details)
 # delete emp
 new_data = []
 for employee in EmployeeDatabase.load(self._db):
 if employee != emp_details:
 new_data.append(employee)
 EmployeeDatabase.create_new(self._db, [])
 
 for emp in new_data:
 EmployeeDatabase.save(self._db, emp)
 # update view panel
 self.refresh_view_panel()
 def change_details(self, change_detail_index: int, new_detail: str) -> None:
 # get 'clicked' emp
 current_emp = self.view_panel.focus()
 emp_info = self.view_panel.item(current_emp)
 details = emp_info["values"]
 # need it as tuple for 'for loop' comparison
 details_tup = tuple(details)
 emp_to_change = details
 # copy rest of emps
 new_data = []
 for employee in EmployeeDatabase.load(self._db):
 if employee != details_tup:
 new_data.append(employee)
 # append emp with changed detail
 details[change_detail_index] = new_detail
 new_data.append(details)
 EmployeeDatabase.create_new(self._db, [])
 # write to database
 for emp in new_data:
 EmployeeDatabase.save(self._db, emp)
 
 self.refresh_view_panel()
 def amend_emp(self) -> None:
 amend_win = tk.Toplevel()
 amend_win.geometry('250x250')
 amend_win.title('Amend Employee Details')
 amend_win.iconbitmap('icon.ico')
 
 # check if data is not empty, if is: n/a
 def is_empty(data: str) -> str:
 if len(data) == 0:
 return 'n/a'
 else:
 return data
 
 main_choice_lbl = ttk.Label(amend_win, text='What would you like to change?', font=('Arial', 12))
 main_choice_lbl.place(x=10, y=10)
 self.data_options = ('Name', 'Surname', 'Position', 'DoB', 'Start', 'End')
 self.choice = StringVar()
 options = ttk.OptionMenu(amend_win,
 self.choice,
 self.data_options[0],
 *self.data_options)
 options.place(x=10, y=47)
 options.config(width=15)
 new_emp_data = ttk.Label(amend_win, text='New data:', font=('Arial', 12))
 new_emp_data.place(x=10, y=90)
 main_entry = ttk.Entry(amend_win, justify='right')
 main_entry.place(x=10, y=130)
 ok_button = ttk.Button(amend_win, text='CHANGE', command=lambda: [self.change_details(self.data_options.index(self.choice.get()), is_empty(main_entry.get())), amend_win.destroy()])
 ok_button.place(x=60, y=200)
 cancel_button = ttk.Button(amend_win, text='CANCEL', command=amend_win.destroy)
 cancel_button.place(x=150, y=200)
 def retire_emp(self) -> None:
 retired_win = tk.Toplevel()
 retired_win.geometry('280x130')
 ret_lbl = ttk.Label(retired_win, text="Are you sure you want to RETIRE your employee?")
 ret_lbl.place(x=10, y=20)
 
 ok_button = ttk.Button(retired_win, text='RETIRE', command=lambda: [self.change_details(6, "Yes"), retired_win.destroy()])
 ok_button.place(x=40, y=60)
 cancel_button = ttk.Button(retired_win, text='CANCEL', command=retired_win.destroy)
 cancel_button.place(x=150, y=60)
class EmployeeDatabase:
 def __init__(self, json_file: str) -> None:
 self._file = json_file
 
 if os.path.isfile(json_file) == True and os.stat(json_file).st_size != 0:
 pass
 else:
 self.create_new([])
 def load(self) -> list[str]:
 employees: list[str] = []
 with open(self._file, 'r') as f:
 data = json.load(f)
 for item in data["people"]:
 name: str = item["name"]
 surname: str = item["surname"]
 position: str = item["position"]
 dob: str = item["dob"]
 start: str = item["start"]
 end: str = item["end"]
 retired: str = item["retired"]
 
 employees.append((name, surname, position, dob, start, end, retired))
 return employees
 
 
 def create_new(self, employees: list) -> None:
 with open(self._file, 'w') as f:
 data = {"people": employees}
 json.dump(data, f, indent=4)
 def save(self, employees: list[str]) -> None:
 new_emp = {
 "name": employees[0],
 "surname": employees[1],
 "position": employees[2],
 "dob": employees[3],
 "start": employees[4],
 "end": employees[5],
 "retired": employees[6]
 }
 with open(self._file, 'r') as f:
 data = json.load(f)
 data["people"].append(new_emp)
 with open(self._file, 'w') as f:
 json.dump(data, f, indent=4)
if __name__ == '__main__':
 path = f'{os.path.dirname(__file__)}\\emp_db.json'
 db = EmployeeDatabase(path)
 Window(database= db).mainloop()
answered Oct 4, 2021 at 20:58
\$\endgroup\$
2
  • 1
    \$\begingroup\$ In general, do not bother posting quick updates like this. Instead, wait a day or two (or seven) to get all the responses, then if you want more feedback post a new question with your updated code. (And if you want "help" feel free to post a link to an online repository for the code.) \$\endgroup\$ Commented Oct 4, 2021 at 21:09
  • \$\begingroup\$ Thank you. Will remember for the next time. \$\endgroup\$ Commented Oct 4, 2021 at 21:13

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.