# System that manages employee data for managers

This code is for an employee management system. The goal that I am trying to reach is to allow managers to access data of their employees more easily. The data that is entered is saved to a CSV file and I have systems in place that will allow those managers to access their employees' data through a quick search of the file. I'm 16 and a beginner, please explain some good clean code concepts that you guys recommend.

'''
Employee System

By Ronald Colyar : 1/2/2018

'''
#our modules for this project
import csv
from tkinter import *
from tkinter import ttk

#main class gui
class employee_gui :

def __init__(self , master):

self.master = master

#configuring the title of the main window , aswell as the background color
master.title(string = 'Pha<n>tex Employee Management System')
master.configure(background = 'black')

#employee first name
self.firstname_label = Label(master, text = 'Employee First Name ***' ,bg= 'black', fg ='white')
self.firstname_label.grid(row = 1 , column = 0 , sticky ='we', padx=5, pady=5)

self.first_name = ttk.Entry(master)
self.first_name.grid(row = 2 ,column  = 0 , sticky = 'we',padx=5, pady=5 )

#employee lastname
self.lastname_label = Label(master, text = 'Employee last Name ***',bg= 'black', fg ='white')

self.last_name = ttk.Entry(master)

#employee email
self.employee_email = Label(master, text = 'Employee Email ***',bg= 'black', fg ='white')
self.employee_email.grid(row = 3 , column  = 0 , columnspan = 2 , sticky = 'we')

self.employee_email_entry = ttk.Entry(master)
self.employee_email_entry.grid(row = 4  , column = 0 , columnspan = 3 , sticky = 'we', padx=5, pady=5)

#day
self.DAY= Label(master, text = 'Day**',bg= 'black', fg ='white')
self.DAY.grid(row = 5 , column = 0 , sticky = 'we',padx=5, pady=5)
#day options
self.dayoptions = ['1', '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9', '10' , '11' , '12' , '13' , '14' , '15' , '16' , '17' , '18' , '19', '20' , '21' , '22' , '23' , '24' , '25' , '26' , '27' , '28' , '29' , '30' , '31']
#day container
self.dayvar= StringVar()
self.dayvar.set('none')
self.dropdown_day.grid(row = 6 , column = 0 , sticky= 'we')

