Advertisement
Guest User

Untitled

a guest
Jan 25th, 2020
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 33.96 KB | None | 0 0
  1. import tkinter as tk
  2. import tkinter.messagebox
  3. import csv
  4. import datetime
  5. from fpdf import FPDF
  6. import os
  7. import webbrowser
  8.  
  9. # Setting constants used
  10. TITLE_FONT = ("Segoe UI", 26)
  11. SUB_TEXT_FONT = ("Segoe UI", 12)
  12. ADMIN_USERNAME = "admin"
  13. ADMIN_PASSWORD = "admin"
  14. POSITIONS = []
  15. with open("config.txt", "r") as config_file_txt:
  16.     csv_reader = csv.reader(config_file_txt)
  17.     for config in csv_reader:
  18.         if config[0] == "positions":
  19.             for index, position_name in enumerate(config):
  20.                 if index != 0:
  21.                     POSITIONS.append(position_name)
  22.  
  23.  
  24. # Function to make PDF file with results
  25. def make_printout(data, controller):
  26.     # Check if 'results.pdf' already exists or is open
  27.     if os.path.isfile("results.pdf"):
  28.         try:
  29.             os.remove("results.pdf")
  30.         except WindowsError:
  31.             tk.messagebox.showerror("Error", "The file 'results.pdf' is already open, please close it.")
  32.             return None
  33.     # Construct PDF
  34.     pdf = FPDF()
  35.     pdf.add_page()
  36.     pdf.set_font("Arial", size=14, style="B")
  37.     pdf.cell(200, 10, txt="Voting Results", ln=1, align="C")
  38.     pdf.image("gsu_logo.png", x=10, y=8, w=35)
  39.     pdf.ln(10)
  40.     for position in data.items():
  41.         candidates = position[1]
  42.         pdf.set_font("Arial", size=14, style="B")
  43.         pdf.cell(100, 10, txt=position[0], ln=1)
  44.         pdf.set_font("Arial", size=11)
  45.         pdf.cell(35, 6, txt="Candidate", align="C")
  46.         pdf.cell(35, 6, txt="1st preference", align="C")
  47.         pdf.cell(35, 6, txt="2nd preference", align="C")
  48.         pdf.cell(35, 6, txt="3rd preference", align="C")
  49.         pdf.cell(35, 6, txt="4th preference", align="C", ln=1)
  50.         candidates_sorted = []
  51.         for candidate in candidates.items():
  52.             row = [candidate[0], candidate[1][0], candidate[1][1], candidate[1][2], candidate[1][3]]
  53.             candidates_sorted.append(row)
  54.         candidates_sorted.sort()
  55.         for candidate in candidates_sorted:
  56.             pdf.cell(35, 6, txt=candidate[0], align="C")
  57.             pdf.cell(35, 6, txt=str(candidate[1]), align="C")
  58.             pdf.cell(35, 6, txt=str(candidate[2]), align="C")
  59.             pdf.cell(35, 6, txt=str(candidate[3]), align="C")
  60.             pdf.cell(35, 6, txt=str(candidate[4]), align="C", ln=1)
  61.         pdf.ln(5)
  62.         candidates = controller.votes.get_winner(position[0])
  63.         pdf.cell(100, 6, txt="Winner: " + candidates[0][4], ln=1)
  64.         total_votes = 0
  65.         for vote in candidates:
  66.             total_votes += vote[0]
  67.         text = "Votes received: " + str(candidates[0][0]) + " ("\
  68.                + str(int(round((candidates[0][0] / total_votes) * 100))) + "%)"
  69.         pdf.cell(100, 6, txt=text, ln=1)
  70.         pdf.cell(100, 6, txt="Total votes: " + str(total_votes), ln=1)
  71.         pdf.ln(7)
  72.  
  73.     pdf.output("results.pdf")
  74.     webbrowser.open_new("results.pdf")
  75.  
  76.  
  77. # Function to make a title label
  78. def title_label(root, text, grid):
  79.     if grid:
  80.         return tk.Label(root, text=text, font=TITLE_FONT) \
  81.             .grid(column=0, row=0, columnspan=2, sticky=tk.W, padx=40, pady=10)
  82.     return tk.Label(root, text=text, font=TITLE_FONT)
  83.  
  84.  
  85. # Function to make normal text
  86. def sub_text(root, text):
  87.     return tk.Label(root, text=text, font=SUB_TEXT_FONT)
  88.  
  89.  
  90. class User:
  91.     def __init__(self, login_id, first_name, last_name):
  92.         # Basic details about user
  93.         self.login_id = login_id
  94.         self.first_name = first_name
  95.         self.last_name = last_name
  96.         self.preferences_voted = {}
  97.         # self.preferences_voted = {
  98.         #     "President": self.is_pref_full("President"),
  99.         #     "GSU Officer": self.is_pref_full("GSU Officer"),
  100.         #     "Faculty Officer": self.is_pref_full("Faculty Officer")
  101.         # }
  102.         for position in POSITIONS:
  103.             self.preferences_voted[position] = self.is_pref_full(position)
  104.         self.can_vote = self.votes_available()
  105.         self.position_chosen = ""
  106.  
  107.     def votes_available(self):
  108.         # Check if all positions have been fully voted for
  109.         if not self.preferences_voted["President"][0] and not self.preferences_voted["GSU Officer"][0] \
  110.                 and not self.preferences_voted["Faculty Officer"][0]:
  111.             return False
  112.         return True
  113.  
  114.     def is_pref_full(self, position):
  115.         preferences_chosen = []
  116.         with open("Votes.txt", "r") as votes_file:
  117.             reader = csv.reader(votes_file)
  118.             for line in reader:
  119.                 # Look for votes for user and position
  120.                 if line[2] == self.login_id and line[5] == position:
  121.                     preferences_chosen.append([line[6], line[3], line[4]])
  122.         # If 4 votes exists, change can_vote
  123.         if len(preferences_chosen) == 4:
  124.             can_vote = False
  125.             btn_state = "disabled"
  126.         else:
  127.             can_vote = True
  128.             btn_state = "normal"
  129.  
  130.         return [can_vote, preferences_chosen, btn_state]
  131.  
  132.  
  133. class Application(tk.Tk):
  134.     def __init__(self, *args, **kwargs):
  135.         self.votes = Votes()
  136.         self.current_user = User("", "", "")
  137.         tk.Tk.__init__(self, *args, **kwargs)
  138.         self.title("Voting System")
  139.         self.position_voting = ""
  140.         self.position_admining = ""
  141.         self.position_viewing = ""
  142.         container = tk.Frame(self)
  143.         container.pack(expand=True, side="top", fill="both")
  144.         container.grid_rowconfigure(0, weight=1)
  145.         container.grid_columnconfigure(0, weight=1)
  146.         self.frames = {}
  147.         # Place all frames inside the main tkinter root
  148.         for x in (LoginFrame, ChoosePositionFrame, CheckDate, VoteFrame, VoteFrame1, VoteFrame2,
  149.                   ManagementInterfaceFrame, ChooseDateFrame, EditPositionsFrame, ViewResultsFrame, ChooseDetailsFrame,
  150.                   ViewDetailsFrame):
  151.             frame = x(container, self)
  152.             self.frames[x] = frame
  153.             frame.grid(column=0, row=0, sticky=tk.NSEW)
  154.         # Show first frame
  155.         self.show_frame(LoginFrame)
  156.  
  157.     def show_frame(self, name):
  158.         frame = self.frames[name]
  159.         # Check for frame name to see if function needs to be called before showing frame
  160.         if name == ChoosePositionFrame:
  161.             self.geometry("400x610")
  162.             frame.make_title(self)
  163.         elif name == CheckDate:
  164.             if frame.in_date:
  165.                 self.geometry("400x610")
  166.                 frame = self.frames[ChoosePositionFrame]
  167.                 frame.make_title(self)
  168.             else:
  169.                 self.geometry("400x190")
  170.         elif name == VoteFrame:
  171.             frame = self.frames[frame.on_open(self)]
  172.             self.geometry("400x350")
  173.             frame.make_ui(self, self.current_user.position_chosen)
  174.         elif name == ViewDetailsFrame:
  175.             frame.on_open()
  176.         elif name == LoginFrame:
  177.             self.geometry("400x230")
  178.         elif name == ViewResultsFrame:
  179.             self.geometry("400x350")
  180.         elif name == ChooseDetailsFrame:
  181.             self.geometry("400x570")
  182.         elif name == ManagementInterfaceFrame:
  183.             self.geometry("400x600")
  184.         elif name == EditPositionsFrame:
  185.             self.geometry("400x370")
  186.         elif name == ChooseDateFrame:
  187.             self.geometry("400x200")
  188.         frame.tkraise()
  189.  
  190.  
  191. class LoginFrame(tk.Frame):
  192.     def __init__(self, parent, controller):
  193.         # Create UI
  194.         tk.Frame.__init__(self, parent)
  195.         title_label(self, "Login", True)
  196.  
  197.         sub_text(self, "Login ID:").grid(padx=42, row=1, column=0, sticky=tk.W)
  198.         self.login_id_entry = tk.Entry(self, width=32)
  199.         self.login_id_entry.grid(row=1, column=1, sticky=tk.E)
  200.  
  201.         sub_text(self, "Password:").grid(padx=42, pady=10, row=2, column=0, sticky=tk.W)
  202.         self.password_entry = tk.Entry(self, width=32, show="*")
  203.         self.password_entry.grid(row=2, column=1, sticky=tk.E)
  204.  
  205.         tk.Button(self, text="Submit", command=lambda: self.check_password(controller), height=2, width=10) \
  206.             .grid(pady=10, row=3, column=1, sticky=tk.E)
  207.         tk.Button(self, text="Exit", command=controller.destroy, width=10, height=2) \
  208.             .grid(pady=10, row=3, column=0, sticky=tk.W, padx=(42, 0))
  209.  
  210.     def check_password(self, controller):
  211.         # Check if admin details is entered
  212.         if self.login_id_entry.get() == ADMIN_USERNAME and self.password_entry.get() == ADMIN_PASSWORD:
  213.             self.password_entry.delete(0, "end")
  214.             self.login_id_entry.delete(0, "end")
  215.             controller.show_frame(ManagementInterfaceFrame)
  216.             return None
  217.         # Check for details in 'StudentVoters.txt'
  218.         with open("StudentVoters.txt", "r", newline="") as student_voters:
  219.             reader = csv.reader(student_voters)
  220.             if not self.login_id_entry.get() in [item[0] for item in reader]:
  221.                 tk.messagebox.showerror("Incorrect details", "Your user ID was not found.")
  222.                 return None
  223.             # Reset file reading position
  224.             student_voters.seek(0)
  225.             for user in reader:
  226.                 if user[0] == self.login_id_entry.get() and user[3] == self.password_entry.get():
  227.                     controller.current_user = User(user[0], user[1], user[2])
  228.                     self.password_entry.delete(0, "end")
  229.                     self.login_id_entry.delete(0, "end")
  230.                     controller.show_frame(CheckDate)
  231.                     return None
  232.         tk.messagebox.showerror("Incorrect details", "The login ID or password you entered is incorrect.")
  233.  
  234.  
  235. class CheckDate(tk.Frame):
  236.     def __init__(self, parent, controller):
  237.         tk.Frame.__init__(self, parent)
  238.         # Set in_date attribute, which is checked when controller.show_frame is called from any class
  239.         self.in_date = True
  240.         # Read config file
  241.         with open("config.txt", "r") as config_file:
  242.             reader = csv.reader(config_file)
  243.             for line in reader:
  244.                 if line[0] == "start_date":
  245.                     self.start_date = datetime.datetime.strptime(line[1], "%d-%m-%Y %H:%M")
  246.                 elif line[0] == "end_date":
  247.                     self.end_date = datetime.datetime.strptime(line[1], "%d-%m-%Y %H:%M")
  248.         # Compare current date to dates set in config
  249.         self.current_date = datetime.datetime.now()
  250.         if self.end_date < self.current_date:
  251.             title_label(self, "Voting session ended", True)
  252.             tk.Label(self, text="Voting ended on " + self.end_date.strftime("%d/%m/%Y") + ".", width=32, anchor=tk.W,
  253.                      font=SUB_TEXT_FONT) \
  254.                 .grid(padx=42, row=1, column=0, sticky=tk.W)
  255.             self.in_date = False
  256.             tk.Button(self, text="View results", command=lambda: controller.show_frame(ViewResultsFrame), height=2,
  257.                       width=10).grid(pady=20, row=2, column=0, sticky=tk.E, padx=42)
  258.         elif self.start_date >= self.current_date:
  259.             title_label(self, "Voting session\nnot started yet", True)
  260.             tk.Label(self, text="Voting will start from " + self.start_date.strftime("%d/%m/%Y") + ".", width=29,
  261.                      anchor=tk.W, font=SUB_TEXT_FONT) \
  262.                 .grid(padx=42, row=1, column=0)
  263.             self.in_date = False
  264.  
  265.         tk.Button(self, text="Back", command=lambda: controller.show_frame(LoginFrame), height=2, width=10) \
  266.             .grid(pady=20, row=2, column=0, sticky=tk.W, padx=42)
  267.  
  268.  
  269. class EditPositionsFrame(tk.Frame):
  270.     def __init__(self, parent, controller):
  271.         tk.Frame.__init__(self, parent)
  272.         title_label(self, "Edit candidates", True)
  273.         tk.Label(self, text="Enter 4 new candidates:", width=34, anchor=tk.W, font=SUB_TEXT_FONT) \
  274.             .grid(row=1, column=0, sticky=tk.W, padx=(42, 0))
  275.         tk.Label(self, text="WARNING: This will erase all existing candidates", fg="red") \
  276.             .grid(row=2, column=0, padx=(42, 0), sticky=tk.W)
  277.         # Generate 4 entry boxes for candidates' names
  278.         self.entries = []
  279.         for i in range(4):
  280.             self.entries.append(tk.Entry(self, width=30))
  281.             self.entries[i].grid(row=i + 3, column=0, sticky=tk.W, padx=(42, 0), pady=10)
  282.         tk.Button(self, text="Back", width=10, height=2,
  283.                   command=lambda: controller.show_frame(ManagementInterfaceFrame))\
  284.             .grid(row=7, column=0, sticky=tk.W, pady=10, padx=(42, 0))
  285.         tk.Button(self, text="Submit", width=10, height=2, command=lambda: self.submit_candidates(controller))\
  286.             .grid(row=7, column=0, sticky=tk.E, pady=10)
  287.  
  288.     def submit_candidates(self, controller):
  289.         new_candidates = []
  290.         position = controller.position_admining
  291.         positions_entered = []
  292.         # Check input data and extract information
  293.         for entry in self.entries:
  294.             positions_entered.append(entry.get())
  295.             if entry.get().strip() == "":
  296.                 tk.messagebox.showerror("Error", "Please enter a value in all 4 entries")
  297.                 return None
  298.             if len(entry.get().strip().split()) != 2:
  299.                 tk.messagebox.showerror("Error", "Please enter two names in each field")
  300.                 return None
  301.             first_name = entry.get().strip().split()[0]
  302.             last_name = entry.get().strip().split()[1]
  303.             new_candidates.append([position, first_name, last_name])
  304.         # Check for duplicates in entry boxes
  305.         if len(new_candidates) != len([list(i) for i in set(map(tuple, new_candidates))]):
  306.             tk.messagebox.showerror("Error", "Please ensure there are no duplicates")
  307.             return None
  308.         # Check if candidate already existing for another position
  309.         with open("GSUCandidates.txt", "r") as cand_file:
  310.             reader = csv.reader(cand_file)
  311.             for candidate in reader:
  312.                 if candidate[0] != controller.position_admining:
  313.                     if candidate[1] + " " + candidate[2] in positions_entered:
  314.                         tk.messagebox.showerror("Error", "Candidate already exists for a different position.")
  315.                         return None
  316.                     new_candidates.append(candidate)
  317.         # Write candidates into file
  318.         with open("GSUCandidates.txt", "w", newline="") as cand_file:
  319.             writer = csv.writer(cand_file)
  320.             for candidate in new_candidates:
  321.                 writer.writerow(candidate)
  322.         for entry in self.entries:
  323.             entry.delete(0, "end")
  324.         # Remove votes for position editing
  325.         votes = []
  326.         with open("Votes.txt", "r") as cand_file:
  327.             reader = csv.reader(cand_file)
  328.             for vote in reader:
  329.                 if vote[5] != position:
  330.                     votes.append(vote)
  331.         with open("Votes.txt", "w", newline="") as cand_file:
  332.             writer = csv.writer(cand_file)
  333.             for vote in votes:
  334.                 writer.writerow(vote)
  335.         controller.show_frame(ManagementInterfaceFrame)
  336.  
  337.  
  338. class ManagementInterfaceFrame(tk.Frame):
  339.     def __init__(self, parent, controller):
  340.         tk.Frame.__init__(self, parent)
  341.         title_label(self, "Management", True)
  342.         tk.Label(self, text="Choose a position to edit:", width=35, anchor=tk.W, font=SUB_TEXT_FONT) \
  343.             .grid(row=1, column=0, sticky=tk.W, padx=(42, 0))
  344.         buttons = []
  345.         # Create button for every position
  346.         for x, name in enumerate(POSITIONS):
  347.             buttons.append(tk.Button(self, text=name, height=2, width=23,
  348.                                      command=lambda i=name: self.choose_position(i, controller)))
  349.             buttons[x].grid(row=x + 2, column=0, pady=10, padx=(42, 0))
  350.         tk.Button(self, text="Edit voting dates", width=23, height=2,
  351.                   command=lambda: controller.show_frame(ChooseDateFrame)).grid(row=len(POSITIONS) + 2, column=0,
  352.                                                                                pady=10, padx=(42, 0))
  353.         tk.Button(self, text="Back", width=10, height=2, command=lambda: controller.show_frame(LoginFrame)).grid(
  354.             pady=10, row=len(POSITIONS) + 3, column=0, sticky=tk.W, padx=42)
  355.  
  356.     def choose_position(self, position, controller):
  357.         # Set controller attribute for next class to find
  358.         controller.position_admining = position
  359.         controller.show_frame(EditPositionsFrame)
  360.  
  361.  
  362. class ChooseDateFrame(tk.Frame):
  363.     def __init__(self, parent, controller):
  364.         tk.Frame.__init__(self, parent)
  365.         tk.Label(self, text="Choose dates", width=13, anchor=tk.W, font=TITLE_FONT)\
  366.             .grid(padx=42, row=0, column=0, sticky=tk.W, columnspan=2)
  367.         tk.Label(self, text="Start date:", font=SUB_TEXT_FONT)\
  368.             .grid(row=1, column=0, sticky=tk.W, padx=(42, 0))
  369.         tk.Label(self, text="End date:", font=SUB_TEXT_FONT)\
  370.             .grid(row=2, column=0, sticky=tk.W, padx=(42, 0))
  371.         # Get dates from config file and put values in entries
  372.         with open("config.txt", "r") as config_file:
  373.             reader = csv.reader(config_file)
  374.             for line in reader:
  375.                 if line[0] == "start_date":
  376.                     self.start_date = datetime.datetime.strptime(line[1], "%d-%m-%Y %H:%M")
  377.                     self.start_date = datetime.datetime.strftime(self.start_date, "%d-%m-%Y %H:%M")
  378.                 elif line[0] == "end_date":
  379.                     self.end_date = datetime.datetime.strptime(line[1], "%d-%m-%Y %H:%M")
  380.                     self.end_date = datetime.datetime.strftime(self.end_date, "%d-%m-%Y %H:%M")
  381.         self.start_date_entry = tk.Entry(self)
  382.         self.start_date_entry.insert(0, self.start_date)
  383.         self.start_date_entry.grid(row=1, column=1, sticky=tk.EW, pady=5)
  384.         self.end_date_entry = tk.Entry(self)
  385.         self.end_date_entry.insert(0, self.end_date)
  386.         self.end_date_entry.grid(row=2, column=1, sticky=tk.EW, pady=5)
  387.         tk.Button(self, text="Back", width=10, height=2,
  388.                   command=lambda: controller.show_frame(ManagementInterfaceFrame)) \
  389.             .grid(row=4, column=0, sticky=tk.W, padx=(42, 0), pady=10)
  390.         tk.Button(self, text="Submit", width=10, height=2, command=lambda: self.change_date(controller)) \
  391.             .grid(row=4, column=1, sticky=tk.E, pady=10)
  392.  
  393.     def change_date(self, controller):
  394.         # Check format of entry
  395.         try:
  396.             datetime.datetime.strptime(self.end_date_entry.get(), "%d-%m-%Y %H:%M")
  397.             datetime.datetime.strptime(self.start_date_entry.get(), "%d-%m-%Y %H:%M")
  398.         except ValueError:
  399.             tk.messagebox.showerror("Error",
  400.                                     "Please enter the date and time in the correct format (DD-MM-YYYY HH:  MM)")
  401.             return None
  402.         # Write dates into config file
  403.         with open("config.txt", "w", newline="") as config_file:
  404.             writer = csv.writer(config_file)
  405.             writer.writerow(["start_date", self.start_date_entry.get()])
  406.             writer.writerow(["end_date", self.end_date_entry.get()])
  407.         controller.show_frame(ManagementInterfaceFrame)
  408.  
  409.  
  410. def make_choices(controller, position):
  411.     with open("GSUCandidates.txt", "r") as cand_file:
  412.         reader = csv.reader(cand_file)
  413.         candidates_voted = controller.current_user.preferences_voted[position]
  414.         candidates_voted_names = []
  415.         for candidate in candidates_voted[1]:
  416.             candidates_voted_names.append(candidate[1] + " " + candidate[2])
  417.         candidates_available = []
  418.  
  419.         for candidate in reader:
  420.             if candidate[0] == position:
  421.                 if not str(candidate[1] + " " + candidate[2]) in candidates_voted_names:
  422.                     candidates_available.append([str(candidate[1] + " " + candidate[2]), [candidate[1], candidate[2]]])
  423.  
  424.     return candidates_available
  425.  
  426.  
  427. # Function for first vote
  428. def cast_vote_1(controller, name, preference, position):
  429.     cast_vote(controller, name, preference, position)
  430.     controller.current_user.position_chosen = position
  431.     controller.show_frame(VoteFrame)
  432.  
  433.  
  434. # Function to handle 2nd, 3rd and 4th vote
  435. def cast_vote_2(controller, choice_boxes, position):
  436.     if len([item[0].get() for item in choice_boxes]) != len(set([item[0].get() for item in choice_boxes])):
  437.         tk.messagebox.showerror("Invalid options", "You have selected a candidate more than once.")
  438.         return None
  439.     for i, choice_box in enumerate(choice_boxes):
  440.         if not choice_box[0].get() == "N/A" and not choice_box[0].get()[0] == "(":
  441.             cast_vote(controller, choice_box[0].get(), str(i + 2), position)
  442.     controller.show_frame(ChoosePositionFrame)
  443.  
  444.  
  445. # Function to add individual vote into votes file
  446. def cast_vote(controller, name, preference, position):
  447.     first_name = name.split()[0]
  448.     last_name = name.split()[1]
  449.     with open("Votes.txt", "a", newline="") as vote_file:
  450.         csv.writer(vote_file).writerow([controller.current_user.first_name, controller.current_user.last_name,
  451.                                         controller.current_user.login_id, first_name, last_name, position, preference])
  452.     controller.current_user.__init__(controller.current_user.login_id, controller.current_user.first_name,
  453.                                      controller.current_user.last_name)
  454.  
  455.  
  456. class ChoosePositionFrame(tk.Frame):
  457.     def __init__(self, parent, controller):
  458.         tk.Frame.__init__(self, parent)
  459.         self.make_title(controller)
  460.  
  461.     def make_title(self, controller):
  462.         subtitle = tk.Label(self, text="You have already voted", font=SUB_TEXT_FONT, width=34, anchor=tk.W)
  463.         subtitle.grid(padx=42, row=1, column=0, sticky=tk.W)
  464.         # Destroy title and make new one if title already exists
  465.         if hasattr(self, "title"):
  466.             self.title.destroy()
  467.         self.title = title_label(self, "Hey there " + controller.current_user.first_name + "!", False)
  468.         self.title.grid(column=0, row=0, columnspan=2, sticky=tk.W, padx=40, pady=10)
  469.         # Change text if current_user can_vote is true
  470.         if controller.current_user.can_vote:
  471.             subtitle.config(text="Choose a position to vote for:")
  472.         self.draw_buttons(controller)
  473.  
  474.     def cast_vote(self, position, controller):
  475.         # Change position_chosen to be accessed by other classes
  476.         controller.current_user.position_chosen = position
  477.         controller.show_frame(VoteFrame)
  478.  
  479.     def draw_buttons(self, controller):
  480.         # Make buttons for each position
  481.         buttons = []
  482.         for x, name in enumerate(POSITIONS):
  483.             buttons.append(tk.Button(self, text=name, height=2, width=25,
  484.                                      state=controller.current_user.preferences_voted[name][2],
  485.                                      command=lambda i=name: self.cast_vote(i, controller)))
  486.             buttons[x].grid(row=x + 2, column=0, pady=15)
  487.         tk.Button(self, text="Back", command=lambda: controller.show_frame(LoginFrame), height=2, width=10).grid(
  488.             pady=20, row=len(POSITIONS) + 2, column=0, padx=42, sticky=tk.W)
  489.  
  490.  
  491. # Frame to redirect to the correct frame depending on votes made
  492. class VoteFrame(tk.Frame):
  493.     def __init__(self, parent, controller):
  494.         tk.Frame.__init__(self, parent)
  495.  
  496.     def on_open(self, controller):
  497.         if len(make_choices(controller, controller.current_user.position_chosen)) == 4:
  498.             return VoteFrame1
  499.         else:
  500.             return VoteFrame2
  501.  
  502.  
  503. # Frame for 1st vote
  504. class VoteFrame1(tk.Frame):
  505.     def __init__(self, parent, controller):
  506.         tk.Frame.__init__(self, parent)
  507.         title_label(self, "Vote", True)
  508.         tk.Label(self, text="Choose your first preference:", width=29, anchor=tk.W, font=SUB_TEXT_FONT) \
  509.             .grid(row=1, column=0, padx=42)
  510.         tk.Button(self, text="Back", width=10, height=2, command=lambda: controller.show_frame(ChoosePositionFrame)) \
  511.             .grid(row=3, column=0, sticky=tk.W, pady=10, padx=42)
  512.         self.options = []
  513.         self.default_value = tk.StringVar(self)
  514.  
  515.     def make_ui(self, controller, position):
  516.         self.options = []
  517.         # Get options available
  518.         for candidate in make_choices(controller, position):
  519.             self.options.append(candidate[0])
  520.         # Remove OptionMenu if already exists in class
  521.         if hasattr(self, "choice_box"):
  522.             self.choice_box.destroy()
  523.  
  524.         self.default_value = tk.StringVar(self)
  525.         self.default_value.set(self.options[0])
  526.         self.choice_box = tk.OptionMenu(self, self.default_value, *self.options)
  527.         self.choice_box.grid(column=0, row=2, padx=42, sticky=tk.W, pady=10)
  528.         tk.Button(self, text="Submit", height=2, width=10,
  529.                   command=lambda: cast_vote_1(controller, self.default_value.get(), "1", position)) \
  530.             .grid(pady=10, row=3, column=0, sticky=tk.E)
  531.  
  532.  
  533. # Frame for 2nd, 3rd and 4th vote
  534. class VoteFrame2(tk.Frame):
  535.     def __init__(self, parent, controller):
  536.         tk.Frame.__init__(self, parent)
  537.         title_label(self, "Vote", True)
  538.         tk.Label(self, text="Choose your other preferences:", width=29, anchor=tk.W, font=SUB_TEXT_FONT) \
  539.             .grid(columnspan=2, row=1, column=0, padx=42, sticky=tk.W)
  540.         self.options = ["N/A"]
  541.         self.choice_boxes = []
  542.         self.choice_labels = []
  543.  
  544.     def make_ui(self, controller, position):
  545.         self.options = ["N/A"]
  546.         self.choice_labels = []
  547.         # Get candidates
  548.         for candidate in make_choices(controller, position):
  549.             self.options.append(candidate[0])
  550.         if len(self.choice_boxes) == 3:
  551.             for choice_box in self.choice_boxes:
  552.                 choice_box[1].destroy()
  553.         self.choice_boxes = []
  554.         preferences_voted = []
  555.         for candidate in controller.current_user.preferences_voted[position][1]:
  556.             preferences_voted.append(candidate[0])
  557.         # Create choice boxes
  558.         for x in range(3):
  559.             sub_text(self, "Pref " + str(x + 2) + ":").grid(column=0, row=x + 3, padx=42, sticky=tk.W)
  560.             self.choice_boxes.append([])
  561.             self.choice_boxes[x].append(tk.StringVar(self))
  562.             # Check if preference already voted for
  563.             if str(x + 2) in preferences_voted:
  564.                 self.choice_boxes[x].append(tk.OptionMenu(self, self.choice_boxes[x][0], *self.options))
  565.                 self.choice_boxes[x][1].configure(state="disabled")
  566.                 for i, vote in enumerate(controller.current_user.preferences_voted[position][1]):
  567.                     if vote[0] == str(x + 2):
  568.                         self.choice_boxes[x][0].set(controller.current_user.preferences_voted[position][1][i][1:3])
  569.             else:
  570.                 self.choice_boxes[x].append(tk.OptionMenu(self, self.choice_boxes[x][0], *self.options))
  571.                 self.choice_boxes[x][0].set(self.options[0])
  572.             self.choice_boxes[x][1].grid(column=1, row=x + 3, padx=0, pady=10, sticky=tk.EW)
  573.             self.choice_boxes[x][1].config(width=20)
  574.  
  575.         tk.Button(self, text="Back", width=10, height=2, command=lambda: controller.show_frame(ChoosePositionFrame)) \
  576.             .grid(column=0, row=6, sticky=tk.W, padx=(42, 0), pady=10, columnspan=2)
  577.         tk.Button(self, text="Submit", width=10, height=2,
  578.                   command=lambda: cast_vote_2(controller, self.choice_boxes, controller.current_user.position_chosen)) \
  579.             .grid(column=0, row=6, sticky=tk.E, pady=10, columnspan=2)
  580.  
  581.  
  582. class Votes:
  583.     def __init__(self):
  584.         # self.votes = {
  585.         #     "President": {},
  586.         #     "GSU Officer": {},
  587.         #     "Faculty Officer": {}
  588.         # }
  589.         self.votes = {}
  590.         for position in POSITIONS:
  591.             self.votes[position] = {}
  592.         # Add dictionaries with empty lists inside votes attribute
  593.         with open("GSUCandidates.txt", "r") as cand_file:
  594.             reader = csv.reader(cand_file)
  595.             for candidate in reader:
  596.                 self.votes[candidate[0]][str(candidate[1] + " " + candidate[2])] = [0, 0, 0, 0]
  597.         # Add up votes from votes file
  598.         with open("Votes.txt", "r") as votes_file:
  599.             reader = csv.reader(votes_file)
  600.             for vote in reader:
  601.                 self.votes[vote[5]][str(vote[3] + " " + vote[4])][int(vote[6]) - 1] += 1
  602.  
  603.     def get_winner(self, position):
  604.         # Sort candidates by votes
  605.         votes_formed = []
  606.         for candidate in self.votes[position].items():
  607.             votes_formed.append([candidate[1][0], candidate[1][1], candidate[1][2], candidate[1][3], candidate[0]])
  608.         votes_formed.sort(reverse=True)
  609.         if votes_formed[0] == votes_formed[1]:
  610.             tk.messagebox.showerror("No winner", "Unfortunately, there was no winner for "
  611.                                     + position + ". A re-election is recommended. ")
  612.         return votes_formed
  613.  
  614.  
  615. # Frame with summary statistics of the election
  616. class ViewResultsFrame(tk.Frame):
  617.     def __init__(self, parent, controller):
  618.         tk.Frame.__init__(self, parent)
  619.         tk.Label(self, text="Results", anchor=tk.W, font=TITLE_FONT, width=13).grid(row=0, column=0, sticky=tk.W,
  620.                                                                                     padx=42)
  621.         for i, position in enumerate(POSITIONS):
  622.             winner = controller.votes.get_winner(position)
  623.             text = position + " winner: " + winner[0][4] + "."
  624.             tk.Label(self, text=text, anchor=tk.W).grid(row=i + 1, column=0, sticky=tk.W, pady=3, padx=(42, 0))
  625.         tk.Button(self, text="Back", width=10, height=2, command=lambda: controller.show_frame(CheckDate)) \
  626.             .grid(row=len(POSITIONS) + 1, column=0, sticky=tk.W, padx=(42, 0), pady=10)
  627.         tk.Button(self, text="Details", width=10, height=2, command=lambda: controller.show_frame(ChooseDetailsFrame)) \
  628.             .grid(row=len(POSITIONS) + 1, column=0, sticky=tk.E, pady=10)
  629.         tk.Button(self, text="Print", width=10, height=2,
  630.                   command=lambda: make_printout(controller.votes.votes, controller)) \
  631.             .grid(row=len(POSITIONS) + 2, column=0, sticky=tk.E, pady=5)
  632.  
  633.  
  634. # Frame to show details of each position
  635. class ViewDetailsFrame(tk.Frame):
  636.     def __init__(self, parent, controller):
  637.         tk.Frame.__init__(self, parent)
  638.         self.controller = controller
  639.         tk.Button(self, text="Back", width=10, height=2, command=self.go_back)\
  640.             .grid(row=9, column=0, sticky=tk.W, padx=(42, 0), pady=10)
  641.  
  642.     def on_open(self):
  643.         # Remove title if already made
  644.         if hasattr(self, "title"):
  645.             self.title.destroy()
  646.             for label in self.summary_labels:
  647.                 label.destroy()
  648.         position_votes = self.controller.votes.get_winner(self.controller.position_viewing)
  649.         rows_of_text = [["Candidate", "1st pref", "2nd pref", "3rd pref", "4th pref"]]
  650.         position_votes = sorted(position_votes, key=lambda o: o[4])
  651.         candidates = self.controller.votes.get_winner(self.controller.position_viewing)
  652.         # Create a table using vote data
  653.         labels = []
  654.         for vote in position_votes:
  655.             rows_of_text.append([vote[4], str(vote[0]), str(vote[1]), str(vote[2]), str(vote[3])])
  656.         for x, row in enumerate(rows_of_text):
  657.             for i, col in enumerate(row):
  658.                 if i == 0:
  659.                     labels.append(tk.Label(self, text=col, width=10))
  660.                     labels[5*x+i].grid(padx=(42, 0), pady=5, row=x + 1, column=i)
  661.                 else:
  662.                     labels.append(tk.Label(self, text=col, width=10))
  663.                     labels[5*x+i].grid(pady=5, row=x + 1, column=i)
  664.         # Calculate summary statistics
  665.         total_votes = 0
  666.         for vote in candidates:
  667.             total_votes += vote[0]
  668.         self.summary_labels = []
  669.         self.summary_labels.append(tk.Label(self, text="Winner: " + candidates[0][4], font=SUB_TEXT_FONT))
  670.         self.summary_labels[0].grid(row=6, column=0, columnspan=5, sticky=tk.W, padx=(42, 0), pady=5)
  671.         text = "Votes received: " + str(candidates[0][0]) + " ("\
  672.                + str(int(round((candidates[0][0] / total_votes) * 100))) + "%)"
  673.         self.summary_labels.append(tk.Label(self, text=text, font=SUB_TEXT_FONT))
  674.         self.summary_labels[1].grid(row=7, column=0, columnspan=5, sticky=tk.W, padx=(42, 0), pady=5)
  675.         self.summary_labels.append(tk.Label(self, text="Total votes: " + str(total_votes), font=SUB_TEXT_FONT))
  676.         self.summary_labels[2].grid(row=8, column=0, columnspan=5, sticky=tk.W, padx=(42, 0), pady=5)
  677.         self.title = tk.Label(self, text=self.controller.position_viewing, anchor=tk.W, font=TITLE_FONT, width=13)
  678.         self.title.grid(row=0, column=0, sticky=tk.W, padx=42, columnspan=5)
  679.         # Make window bigger to accompany more data
  680.         self.controller.geometry("460x410")
  681.  
  682.     def go_back(self):
  683.         # Change window size back
  684.         self.controller.geometry("400x410")
  685.         self.controller.show_frame(ChooseDetailsFrame)
  686.  
  687.  
  688. class ChooseDetailsFrame(tk.Frame):
  689.     def __init__(self, parent, controller):
  690.         tk.Frame.__init__(self, parent)
  691.         self.controller = controller
  692.         tk.Label(self, text="View details", anchor=tk.W, font=TITLE_FONT, width=13)\
  693.             .grid(row=0, column=0, sticky=tk.W, padx=42)
  694.         # Create buttons for each position
  695.         buttons = []
  696.         for x, name in enumerate(POSITIONS):
  697.             buttons.append(tk.Button(self, text=name, height=2, width=25,
  698.                                      state=controller.current_user.preferences_voted[name][2],
  699.                                      command=lambda i=name: self.view_details(i)))
  700.             buttons[x].grid(row=x + 1, column=0, pady=15, padx=(42, 0))
  701.         tk.Button(self, text="Back", width=10, height=2, command=lambda: controller.show_frame(ViewResultsFrame))\
  702.             .grid(row=len(POSITIONS) + 1, column=0, sticky=tk.W, padx=(42, 0), pady=10)
  703.  
  704.     def view_details(self, position):
  705.         self.controller.position_viewing = position
  706.         self.controller.show_frame(ViewDetailsFrame)
  707.  
  708.  
  709. if __name__ == "__main__":
  710.     # Create tkinter application
  711.     app = Application()
  712.     app.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement