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()
\$\endgroup\$
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.

\$\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\$
    – Jakub
    Sep 29 at 19:49
0
\$\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()
\$\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\$
    – aghast
    Oct 4 at 21:09
  • \$\begingroup\$ Thank you. Will remember for the next time. \$\endgroup\$
    – Jakub
    Oct 4 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.