#month
self.month= Label(master, text = 'month**',bg= 'black', fg ='white')
self.month.grid(row = 5 , column = 1 , sticky = 'we',padx=5, pady=5)
#month options
self.monthoptions = ['1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '10' , '11' , '12']

#month container
self.monthvar = StringVar()
self.monthvar.set('select')

self.dropdown_month = ttk.OptionMenu(master, self.monthvar , *self.monthoptions)
self.dropdown_month.grid(row = 6 , column = 1 , sticky = 'we',padx=5, pady=5)

# year

#year options
self.years_unsplit       =  '2029 - 2028 - 2027 - 2026 - 2025 - 2024 - 2023 - 2022 - 2021 -2020 - 2019 - 2018 - 2017 - 2016 - 2015 - 2014 - 2013 - 2012 - 2011 -2010 - 2009 - 2008 - 2007 - 2006 - 2005 - 2004 - 2003 - 2002 - 2001 -2000 - 1999 - 1998 - 1997 - 1996 - 1995 - 1994 - 1993 - 1992 - 1991 - 1990 - 1989 - 1988 - 1987 - 1986 - 1985 - 1984 - 1983 - 1982 - 1981 -1980 - 1979 - 1978 - 1977 - 1976 - 1975 - 1974 - 1973 - 1972 - 1971 -1970 - 1969 - 1968 - 1967 - 1966 - 1965 - 1964 - 1963 - 1962 - 1961 -1960 - 1959 - 1958 - 1957 - 1956 - 1955 - 1954 - 1953 - 1952 - 1951 -1950 - 1949 - 1948 - 1947 - 1946 - 1945 - 1944 - 1943 - 1942 - 1941 -1940 - 1939 - 1938 - 1937 - 1936 - 1935 - 1934 - 1933 - 1932 - 1931 -1930 - 1929 - 1928 - 1927 - 1926 - 1925 - 1924 - 1923 - 1922 - 1921'

self.yearoptions2 = self.years_unsplit.split('-')
#year variable
self.yearvar = StringVar()

self.dropdown_year = ttk.OptionMenu(master , self.yearvar , *self.yearoptions2)
self.dropdown_year.grid(row = 6 ,column = 2 , sticky = 'we')

self.year = Label(master, text = 'Year**' ,bg= 'black', fg ='white')
self.year.grid(row = 5 , column = 2, sticky = 'we', padx=5, pady=5)

self.Address = Label(master ,text = 'Address(optional)*** Example: 110 s. grove street',bg= 'black', fg ='white')

#the position_ occupation section
self.Position = Label(master , text= 'Position/Occupation***' , bg= 'black', fg ='white')
self.Position.grid(row = 9 , column = 0 , sticky = 'we',padx=5, pady=5)
self.Position_entry = ttk.Entry(master)
self.Position_entry.grid(row = 10 , column = 0 ,columnspan = 3, sticky = 'we',padx=5, pady=5)

#thesalary section
self.salary = Label(master , text = 'Employee Salary***',bg= 'black', fg ='white')
self.salary.grid(row = 11 , column = 0 , sticky = 'we',padx=5, pady=5)

self.salary_entry = ttk.Entry(master)
self.salary_entry.grid(row = 12 , column = 0 , sticky = 'we',padx=5, pady=5)

#the employee id informaiton

self.employee_id_label = Label(master, text = 'Employee Id **VERY IMPORTANT**',bg= 'black', fg ='white')
self.employee_id_label.grid(row = 13 , column = 0 , sticky = 'we',padx=5, pady=5)

self.employee_id_entry = ttk.Entry(master)
self.employee_id_entry.grid(row = 14 , column = 0 , sticky = 'we' ,padx=5, pady=5 )

#the file section in the menu bar
self.filesystem.add_command(label = 'Display Employee Information'  , command =display_information_window)
self.savesystem.add_command(label= 'Save Employee File' , command= save_information_window)
self.deletesystem.add_command(label = 'Delete Employee' , command = delete_information_window)
self.help = Menu(master, tearoff = False)
self.help.add_command(label= 'Help' , command = help_window)

#the icon for the window
master.iconbitmap('employeeicon.ico')

#logos
self.photo = PhotoImage(file = 'phantexlogo.png')
self.phantexlogo = Label(master, image = self.photo, bg= 'black', fg ='white')
self.phantexlogo.image = self.photo
self.phantexlogo.grid(row = 15 , column = 0 , sticky = 'we')

self.photo2 = PhotoImage(file = 'phantexlogo.png')
self.phantexlogo2 = Label(master, image = self.photo, bg= 'black', fg ='white')
self.phantexlogo2.image = self.photo
self.phantexlogo2.grid(row = 15 , column = 1 , sticky = 'we')

def delete_information_method():
#a tkinter entry
global delete_entry, csv_writer1
with open('employees.csv' , 'r', newline='') as emp_read:
contents = [line for line in csv_dictreader]
#opening csv file in write mode
with open ('employees.csv' , 'w', newline='') as emp_write:
#creating our writer
csv_writer1 = csv.DictWriter(emp_write, fieldnames=fieldnames)
#our loop to check each line inside of our csv file is not equal to what is inside the delete entry
for line in contents:
if line['employee id'] != str(delete_entry.get()):
csv_writer1.writerow(line)

#grabbing all the employee data and inserting it inside of the listbox
def all_emp_search():
global information_box
#deleting data out of the listbox , that was previously there
information_box.delete(0 , 'end')
with open ('employees.csv' , 'r') as employee_read_file:
#inserting all information inside of the csv file into the listbox
information_box.insert(END , line)
#grabbing a single employee data and inserting it inside the listbox
def single_emp_search():

global search_label_entry,information_box
#deleting data out of the listbox , that was previously there
information_box.delete(0 , END)
with open ('employees.csv' , 'r') as employee_read_file:
'''

searching to see if the contents of the csv file matches what is put inside of the entry
and if so inserting the line into the listbox
'''
if line.find(str(search_label_entry.get())) > -1:
information_box.insert(END , line)

#the information deletion window

def delete_information_window():
global delete_entry
delete_frame = Toplevel()
delete_frame.config(background = 'black')

#intro label(title)
delete_intro_header = Label(delete_frame , text = 'Welcome to the Delete section' , font = 'times 14 bold',bg= 'black', fg ='white')
delete_intro_header.grid(row  = 2 , column = 0 , sticky = 'we')

delete_entry_header = Message(delete_frame , text = 'Enter in the employee ID , you would like to remove , if you dont recall , you can access the employee information , by going to Mainpage/Open/Display Employee Information, here you can search for an employee name , and all the information including the ID will be present',bg= 'black', fg ='white')
delete_entry_header.grid(row = 3, column = 0 , sticky= 'we')

# delete entry
delete_note_header = Label(delete_frame , text = 'Note: Once You Delete An Employee There is no recovery , be careful with this process',bg= 'black', fg ='white')
delete_note_header.grid(row = 4, column = 0 , sticky = 'we')

delete_entry = ttk.Entry(delete_frame )
delete_entry.grid(row = 5, column = 0 , sticky  = 'we')
#the delete button(submit button)
delete_button = Button(delete_frame , text = 'Delete Employee' ,  fg = 'white', bg = 'black',command = delete_information_method)
delete_button.grid(row = 5 , column = 1, sticky = 'we')

#the information display window

def display_information_window():
global search_label_entry,information_box
display_frame = Toplevel()
display_frame.config(background = 'black')

#the intro message(title)
intro_message = Label(display_frame, text = 'Welcome to the Display section' , font = 'times 14 bold',bg= 'black', fg ='white')

search_label = Label(display_frame , text = 'Search for one Employee information',bg= 'black', fg ='white')
search_label.grid(row = 3 , column = 0 , sticky = 'we')
#the search entry
search_label_entry = ttk.Entry(display_frame)
search_label_entry.grid(row = 4, column = 0 , sticky = 'we')

all_information_label = Label(display_frame , text = 'All employee information',bg= 'black', fg ='white')
all_information_label.grid(row = 3 , column =2 , sticky = 'we')

#our listbox
information_box = Listbox(display_frame, bd = 0 , width = 70)
information_box.grid(row  = 6 , column = 0, sticky= 'we')
#all information button
all_information_button = Button(display_frame , text = 'All Information',bg = 'black' , fg = 'white',  command = all_emp_search)
all_information_button.grid(row = 4 , column = 2 , sticky = 'we')
#emp single search Button
search_single_emp = Button(display_frame , text  = 'Search Single Employee', command  = single_emp_search,bg = 'black' , fg = 'white' )

search_single_emp.grid(row = 5 , column = 2 , sticky = 'we')

#help window
def help_window():
help_frame = Toplevel()

intro_message = Label(help_frame, text = 'Welcome to the help section' , font = 'times 14 bold')
intro_message.grid(row = 3  , column = 0 , sticky = 'we')

mainmessage = Message(help_frame, text = 'The way this program works , is it allows you to  store your employee information , and go back and later access it , using the employee ID feature. The Employee ID makes accessing your information more smooth and manageable , feel free to use this program for your buisnesses and ect. Try to keep the employee IDs different for management purposes')
mainmessage.grid(row = 4 , column = 0 , sticky  ='we')

help_frame.iconbitmap('employeeicon.ico')

#saving the information to the csv file method
def save_information_window():
global csvwriter
#grabbing all the information from our entrys

#putting our information into a list of strings ,
fieldnames_list = ['first name' , 'last name' , 'email' , 'DOB' ,'adress' ,'position' , 'employee salary' , 'employee id'  ]
whole_information = [str(first_info) , str(last_info) , str(email_info) , str(month_info + '-' +day_info +'-' + year_info ) ,str(adress_info),  str(position_info) , str(employee_salary_info) , str(employee_id_info) ]
#creating our dictonary , using the fieldnames as the key and the whole information as the value of the dictionary
my_whole_info_dict = dict(zip(fieldnames_list , whole_information))

#opening our csv file in write mode and adding the data from the entrys

with open('employees.csv', 'a' ,newline = "") as employee_file:
csvwriter = csv.DictWriter(employee_file, fieldnames = fieldnames_list , delimiter = ',')

csvwriter.writerow(my_whole_info_dict)

#main window
root = Tk()

#employee_gui object

#constant loop
root.mainloop()


Welcome to Code Review! Kudos to writing a fairly large program.

Several things pop-out from your program. But, a few things first. If you are using any intelligent editor; please see if you can get a python linter (or a PEP-8 integration) in it. PEP-8 is the python's style guide, which makes the code consistent, and hence, readable/maintainable.

I noticed that you are creating months, days and years list. Use the range function. It'll generate the whole list for you. You can later map the list to convert each value to string (if tkinter expects strings):

self.yearoptions2 = map(str, range(2029, 1921, -1))


Do not rely on global variables. As your codebase grows; it'll be difficult to follow which globals are referencing what. Pass the delete_entry, information_box etc. values as parameters to your functions instead.

Nest the execution of your tkinter application inside an if __name__ block:

if __name__ == "__main__":
#main window
root = Tk()

#employee_gui object

#constant loop
root.mainloop()


I haven't executed your program yet. However, I'd also suggest that instead of maintaining CSV's for the purpose of database, you make use of an actual DBMS. Out of the box, python provides you with sqlite3 package.

This will help in performance when your employee records reach let's say a 1000 employees and you want to filter employees within a salary range or similar analytical observations.

Some important PEP-8 pointers you can follow:

• A statement/line-of-code should be at most 80 characters
• Avoid whitespaces in following scenarios:
• Immediately before a comma, semicolon, or colon
• Immediately before the open parenthesis that starts the argument list
• Don't use spaces around the = sign when used to indicate a keyword argument
• woah , you got some great points ,so you think sqlite3 would be better? , i use sublime text 3 as a enviorment btw – RonsJurisdiction Jan 10 '18 at 9:38
• aswell as , do you really think an if name is main statement would be necessary , or are you making it future proof? – RonsJurisdiction Jan 10 '18 at 9:41
• @RonComputing An if __name__ == "__main__": construct is necessary (besides looking a heck of a lot neater than not using it since it immediately tells the reader what the top level of your code is) when you start to import your code from elsewhere. If you don't do it, all code will be executed on import. With such a clause, it will only be executed if the file itself is executed instead of imported by another file. – Mast Jan 10 '18 at 9:44
• @RonComputing packagecontrol.io/packages/Python%20PEP8%20Autoformat sqlite3 is just a suggestion. Maintaining CSV is hard, and inefficient for analytical purposes. Since you are a beginner, sqlite3 would be the easiest DBMS to learn. – hjpotter92 Jan 10 '18 at 9:48
• @RonComputing just to talk a little bit about sqlite for a moment. In the "real world" most programs will be dealing with SQL in some way so most developers will need to know at least a little SQL. sqlite is the simplest SQL database to setup, it is much more lightweight than things like MySQL or Postgres. – Captain Man Jan 10 '18 at 14:22

## Don't do wildcard imports

Use import tkinter as tk and then prefix all tk classes and commands with tk. (eg: tk.Tk(), tk.Frame(...), etc).

PEP8 discourages wildcard imports, and for good reasons. They pollute the global namespace, and they can overwrite variables and classes without you knowing. Tkinter is particularly susceptible to this since both tk and ttk define classes with the same name.

## Use PEP8 naming conventions

You should adopt PEP8 naming conventions. Specifically, start class names with an uppercase character and use CamelCase (eg: class EmployeeGUI).

Comments are important, but they can also be a source of noise. Comments like #employee first name immediately before creating a variable named self.firstname_label are pointless.

## Group your layout statements together

You should separate widget creation from widget layout. By grouping your layout code together it's much easier to visualize, and much easier to modify. It's also easier to notice inconsitencies, such as using slightly different colors or paddings for some widgets. My experience tells me that during development the layout can change fairly often.

Organize them in the same groups as they appear in the UI. For example, if all of your labels and entries are in one big block in the UI, make them one big block in the code. If you break them out into separate UI sections (eg: an address block, a name block, etc.) then organize them that way.

For example:

    self.firstname_label = Label(...)
self.first_name = ttk.Entry(...)
self.lastname_label = Label(...)
self.last_name = ttk.Entry(...)
self.employee_email = Label(...)
self.employee_email_entry = ttk.Entry(...)
...

self.firstname_label.grid(....)
self.first_name.grid(...)
self.lastname_label.grid(...)
self.last_name.grid(...)
self.employee_email.grid(...)
self.employee_email_entry.grid(...)
...


Whether you do "label, entry, label, entry, ...", or "label, label, label, ..., entry, entry, entry, ..." is whatever you think makes the code easier to understand.

• great point i will look into organizing my code in groups seeing as though that will be eaiser to manage – RonsJurisdiction Jan 10 '18 at 12:22
• I had completely missed the wildcard import. Nice catch :) – hjpotter92 Jan 10 '18 at 13:05
• I actually don't mind wildcard imports in tkinter -- it's idiomatic there (even if it's idiotic elsewhere). – Adam Smith Jan 10 '18 at 18:24
• @AdamSmith: I wouldn't say it's idiomatic. Pervasive, perhaps, probably because one guy chose to do it when writing one of the first tutorials and everyone copied from that one source. It's bad for Tkinter for the same reasons it's bad for anything else - it pollutes the global namespace and makes the code a bit harder to understand. However, at the end of the day it's mostly personal preference. – Bryan Oakley Jan 10 '18 at 18:55
• @BryanOakley ehh that's splitting hairs though isn't it? Idiomatic means little apart from "pervasive [in its context]," and though I agree that explicit namespacing is better in the general case, "A Foolish Consistency" wins through for me. If you're writing a tkinter GUI, write it like tkinter GUIs are written, which (in my subjective experience) includes from tkinter import *. That said: I'm aware I'm bikeshedding a bit ;) – Adam Smith Jan 10 '18 at 20:52

In addition to the two previous answers, you should keep the initializer (__init__()) light and minimalist, and delegate most of the job to different functions each fulfilling one simple goal (You may throw a glance to Tkinter best practices). Also I find the individual functions to be quite long and an improved design would lead to getting rid of the global variables in use

Using files to store large amounts of data can be problematic causing data ambiguities, data islands, etc. You'd be better off using a well designed database, which you then manipulate with SQL. Check out the dba section of stack overflow for more info. You also might want to write your user-interface in HTML, so a company could easily use it without installing python and tk.

• i plan on converting this into a exe before selling it or allowing a company to use it. There will be no need for a windows based computer to download any thing programming related. – RonsJurisdiction Jan 11 '18 at 12:20
• I'm a little bias toward HTML's client-server architecture. Using it isn't necessary, but without it you will have to manage some things you otherwise wouldn't have to. A few things that come to mind are: version control and installation across multiple machines, security (which machines can you install it on vs https with a password), and making calls to a remote DB server via FTP or something. Of course, using an exe has advantages too. Ultimately you have to make these decisions yourself. – mikeLundquist Jan 11 '18 at 17:13
• No , that is a great point , i didnt take in the possibility of multi platforms installing the application – RonsJurisdiction Jan 12 '18 at 12:35