From 20201ddedf5bda3c1369a6eb8d9288548d64bf2b Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 14:08:04 -0700 Subject: [PATCH 01/14] merge bash scripts and move to root, re add sh extension --- src/run => run_dev.sh | 4 ++-- src/run_verbose | 13 ------------- 2 files changed, 2 insertions(+), 15 deletions(-) rename src/run => run_dev.sh (61%) delete mode 100755 src/run_verbose diff --git a/src/run b/run_dev.sh similarity index 61% rename from src/run rename to run_dev.sh index ab07bed..7d28756 100755 --- a/src/run +++ b/run_dev.sh @@ -1,7 +1,7 @@ #!/bin/bash set -a -source "$(dirname "$0")/../.env" +source "$(dirname "$0")/.env" set +a output_file="log.txt" @@ -10,4 +10,4 @@ echo "" >> "$output_file" date "+%Y-%m-%d %H:%M:%S" >> "$output_file" -python main.py 2>&1 | tee -a log.txt \ No newline at end of file +python src/main.py "$@" 2>&1 | tee -a log.txt diff --git a/src/run_verbose b/src/run_verbose deleted file mode 100755 index e94a274..0000000 --- a/src/run_verbose +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -a -source "$(dirname "$0")/../.env" -set +a - -output_file="log.txt" - -echo "" >> "$output_file" - -date "+%Y-%m-%d %H:%M:%S" >> "$output_file" - -python main.py -v 2>&1 | tee -a log.txt \ No newline at end of file From a15efd0b371ccb926a7af8b11e746a9ff4f1b36d Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 14:15:39 -0700 Subject: [PATCH 02/14] refactor file and class names --- src/core/handle_check_in.py | 12 ++++---- src/gui.py | 30 +++++++++---------- src/main.py | 18 +++++------ .../{AccNoWaiver.py => acc_no_waiver.py} | 0 ...oWaiverSwipe.py => acc_no_waiver_swipe.py} | 2 +- .../{CheckInNoId.py => check_in_no_id.py} | 18 +++++------ src/screens/{MainPage.py => main_page.py} | 6 ++-- src/screens/{ManualFill.py => manual_fill.py} | 4 +-- ...CheckInOnly.py => no_acc_check_in_only.py} | 0 .../{NoAccNoWaiver.py => no_acc_no_waiver.py} | 0 ...iverSwipe.py => no_acc_no_waiver_swipe.py} | 2 +- src/screens/{QRCodes.py => qr_codes.py} | 2 +- src/screens/{UserThank.py => user_thank.py} | 4 +-- .../{UserWelcome.py => user_welcome.py} | 4 +-- .../{WaiverNoAcc.py => waiver_no_acc.py} | 0 ...erNoAccSwipe.py => waiver_no_acc_swipe.py} | 2 +- src/swipe.py | 16 +++++----- src/utils.py | 8 ++--- 18 files changed, 64 insertions(+), 64 deletions(-) rename src/screens/{AccNoWaiver.py => acc_no_waiver.py} (100%) rename src/screens/{AccNoWaiverSwipe.py => acc_no_waiver_swipe.py} (97%) rename src/screens/{CheckInNoId.py => check_in_no_id.py} (87%) rename src/screens/{MainPage.py => main_page.py} (92%) rename src/screens/{ManualFill.py => manual_fill.py} (98%) rename src/screens/{NoAccCheckInOnly.py => no_acc_check_in_only.py} (100%) rename src/screens/{NoAccNoWaiver.py => no_acc_no_waiver.py} (100%) rename src/screens/{NoAccNoWaiverSwipe.py => no_acc_no_waiver_swipe.py} (97%) rename src/screens/{QRCodes.py => qr_codes.py} (96%) rename src/screens/{UserThank.py => user_thank.py} (93%) rename src/screens/{UserWelcome.py => user_welcome.py} (94%) rename src/screens/{WaiverNoAcc.py => waiver_no_acc.py} (100%) rename src/screens/{WaiverNoAccSwipe.py => waiver_no_acc_swipe.py} (97%) diff --git a/src/core/handle_check_in.py b/src/core/handle_check_in.py index 9d686cb..8d5f964 100644 --- a/src/core/handle_check_in.py +++ b/src/core/handle_check_in.py @@ -9,12 +9,12 @@ def handle_check_in(tag): status = result.get("status") def update_ui(): - from screens.MainPage import MainPage - from screens.NoAccNoWaiver import NoAccNoWaiver - from screens.NoAccNoWaiverSwipe import NoAccNoWaiverSwipe - from screens.AccNoWaiver import AccNoWaiver - from screens.AccNoWaiverSwipe import AccNoWaiverSwipe - from screens.UserWelcome import UserWelcome + from screens.main_page import MainPage + from screens.no_acc_no_waiver import NoAccNoWaiver + from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe + from screens.acc_no_waiver import AccNoWaiver + from screens.acc_no_waiver_swipe import AccNoWaiverSwipe + from screens.user_welcome import UserWelcome if status == "api_error": logging.error("API error during check-in") diff --git a/src/gui.py b/src/gui.py index 6b4f3e4..99f0ce7 100644 --- a/src/gui.py +++ b/src/gui.py @@ -9,7 +9,7 @@ ################################################# # Acts as the controller and the user interface # ################################################# -class gui(tk.Tk): +class Gui(tk.Tk): def __init__(self, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) @@ -42,19 +42,19 @@ def __init__(self, *args, **kwargs): self.curr_frame = None self.frame_uuid = uuid.uuid4().hex - from screens.MainPage import MainPage - from screens.AccNoWaiver import AccNoWaiver - from screens.AccNoWaiverSwipe import AccNoWaiverSwipe - from screens.ManualFill import ManualFill - from screens.CheckInNoId import CheckInNoId - from screens.NoAccCheckInOnly import NoAccCheckInOnly - from screens.NoAccNoWaiver import NoAccNoWaiver - from screens.NoAccNoWaiverSwipe import NoAccNoWaiverSwipe - from screens.QRCodes import QRCodes - from screens.UserThank import UserThank - from screens.UserWelcome import UserWelcome - from screens.WaiverNoAcc import WaiverNoAcc - from screens.WaiverNoAccSwipe import WaiverNoAccSwipe + from screens.main_page import MainPage + from screens.acc_no_waiver import AccNoWaiver + from screens.acc_no_waiver_swipe import AccNoWaiverSwipe + from screens.manual_fill import ManualFill + from screens.check_in_no_id import CheckInNoId + from screens.no_acc_check_in_only import NoAccCheckInOnly + from screens.no_acc_no_waiver import NoAccNoWaiver + from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe + from screens.qr_codes import QRCodes + from screens.user_thank import UserThank + from screens.user_welcome import UserWelcome + from screens.waiver_no_acc import WaiverNoAcc + from screens.waiver_no_acc_swipe import WaiverNoAccSwipe self._timeouts = { AccNoWaiverSwipe: 30000, @@ -86,7 +86,7 @@ def _on_map(self, event): self.attributes("-fullscreen", True) def timeout_fn(self, curr_uuid): - from screens.MainPage import MainPage + from screens.main_page import MainPage if curr_uuid == self.frame_uuid: self.show_frame(MainPage) global_.traffic_light.set_off() diff --git a/src/main.py b/src/main.py index c8e6c85..766f4b1 100644 --- a/src/main.py +++ b/src/main.py @@ -1,13 +1,13 @@ from tkinter import Label -from gui import gui +from gui import Gui from reader import * -from swipe import swipe +from swipe import Swipe from sheets import * from threading import Thread -from screens.MainPage import MainPage -from screens.ManualFill import ManualFill -from screens.CheckInNoId import CheckInNoId -from utils import utils +from screens.main_page import MainPage +from screens.manual_fill import ManualFill +from screens.check_in_no_id import CheckInNoId +from utils import Utils from core.handle_check_in import handle_check_in from core.render_ports import get_usb_ids import global_ @@ -149,13 +149,13 @@ def clearAndReturn(): reader_usb_id, traffic_usb_id = get_usb_ids() check_api_health() global_.init(traffic_usb_id) - app = gui() + app = Gui() global_.setApp(app) global_.traffic_light.set_off() - sw = swipe() + sw = Swipe() reader = Reader(reader_usb_id) - util = utils() + util = Utils() thread = Thread(target=myLoop, args=(app, reader)) logging.info("Starting thread") thread.start() diff --git a/src/screens/AccNoWaiver.py b/src/screens/acc_no_waiver.py similarity index 100% rename from src/screens/AccNoWaiver.py rename to src/screens/acc_no_waiver.py diff --git a/src/screens/AccNoWaiverSwipe.py b/src/screens/acc_no_waiver_swipe.py similarity index 97% rename from src/screens/AccNoWaiverSwipe.py rename to src/screens/acc_no_waiver_swipe.py index ea877c3..33ff24c 100644 --- a/src/screens/AccNoWaiverSwipe.py +++ b/src/screens/acc_no_waiver_swipe.py @@ -49,6 +49,6 @@ def _build(self, controller): self._window(875.0, 581.0, btn, width=344, height=71) def _back_to_main(self): - from .MainPage import MainPage + from .main_page import MainPage global_.traffic_light.set_off() global_.app.show_frame(MainPage) diff --git a/src/screens/CheckInNoId.py b/src/screens/check_in_no_id.py similarity index 87% rename from src/screens/CheckInNoId.py rename to src/screens/check_in_no_id.py index 1cd5d26..daa9312 100644 --- a/src/screens/CheckInNoId.py +++ b/src/screens/check_in_no_id.py @@ -13,10 +13,10 @@ class CheckInNoId(Screen): def _build(self, controller): - from .NoAccCheckInOnly import NoAccCheckInOnly - from .NoAccNoWaiverSwipe import NoAccNoWaiverSwipe - from .UserWelcome import UserWelcome - from .AccNoWaiver import AccNoWaiver + from .no_acc_check_in_only import NoAccCheckInOnly + from .no_acc_no_waiver_swipe import NoAccNoWaiverSwipe + from .user_welcome import UserWelcome + from .acc_no_waiver import AccNoWaiver self.loading_text_id = None self.pid = StringVar() @@ -63,11 +63,11 @@ def updateEntries(self, pid): self.pid_entry.insert(0, pid) def _call_check_in(self, controller): - from .NoAccCheckInOnly import NoAccCheckInOnly - from .NoAccNoWaiverSwipe import NoAccNoWaiverSwipe - from .UserWelcome import UserWelcome - from .AccNoWaiver import AccNoWaiver - from .MainPage import MainPage + from .no_acc_check_in_only import NoAccCheckInOnly + from .no_acc_no_waiver_swipe import NoAccNoWaiverSwipe + from .user_welcome import UserWelcome + from .acc_no_waiver import AccNoWaiver + from .main_page import MainPage pid = self.pid_entry.get() if not pid: diff --git a/src/screens/MainPage.py b/src/screens/main_page.py similarity index 92% rename from src/screens/MainPage.py rename to src/screens/main_page.py index c05e873..b22740e 100644 --- a/src/screens/MainPage.py +++ b/src/screens/main_page.py @@ -8,8 +8,8 @@ class MainPage(Screen): def _build(self, controller): - from .QRCodes import QRCodes - from .CheckInNoId import CheckInNoId + from .qr_codes import QRCodes + from .check_in_no_id import CheckInNoId logo = self._photo(ASSETS_PATH / "image_3.png") self._image(88.0, 90.0, image=logo) @@ -48,7 +48,7 @@ def _build(self, controller): self._window(1130.0, 40.0, btn2) def _go_to_no_id(self, controller): - from .CheckInNoId import CheckInNoId + from .check_in_no_id import CheckInNoId no_id = global_.app.get_frame(CheckInNoId) no_id.clearEntries() controller.show_frame(CheckInNoId) diff --git a/src/screens/ManualFill.py b/src/screens/manual_fill.py similarity index 98% rename from src/screens/ManualFill.py rename to src/screens/manual_fill.py index 44e9f54..791265c 100644 --- a/src/screens/ManualFill.py +++ b/src/screens/manual_fill.py @@ -1,7 +1,7 @@ from pathlib import Path from tkinter import Button, Entry, StringVar, END from .screen import Screen -from utils import utils +from utils import Utils import logging import timeit @@ -109,7 +109,7 @@ def updateEntries(self, fname, lname, email, pid): self.pid_entry.insert(0, pid) def _call_account_creation(self): - util = utils() + util = Utils() data = self.getEntries() self.clearEntries() try: diff --git a/src/screens/NoAccCheckInOnly.py b/src/screens/no_acc_check_in_only.py similarity index 100% rename from src/screens/NoAccCheckInOnly.py rename to src/screens/no_acc_check_in_only.py diff --git a/src/screens/NoAccNoWaiver.py b/src/screens/no_acc_no_waiver.py similarity index 100% rename from src/screens/NoAccNoWaiver.py rename to src/screens/no_acc_no_waiver.py diff --git a/src/screens/NoAccNoWaiverSwipe.py b/src/screens/no_acc_no_waiver_swipe.py similarity index 97% rename from src/screens/NoAccNoWaiverSwipe.py rename to src/screens/no_acc_no_waiver_swipe.py index 57c86c2..3295962 100644 --- a/src/screens/NoAccNoWaiverSwipe.py +++ b/src/screens/no_acc_no_waiver_swipe.py @@ -43,6 +43,6 @@ def _build(self, controller): self._window(465.0, 554.0, btn, width=349, height=71) def _go_to_manual_fill(self, controller): - from .ManualFill import ManualFill + from .manual_fill import ManualFill global_.app.get_frame(ManualFill).clearEntries() controller.show_frame(ManualFill) diff --git a/src/screens/QRCodes.py b/src/screens/qr_codes.py similarity index 96% rename from src/screens/QRCodes.py rename to src/screens/qr_codes.py index e05205d..e9cd16c 100644 --- a/src/screens/QRCodes.py +++ b/src/screens/qr_codes.py @@ -7,7 +7,7 @@ class QRCodes(Screen): def _build(self, controller): - from .MainPage import MainPage + from .main_page import MainPage img3 = self._photo(ASSETS_PATH / "image_3.png") self._image(88.0, 90.0, image=img3) diff --git a/src/screens/UserThank.py b/src/screens/user_thank.py similarity index 93% rename from src/screens/UserThank.py rename to src/screens/user_thank.py index e5ff541..db5afa2 100644 --- a/src/screens/UserThank.py +++ b/src/screens/user_thank.py @@ -20,7 +20,7 @@ def hide(self): self.canvas.delete("thank") def displayName(self, name, nextPage): - from .MainPage import MainPage + from .main_page import MainPage global_.app.show_frame(UserThank) if nextPage == MainPage: @@ -40,7 +40,7 @@ def displayName(self, name, nextPage): global_.app.after(4000, lambda: self._go_to_next(nextPage)) def _go_to_next(self, nextPage): - from .MainPage import MainPage + from .main_page import MainPage global_.app.show_frame(nextPage) if nextPage == MainPage: global_.traffic_light.set_off() diff --git a/src/screens/UserWelcome.py b/src/screens/user_welcome.py similarity index 94% rename from src/screens/UserWelcome.py rename to src/screens/user_welcome.py index 387463f..0302d74 100644 --- a/src/screens/UserWelcome.py +++ b/src/screens/user_welcome.py @@ -26,7 +26,7 @@ def displayName(self, name): self.last_name = name - from .MainPage import MainPage + from .main_page import MainPage global_.app.show_frame(UserWelcome) text_id = self.canvas.create_text( @@ -43,7 +43,7 @@ def displayName(self, name): self.canvas.after(3000, lambda: self._remove_name(text_id)) def _remove_name(self, text_id): - from .MainPage import MainPage + from .main_page import MainPage self.canvas.delete(text_id) self.offset -= 73 diff --git a/src/screens/WaiverNoAcc.py b/src/screens/waiver_no_acc.py similarity index 100% rename from src/screens/WaiverNoAcc.py rename to src/screens/waiver_no_acc.py diff --git a/src/screens/WaiverNoAccSwipe.py b/src/screens/waiver_no_acc_swipe.py similarity index 97% rename from src/screens/WaiverNoAccSwipe.py rename to src/screens/waiver_no_acc_swipe.py index 8c841c3..d988575 100644 --- a/src/screens/WaiverNoAccSwipe.py +++ b/src/screens/waiver_no_acc_swipe.py @@ -43,6 +43,6 @@ def _build(self, controller): self._window(465.0, 554.0, btn, width=349, height=71) def _go_to_manual_fill(self, controller): - from .ManualFill import ManualFill + from .manual_fill import ManualFill global_.app.get_frame(ManualFill).clearEntries() controller.show_frame(ManualFill) diff --git a/src/swipe.py b/src/swipe.py index a212fc0..1326a05 100644 --- a/src/swipe.py +++ b/src/swipe.py @@ -1,11 +1,11 @@ import tkinter -from screens.ManualFill import ManualFill -from screens.NoAccNoWaiverSwipe import NoAccNoWaiverSwipe -from screens.WaiverNoAccSwipe import WaiverNoAccSwipe -from screens.AccNoWaiverSwipe import AccNoWaiverSwipe -from screens.CheckInNoId import CheckInNoId +from screens.manual_fill import ManualFill +from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe +from screens.waiver_no_acc_swipe import WaiverNoAccSwipe +from screens.acc_no_waiver_swipe import AccNoWaiverSwipe +from screens.check_in_no_id import CheckInNoId from get_info_from_pid import contact_client -from utils import utils +from utils import Utils import global_ import logging @@ -16,13 +16,13 @@ swipe_error_shown = False -class swipe: +class Swipe: def __init__(self): global id_string id_string = "" def keyboardPress(self, key): - util = utils() + util = Utils() global id_string, swipe_error_shown curr_frame = global_.app.get_curr_frame() diff --git a/src/utils.py b/src/utils.py index 4f2fa0a..a8201ff 100644 --- a/src/utils.py +++ b/src/utils.py @@ -2,9 +2,9 @@ import time import global_ import tkinter -from screens.MainPage import MainPage -from screens.AccNoWaiverSwipe import AccNoWaiverSwipe -from screens.UserThank import UserThank +from screens.main_page import MainPage +from screens.acc_no_waiver_swipe import AccNoWaiverSwipe +from screens.user_thank import UserThank import logging ###################################################### @@ -12,7 +12,7 @@ ###################################################### -class utils: +class Utils: def __init__(self) -> None: pass From 3d750edf2aa3672603289c56089989cda0efc843 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 14:21:56 -0700 Subject: [PATCH 03/14] remove unnecessary wrapper calls for globals --- src/global_.py | 17 ++++++----------- src/main.py | 4 ++-- src/main_checkin_only.py | 4 ++-- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/global_.py b/src/global_.py index 1f9e8c3..88982e6 100644 --- a/src/global_.py +++ b/src/global_.py @@ -2,6 +2,11 @@ from sheets import SheetManager from traffic import TrafficLight +rfid = "" +sheets = None +app = None +traffic_light = None + class _TrafficProxy: def __init__(self, light, sheets_mgr): @@ -33,16 +38,6 @@ def set_off(self): def init(traffic_usb_id=None): - global rfid, sheets, app, traffic_light + global sheets, traffic_light sheets = SheetManager() traffic_light = _TrafficProxy(TrafficLight(traffic_usb_id), sheets) - - -def setRFID(new_rfid): - global rfid - rfid = new_rfid - - -def setApp(new_app): - global app - app = new_app diff --git a/src/main.py b/src/main.py index 766f4b1..269c722 100644 --- a/src/main.py +++ b/src/main.py @@ -90,7 +90,7 @@ def myLoop(app, reader): else: logging.debug("RFID Check Succeeded") - global_.setRFID(tag) + global_.rfid = tag handle_check_in(tag) last_tag = tag @@ -150,7 +150,7 @@ def clearAndReturn(): check_api_health() global_.init(traffic_usb_id) app = Gui() - global_.setApp(app) + global_.app = app global_.traffic_light.set_off() sw = Swipe() diff --git a/src/main_checkin_only.py b/src/main_checkin_only.py index 3456ce7..2bfe6c3 100644 --- a/src/main_checkin_only.py +++ b/src/main_checkin_only.py @@ -82,7 +82,7 @@ def myLoop(app, reader): else: logging.debug("RFID Check Succeeded") - global_.setRFID(tag) + global_.rfid = tag handle_check_in(tag, contact, util) last_tag = tag @@ -127,7 +127,7 @@ def clearAndReturn(): check_api_health() global_.init(traffic_usb_id) app = gui() - global_.setApp(app) + global_.app = app reader = Reader() util = utils() From 21dc827c905a3b5f635aba6c8f43f1fb8114d618 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 14:39:33 -0700 Subject: [PATCH 04/14] remove legacy file --- src/main_checkin_only.py | 139 --------------------------------------- 1 file changed, 139 deletions(-) delete mode 100644 src/main_checkin_only.py diff --git a/src/main_checkin_only.py b/src/main_checkin_only.py deleted file mode 100644 index 2bfe6c3..0000000 --- a/src/main_checkin_only.py +++ /dev/null @@ -1,139 +0,0 @@ -from datetime import datetime as dt -from core.handle_check_in import handle_check_in -from core.render_ports import get_usb_ids -from gui import * -from reader import * -import json -from sheets import * -from threading import Thread -from UserWelcome import * -from ManualFill import * -from CheckInNoId import * -from swipe import * -from tkinter import * -import global_ -import socket -import logging -import argparse -from sheets import check_api_health - - -def is_connected(host="8.8.8.8", port=53, timeout=3): - """ - Host: 8.8.8.8 (google-public-dns-a.google.com) - OpenPort: 53/tcp - Service: domain (DNS/TCP) - """ - try: - socket.setdefaulttimeout(timeout) - socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port)) - return True - except socket.error as ex: - return False - - -############################################################## -# This acts as the main loop of the program, ran in a thread # -############################################################## - -no_wifi_shown = False - - -def myLoop(app, reader): - global no_wifi_shown, no_wifi - logging.info("Now reading ID cards") - last_tag = 0 - last_time = 0 - while True: - time.sleep(0.1) - in_waiting = reader.getSerInWaiting() - tag = 0 - - if in_waiting >= 14: - if not is_connected(): - logging.info("ERROR wifi is not connected") - if not no_wifi_shown: - no_wifi_shown = True - no_wifi = Label( - app.get_frame(MainPage), - text="ERROR! Connection cannot be established, please let staff know.", - font=("Arial", 25), - ) - no_wifi.pack(pady=40) - no_wifi.after(4000, lambda: destroyNoWifiError(no_wifi)) - continue - - tag_read_start = perf_counter() - tag = reader.grabRFID() - tag_read_end = perf_counter() - - if " " in tag: - continue - - if tag == last_tag and not reader.canScanAgain(last_time): - logging.debug("Suppressing repeat scan") - continue - - s_reason = reader.checkRFID(tag) - - if s_reason != "good": - logging.debug(s_reason) - continue - else: - logging.debug("RFID Check Succeeded") - - global_.rfid = tag - handle_check_in(tag, contact, util) - - last_tag = tag - last_time = time.time() - - reader.readSerial() - -def destroyNoWifiError(no_wifi): - global no_wifi_shown - no_wifi.destroy() - no_wifi_shown = False - - -def clearAndReturn(): - global_.app.show_frame(MainPage) - global_.app.get_frame(ManualFill).clearEntries() - global_.app.get_frame(CheckInNoId).clearEntries() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Makerspace Check-in System", - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - ) - - parser.add_argument( - "-v", - "--verbose", - action="store_true", - help="Increase verbosity (print debug info)", - ) - - args = parser.parse_args() - config = vars(args) - - if config["verbose"]: - logging.basicConfig(level=logging.DEBUG) - else: - logging.basicConfig(level=logging.INFO) - - reader_usb_id, traffic_usb_id = get_usb_ids() - check_api_health() - global_.init(traffic_usb_id) - app = gui() - global_.app = app - - reader = Reader() - util = utils() - thread = Thread(target=myLoop, args=(app, reader)) - logging.info("Starting thread") - thread.start() - app.bind("", lambda i: clearAndReturn()) - logging.info("Made it to app start") - app.start() From b232762557135bcb6967aad12c69e0b7bccdb5a9 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 14:45:32 -0700 Subject: [PATCH 05/14] switch from global vars to a shared app context --- src/app_context.py | 59 +++++++++++++++++++++++++++ src/core/handle_check_in.py | 27 ++++++------ src/global_.py | 43 ------------------- src/gui.py | 7 ++-- src/main.py | 51 ++++++++++++----------- src/screens/acc_no_waiver_swipe.py | 6 +-- src/screens/check_in_no_id.py | 7 ++-- src/screens/main_page.py | 4 +- src/screens/manual_fill.py | 2 +- src/screens/no_acc_no_waiver_swipe.py | 4 +- src/screens/user_thank.py | 13 +++--- src/screens/user_welcome.py | 7 ++-- src/screens/waiver_no_acc_swipe.py | 4 +- src/swipe.py | 36 +++++----------- src/utils.py | 17 ++++---- 15 files changed, 138 insertions(+), 149 deletions(-) create mode 100644 src/app_context.py delete mode 100644 src/global_.py diff --git a/src/app_context.py b/src/app_context.py new file mode 100644 index 0000000..8ad57fa --- /dev/null +++ b/src/app_context.py @@ -0,0 +1,59 @@ +import threading + +from sheets import SheetManager +from traffic import TrafficLight + + +class _TrafficProxy: + def __init__(self, light: TrafficLight, sheets: SheetManager): + self._light = light + self._sheets = sheets + + @property + def connected(self) -> bool: + return self._light.ser is not None + + def _post(self, color: str) -> None: + threading.Thread( + target=self._sheets.set_traffic_light, + args=(color,), + daemon=True, + ).start() + + def set_red(self) -> None: + self._post("red") + + def set_green(self) -> None: + self._post("green") + + def set_yellow(self) -> None: + self._post("yellow") + + def set_off(self) -> None: + self._post("off") + + +class AppContext: + def __init__(self, sheets: SheetManager, traffic_light: _TrafficProxy): + self.sheets = sheets + self.traffic_light = traffic_light + self.app = None # set after Gui is created + self._rfid_lock = threading.Lock() + self._rfid: str = "" + + @property + def rfid(self) -> str: + with self._rfid_lock: + return self._rfid + + @rfid.setter + def rfid(self, value: str) -> None: + with self._rfid_lock: + self._rfid = value + + @classmethod + def create(cls, traffic_usb_id=None) -> "AppContext": + sheets = SheetManager() + light = TrafficLight(traffic_usb_id) + proxy = _TrafficProxy(light, sheets) + return cls(sheets, proxy) diff --git a/src/core/handle_check_in.py b/src/core/handle_check_in.py index 8d5f964..8fc70a7 100644 --- a/src/core/handle_check_in.py +++ b/src/core/handle_check_in.py @@ -1,11 +1,10 @@ -import global_ import logging from tkinter import Label from core.write_checkin import write_checkin -def handle_check_in(tag): - result = global_.sheets.checkin_by_uuid(tag) +def handle_check_in(ctx, tag): + result = ctx.sheets.checkin_by_uuid(tag) status = result.get("status") def update_ui(): @@ -18,9 +17,9 @@ def update_ui(): if status == "api_error": logging.error("API error during check-in") - global_.traffic_light.set_red() + ctx.traffic_light.set_red() error_label = Label( - global_.app.canvas, + ctx.app.canvas, text="System error, please let staff know.", bg="#153246", fg="white", font=("Arial", 25), ) @@ -30,24 +29,24 @@ def update_ui(): if status == "no_account": logging.info(f"User {tag} not found.") - global_.traffic_light.set_red() - global_.app.show_frame(NoAccNoWaiver) - global_.app.after(3000, lambda: global_.app.show_frame(NoAccNoWaiverSwipe)) + ctx.traffic_light.set_red() + ctx.app.show_frame(NoAccNoWaiver) + ctx.app.after(3000, lambda: ctx.app.show_frame(NoAccNoWaiverSwipe)) return if status == "no_waiver": logging.info(f"User {tag} does not have waiver.") - global_.traffic_light.set_yellow() - global_.app.show_frame(AccNoWaiver) - global_.app.after(3000, lambda: global_.app.show_frame(AccNoWaiverSwipe)) + ctx.traffic_light.set_yellow() + ctx.app.show_frame(AccNoWaiver) + ctx.app.after(3000, lambda: ctx.app.show_frame(AccNoWaiverSwipe)) return logging.info(f"User found: {result['name']}") - global_.traffic_light.set_green() - global_.app.get_frame(UserWelcome).displayName(result["name"]) + ctx.traffic_light.set_green() + ctx.app.get_frame(UserWelcome).displayName(result["name"]) write_checkin({ "Name": result["name"], "Student ID": result["student_id"], }, tag) - global_.app.after(0, update_ui) + ctx.app.after(0, update_ui) diff --git a/src/global_.py b/src/global_.py deleted file mode 100644 index 88982e6..0000000 --- a/src/global_.py +++ /dev/null @@ -1,43 +0,0 @@ -import threading -from sheets import SheetManager -from traffic import TrafficLight - -rfid = "" -sheets = None -app = None -traffic_light = None - - -class _TrafficProxy: - def __init__(self, light, sheets_mgr): - self._light = light - self._sheets = sheets_mgr - - @property - def connected(self): - return self._light.ser is not None - - def _post(self, color): - threading.Thread( - target=self._sheets.set_traffic_light, - args=(color,), - daemon=True, - ).start() - - def set_red(self): - self._post("red") - - def set_green(self): - self._post("green") - - def set_yellow(self): - self._post("yellow") - - def set_off(self): - self._post("off") - - -def init(traffic_usb_id=None): - global sheets, traffic_light - sheets = SheetManager() - traffic_light = _TrafficProxy(TrafficLight(traffic_usb_id), sheets) diff --git a/src/gui.py b/src/gui.py index 99f0ce7..522e0bd 100644 --- a/src/gui.py +++ b/src/gui.py @@ -1,7 +1,7 @@ import uuid import tkinter as tk from pathlib import Path -import global_ +from app_context import AppContext ASSETS_PATH = Path(__file__).parent / "assets" / "shared" @@ -10,8 +10,9 @@ # Acts as the controller and the user interface # ################################################# class Gui(tk.Tk): - def __init__(self, *args, **kwargs): + def __init__(self, ctx: AppContext, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) + self.ctx = ctx self.title("Check-In") self.geometry("1280x720") @@ -89,7 +90,7 @@ def timeout_fn(self, curr_uuid): from screens.main_page import MainPage if curr_uuid == self.frame_uuid: self.show_frame(MainPage) - global_.traffic_light.set_off() + self.ctx.traffic_light.set_off() def show_frame(self, cont): if self.curr_frame is not None: diff --git a/src/main.py b/src/main.py index 269c722..5faa0c1 100644 --- a/src/main.py +++ b/src/main.py @@ -2,7 +2,6 @@ from gui import Gui from reader import * from swipe import Swipe -from sheets import * from threading import Thread from screens.main_page import MainPage from screens.manual_fill import ManualFill @@ -10,12 +9,12 @@ from utils import Utils from core.handle_check_in import handle_check_in from core.render_ports import get_usb_ids -import global_ +from app_context import AppContext +from sheets import check_api_health import socket import logging import argparse from sys import stdout -from sheets import check_api_health def is_connected(host="8.8.8.8", port=53, timeout=3): """ @@ -36,7 +35,7 @@ def is_connected(host="8.8.8.8", port=53, timeout=3): no_wifi_shown = False -def myLoop(app, reader): +def myLoop(ctx: AppContext, reader): global no_wifi_shown, no_wifi logging.info("Now reading ID cards") last_tag = 0 @@ -64,7 +63,7 @@ def myLoop(app, reader): if not no_wifi_shown: no_wifi_shown = True no_wifi = Label( - app.canvas, + ctx.app.canvas, text="ERROR! Connection cannot be established, please let staff know.", bg="#153246", fg="white", font=("Arial", 25), ) @@ -72,7 +71,7 @@ def myLoop(app, reader): no_wifi.after(4000, lambda: destroyNoWifiError(no_wifi)) continue - app.get_frame(ManualFill).clearEntries() + ctx.app.get_frame(ManualFill).clearEntries() tag = reader.grabRFID() if " " in tag: @@ -90,18 +89,18 @@ def myLoop(app, reader): else: logging.debug("RFID Check Succeeded") - global_.rfid = tag - handle_check_in(tag) + ctx.rfid = tag + handle_check_in(ctx, tag) last_tag = tag last_time = time.time() -def trafficLightPoller(): +def trafficLightPoller(ctx: AppContext): last_color = None - light = global_.traffic_light._light + light = ctx.traffic_light._light while True: time.sleep(0.1) - color = global_.sheets.get_traffic_light() + color = ctx.sheets.get_traffic_light() if color != last_color: last_color = color if color == "red": @@ -119,11 +118,11 @@ def destroyNoWifiError(no_wifi): no_wifi.destroy() no_wifi_shown = False -def clearAndReturn(): - global_.traffic_light.set_off() - global_.app.show_frame(MainPage) - global_.app.get_frame(ManualFill).clearEntries() - global_.app.get_frame(CheckInNoId).clearEntries() +def clearAndReturn(ctx: AppContext): + ctx.traffic_light.set_off() + ctx.app.show_frame(MainPage) + ctx.app.get_frame(ManualFill).clearEntries() + ctx.app.get_frame(CheckInNoId).clearEntries() if __name__ == "__main__": parser = argparse.ArgumentParser( @@ -148,21 +147,21 @@ def clearAndReturn(): reader_usb_id, traffic_usb_id = get_usb_ids() check_api_health() - global_.init(traffic_usb_id) - app = Gui() - global_.app = app - global_.traffic_light.set_off() + ctx = AppContext.create(traffic_usb_id) + app = Gui(ctx) + ctx.app = app + ctx.traffic_light.set_off() - sw = Swipe() + sw = Swipe(ctx) reader = Reader(reader_usb_id) util = Utils() - thread = Thread(target=myLoop, args=(app, reader)) + thread = Thread(target=myLoop, args=(ctx, reader)) logging.info("Starting thread") thread.start() - if global_.traffic_light.connected: - poller = Thread(target=trafficLightPoller, daemon=True) + if ctx.traffic_light.connected: + poller = Thread(target=trafficLightPoller, args=(ctx,), daemon=True) poller.start() app.bind("", lambda i: sw.keyboardPress(i)) - app.bind("", lambda i: clearAndReturn()) + app.bind("", lambda i: clearAndReturn(ctx)) logging.info("Made it to app start") - app.start() \ No newline at end of file + app.start() diff --git a/src/screens/acc_no_waiver_swipe.py b/src/screens/acc_no_waiver_swipe.py index 33ff24c..fe85e28 100644 --- a/src/screens/acc_no_waiver_swipe.py +++ b/src/screens/acc_no_waiver_swipe.py @@ -1,8 +1,6 @@ from pathlib import Path from tkinter import Button from .screen import Screen -import global_ - ASSETS_PATH = Path(__file__).parent.parent / "assets" / "acc_no_waiver_swipe_assets" @@ -50,5 +48,5 @@ def _build(self, controller): def _back_to_main(self): from .main_page import MainPage - global_.traffic_light.set_off() - global_.app.show_frame(MainPage) + self.controller.ctx.traffic_light.set_off() + self.controller.show_frame(MainPage) diff --git a/src/screens/check_in_no_id.py b/src/screens/check_in_no_id.py index daa9312..5d53898 100644 --- a/src/screens/check_in_no_id.py +++ b/src/screens/check_in_no_id.py @@ -1,7 +1,6 @@ from pathlib import Path from tkinter import Button, Entry, StringVar, END from .screen import Screen -import global_ import logging ASSETS_PATH = Path(__file__).parent.parent / "assets" / "check_in_no_id_assets" @@ -77,7 +76,7 @@ def _call_check_in(self, controller): self.canvas.update_idletasks() self.clearEntries() - result = global_.sheets.checkin_by_pid(pid) + result = self.controller.ctx.sheets.checkin_by_pid(pid) status = result.get("status") if self.loading_text_id is not None: @@ -97,5 +96,5 @@ def _call_check_in(self, controller): return logging.info(f"Manual check-in for {result['name']}") - global_.traffic_light.set_green() - global_.app.get_frame(UserWelcome).displayName(result["name"]) + self.controller.ctx.traffic_light.set_green() + self.controller.get_frame(UserWelcome).displayName(result["name"]) diff --git a/src/screens/main_page.py b/src/screens/main_page.py index b22740e..b14d1a9 100644 --- a/src/screens/main_page.py +++ b/src/screens/main_page.py @@ -1,8 +1,6 @@ from pathlib import Path from tkinter import Button from .screen import Screen -import global_ - ASSETS_PATH = Path(__file__).parent.parent / "assets" / "main_page_assets" @@ -49,6 +47,6 @@ def _build(self, controller): def _go_to_no_id(self, controller): from .check_in_no_id import CheckInNoId - no_id = global_.app.get_frame(CheckInNoId) + no_id = controller.get_frame(CheckInNoId) no_id.clearEntries() controller.show_frame(CheckInNoId) diff --git a/src/screens/manual_fill.py b/src/screens/manual_fill.py index 791265c..2c2fa17 100644 --- a/src/screens/manual_fill.py +++ b/src/screens/manual_fill.py @@ -114,7 +114,7 @@ def _call_account_creation(self): self.clearEntries() try: delay = timeit.timeit( - lambda: util.createAccount(data[0], data[1], data[2], data[3], ManualFill), + lambda: util.createAccount(self.controller.ctx, data[0], data[1], data[2], data[3], ManualFill), number=1, ) logging.debug(f"Time to create account: {delay}") diff --git a/src/screens/no_acc_no_waiver_swipe.py b/src/screens/no_acc_no_waiver_swipe.py index 3295962..8ddeb3d 100644 --- a/src/screens/no_acc_no_waiver_swipe.py +++ b/src/screens/no_acc_no_waiver_swipe.py @@ -1,8 +1,6 @@ from pathlib import Path from tkinter import Button from .screen import Screen -import global_ - ASSETS_PATH = Path(__file__).parent.parent / "assets" / "no_acc_no_waiver_swipe_assets" @@ -44,5 +42,5 @@ def _build(self, controller): def _go_to_manual_fill(self, controller): from .manual_fill import ManualFill - global_.app.get_frame(ManualFill).clearEntries() + controller.get_frame(ManualFill).clearEntries() controller.show_frame(ManualFill) diff --git a/src/screens/user_thank.py b/src/screens/user_thank.py index db5afa2..65b1c20 100644 --- a/src/screens/user_thank.py +++ b/src/screens/user_thank.py @@ -1,5 +1,4 @@ from .screen import Screen -import global_ class UserThank(Screen): @@ -21,12 +20,12 @@ def hide(self): def displayName(self, name, nextPage): from .main_page import MainPage - global_.app.show_frame(UserThank) + self.controller.show_frame(UserThank) if nextPage == MainPage: - global_.traffic_light.set_green() + self.controller.ctx.traffic_light.set_green() else: - global_.traffic_light.set_yellow() + self.controller.ctx.traffic_light.set_yellow() self.canvas.create_text( 99.0, 323.0, anchor="nw", @@ -37,10 +36,10 @@ def displayName(self, name, nextPage): ) self.canvas.after(4500, lambda: self.canvas.delete("thank")) - global_.app.after(4000, lambda: self._go_to_next(nextPage)) + self.controller.after(4000, lambda: self._go_to_next(nextPage)) def _go_to_next(self, nextPage): from .main_page import MainPage - global_.app.show_frame(nextPage) + self.controller.show_frame(nextPage) if nextPage == MainPage: - global_.traffic_light.set_off() + self.controller.ctx.traffic_light.set_off() diff --git a/src/screens/user_welcome.py b/src/screens/user_welcome.py index 0302d74..82d9da7 100644 --- a/src/screens/user_welcome.py +++ b/src/screens/user_welcome.py @@ -1,5 +1,4 @@ from .screen import Screen -import global_ class UserWelcome(Screen): @@ -27,7 +26,7 @@ def displayName(self, name): self.last_name = name from .main_page import MainPage - global_.app.show_frame(UserWelcome) + self.controller.show_frame(UserWelcome) text_id = self.canvas.create_text( 99.0, @@ -54,5 +53,5 @@ def _remove_name(self, text_id): if not self.canvas.find_withtag("welcome"): self.last_name = None - global_.traffic_light.set_off() - global_.app.show_frame(MainPage) + self.controller.ctx.traffic_light.set_off() + self.controller.show_frame(MainPage) diff --git a/src/screens/waiver_no_acc_swipe.py b/src/screens/waiver_no_acc_swipe.py index d988575..5f8e0c2 100644 --- a/src/screens/waiver_no_acc_swipe.py +++ b/src/screens/waiver_no_acc_swipe.py @@ -1,8 +1,6 @@ from pathlib import Path from tkinter import Button from .screen import Screen -import global_ - ASSETS_PATH = Path(__file__).parent.parent / "assets" / "waiver_no_acc_swipe_assets" @@ -44,5 +42,5 @@ def _build(self, controller): def _go_to_manual_fill(self, controller): from .manual_fill import ManualFill - global_.app.get_frame(ManualFill).clearEntries() + controller.get_frame(ManualFill).clearEntries() controller.show_frame(ManualFill) diff --git a/src/swipe.py b/src/swipe.py index 1326a05..70f1777 100644 --- a/src/swipe.py +++ b/src/swipe.py @@ -6,7 +6,6 @@ from screens.check_in_no_id import CheckInNoId from get_info_from_pid import contact_client from utils import Utils -import global_ import logging ############################################ @@ -17,14 +16,15 @@ class Swipe: - def __init__(self): + def __init__(self, ctx): global id_string id_string = "" + self.ctx = ctx def keyboardPress(self, key): util = Utils() global id_string, swipe_error_shown - curr_frame = global_.app.get_curr_frame() + curr_frame = self.ctx.app.get_curr_frame() if curr_frame not in (NoAccNoWaiverSwipe, WaiverNoAccSwipe, CheckInNoId): return @@ -37,7 +37,7 @@ def keyboardPress(self, key): id_string = "" if not swipe_error_shown: swipe_error_shown = True - canvas = global_.app.canvas + canvas = self.ctx.app.canvas id_error = tkinter.Label( canvas, text="Error, please scan again", bg="#153246", fg="white", font=("Arial", 20), @@ -75,35 +75,21 @@ def pullUser(self, barcode, u_type): if not u_info: logging.info("Student search returned False, returning...") return - + logging.info(f"Info pull succeeded:\n {u_info[0]}, {u_info[1]}, {u_info[3]}") return u_info def swipeCard(self, id_string): - # Grabs the input from the global swipe entry - # Deletes text from the entry box - # Checks if any of the ID is a letter - # If so return - # Calls magswipe() on the entered string - user_card_number = id_string.strip() - # u_info = self.magSwipe(id_string) - - # u_type = u_info[0] - # u_id = u_info[1] - # u_id = u_id.replace("+E?", "")[:9] - - # u_data is a list containing the user type and their ID u_data = self.pullUser(user_card_number, "Student") if not u_data: logging.info("Student search returned False, returning...") return - # if u_type == "Student": - # u_id = "A" + u_id - if global_.app.get_curr_frame() == CheckInNoId: - global_.app.get_frame(CheckInNoId).clearEntries() - global_.app.get_frame(CheckInNoId).updateEntries(u_data[3]) + + if self.ctx.app.get_curr_frame() == CheckInNoId: + self.ctx.app.get_frame(CheckInNoId).clearEntries() + self.ctx.app.get_frame(CheckInNoId).updateEntries(u_data[3]) return email_to_use = "" if len(u_data[2]) == 0 else u_data[2][0] @@ -111,14 +97,14 @@ def swipeCard(self, id_string): if email.endswith("@ucsd.edu"): email_to_use = email - manfill = global_.app.get_frame(ManualFill) + manfill = self.ctx.app.get_frame(ManualFill) manfill.clearEntries() logging.info( f"Filling data with {u_data[0]} {u_data[1]} {email_to_use} {u_data[3]}" ) manfill.updateEntries(u_data[0], u_data[1], email_to_use, u_data[3]) - global_.app.show_frame(ManualFill) + self.ctx.app.show_frame(ManualFill) def magSwipe(self, ID): # Makes a new empty string diff --git a/src/utils.py b/src/utils.py index a8201ff..123c2f8 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,6 +1,5 @@ from datetime import datetime import time -import global_ import tkinter from screens.main_page import MainPage from screens.acc_no_waiver_swipe import AccNoWaiverSwipe @@ -51,13 +50,13 @@ def IDVet(self, id_check): def getDatetime(self): return datetime.now().strftime("%m/%d/%Y %H:%M:%S") - def createAccount(self, fname, lname, email, pid, ManualFill): + def createAccount(self, ctx, fname, lname, email, pid, ManualFill): start = time.perf_counter() idValid = self.IDCheck(pid) emailValid = self.emailCheck(email) nameValid = self.nameCheck(fname, lname) - canvas = global_.app.canvas + canvas = ctx.app.canvas for validation in (idValid, emailValid, nameValid): if validation != "good": @@ -77,7 +76,7 @@ def createAccount(self, fname, lname, email, pid, ManualFill): bg="#153246", fg="white", font=("Arial", 25), ) inProgress.place(relx=0.5, rely=0.87, anchor="center") - global_.app.update() + ctx.app.update() full_name = fname + " " + lname logging.info(f"Creating user account for {full_name}") @@ -94,7 +93,7 @@ def createAccount(self, fname, lname, email, pid, ManualFill): retries = 1 while retries < 6: try: - result = global_.sheets.create_account(fname, lname, email, pid, global_.rfid) + result = ctx.sheets.create_account(fname, lname, email, pid, ctx.rfid) end3 = time.perf_counter() logging.debug(f"Time to create account: {end3 - end2}") @@ -106,25 +105,25 @@ def createAccount(self, fname, lname, email, pid, ManualFill): logging.warning("Exception occurred while in account creation") logging.exception("Exception occurred while in account creation") no_wifi.place(relx=0.5, rely=0.91, anchor="center") - global_.app.update() + ctx.app.update() time.sleep(retries) retries += 1 no_wifi.destroy() if retries == 6: - global_.app.show_frame(MainPage) + ctx.app.show_frame(MainPage) inProgress.destroy() return end4 = time.perf_counter() logging.debug(f"Total time to send data: {end4 - end2}") - checkin_result = global_.sheets.checkin_by_uuid(global_.rfid) + checkin_result = ctx.sheets.checkin_by_uuid(ctx.rfid) toGoTo = AccNoWaiverSwipe if checkin_result.get("status") == "no_waiver" else MainPage end5 = time.perf_counter() logging.debug(f"Time to check waiver via check-in: {end5 - end4}") - global_.app.get_frame(UserThank).displayName(full_name, toGoTo) + ctx.app.get_frame(UserThank).displayName(full_name, toGoTo) inProgress.destroy() From 351489a2311e5b9877488851d16aaef5cd4ddf40 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 15:02:39 -0700 Subject: [PATCH 06/14] separate controller logic --- src/app_context.py | 3 +- src/controllers/__init__.py | 0 src/controllers/navigation_controller.py | 78 ++++++++++++++ src/controllers/swipe_controller.py | 88 ++++++++++++++++ src/core/handle_check_in.py | 14 +-- src/gui.py | 113 -------------------- src/main.py | 57 +++++----- src/swipe.py | 129 ----------------------- src/utils.py | 10 +- src/window.py | 38 +++++++ 10 files changed, 242 insertions(+), 288 deletions(-) create mode 100644 src/controllers/__init__.py create mode 100644 src/controllers/navigation_controller.py create mode 100644 src/controllers/swipe_controller.py delete mode 100644 src/gui.py delete mode 100644 src/swipe.py create mode 100644 src/window.py diff --git a/src/app_context.py b/src/app_context.py index 8ad57fa..154c59d 100644 --- a/src/app_context.py +++ b/src/app_context.py @@ -37,7 +37,8 @@ class AppContext: def __init__(self, sheets: SheetManager, traffic_light: _TrafficProxy): self.sheets = sheets self.traffic_light = traffic_light - self.app = None # set after Gui is created + self.window = None + self.nav = None self._rfid_lock = threading.Lock() self._rfid: str = "" diff --git a/src/controllers/__init__.py b/src/controllers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/controllers/navigation_controller.py b/src/controllers/navigation_controller.py new file mode 100644 index 0000000..fa8a37f --- /dev/null +++ b/src/controllers/navigation_controller.py @@ -0,0 +1,78 @@ +import uuid + + +class NavigationController: + def __init__(self, window, ctx): + self.ctx = ctx + self._window = window + self._frames = {} + self._curr = None + self._frame_uuid = uuid.uuid4().hex + + from screens.main_page import MainPage + from screens.acc_no_waiver import AccNoWaiver + from screens.acc_no_waiver_swipe import AccNoWaiverSwipe + from screens.manual_fill import ManualFill + from screens.check_in_no_id import CheckInNoId + from screens.no_acc_check_in_only import NoAccCheckInOnly + from screens.no_acc_no_waiver import NoAccNoWaiver + from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe + from screens.qr_codes import QRCodes + from screens.user_thank import UserThank + from screens.user_welcome import UserWelcome + from screens.waiver_no_acc import WaiverNoAcc + from screens.waiver_no_acc_swipe import WaiverNoAccSwipe + + self._timeouts = { + AccNoWaiverSwipe: 30000, + QRCodes: 30000, + NoAccNoWaiverSwipe: 30000, + } + + for F in ( + MainPage, + AccNoWaiver, + AccNoWaiverSwipe, + ManualFill, + CheckInNoId, + NoAccCheckInOnly, + NoAccNoWaiver, + NoAccNoWaiverSwipe, + QRCodes, + UserThank, + UserWelcome, + WaiverNoAcc, + WaiverNoAccSwipe, + ): + self._frames[F] = F(window.canvas, self) + + self.show_frame(MainPage) + + def show_frame(self, screen_class): + if self._curr is not None: + self._frames[self._curr].hide() + self._curr = screen_class + self._frame_uuid = uuid.uuid4().hex + self._frames[screen_class].show() + + if screen_class in self._timeouts: + uid = self._frame_uuid + self._window.after( + self._timeouts[screen_class], + lambda: self._on_timeout(uid), + ) + + def get_frame(self, screen_class): + return self._frames[screen_class] + + def get_curr_frame(self): + return self._curr + + def after(self, ms, fn): + self._window.after(ms, fn) + + def _on_timeout(self, uid): + from screens.main_page import MainPage + if uid == self._frame_uuid: + self.show_frame(MainPage) + self.ctx.traffic_light.set_off() diff --git a/src/controllers/swipe_controller.py b/src/controllers/swipe_controller.py new file mode 100644 index 0000000..11193e3 --- /dev/null +++ b/src/controllers/swipe_controller.py @@ -0,0 +1,88 @@ +import tkinter +import logging +from screens.manual_fill import ManualFill +from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe +from screens.waiver_no_acc_swipe import WaiverNoAccSwipe +from screens.check_in_no_id import CheckInNoId +from get_info_from_pid import contact_client +from utils import Utils + + +class SwipeController: + def __init__(self, ctx): + self.ctx = ctx + self._id_string = "" + self._swipe_error_shown = False + + def keyboardPress(self, key): + util = Utils() + curr_frame = self.ctx.nav.get_curr_frame() + + if curr_frame not in (NoAccNoWaiverSwipe, WaiverNoAccSwipe, CheckInNoId): + return + + self._id_string += key.char + logging.debug("The array is now: " + repr(str(self._id_string))) + + if self._id_string.endswith("\r"): + if util.IDVet(self._id_string) == "bad": + self._id_string = "" + if not self._swipe_error_shown: + self._swipe_error_shown = True + id_error = tkinter.Label( + self.ctx.window.canvas, text="Error, please scan again", + bg="#153246", fg="white", font=("Arial", 20), + ) + id_error.place(relx=0.5, rely=0.85, anchor="center") + id_error.after(1500, lambda: self._destroySwipeError(id_error)) + return + + self._swipeCard(self._id_string) + self._id_string = "" + + def _pullUser(self, barcode, u_type): + logging.info(f"Card barcode read is: {barcode}. Trying to pull user...") + + contact = contact_client() + try: + if u_type == "Staff": + u_info = contact.get_staff_info(barcode) + elif u_type == "Student": + u_info = contact.get_student_info(barcode) + except Exception: + logging.warning("An exception has ocurred with pulling user information", exc_info=True) + return None + + if not u_info: + logging.info("Student search returned False, returning...") + return None + + logging.info(f"Info pull succeeded:\n {u_info[0]}, {u_info[1]}, {u_info[3]}") + return u_info + + def _swipeCard(self, id_string): + u_data = self._pullUser(id_string.strip(), "Student") + if not u_data: + logging.info("Student search returned False, returning...") + return + + if self.ctx.nav.get_curr_frame() == CheckInNoId: + self.ctx.nav.get_frame(CheckInNoId).clearEntries() + self.ctx.nav.get_frame(CheckInNoId).updateEntries(u_data[3]) + return + + email_to_use = "" if len(u_data[2]) == 0 else u_data[2][0] + for email in u_data[2]: + if email.endswith("@ucsd.edu"): + email_to_use = email + + manfill = self.ctx.nav.get_frame(ManualFill) + manfill.clearEntries() + logging.info(f"Filling data with {u_data[0]} {u_data[1]} {email_to_use} {u_data[3]}") + manfill.updateEntries(u_data[0], u_data[1], email_to_use, u_data[3]) + + self.ctx.nav.show_frame(ManualFill) + + def _destroySwipeError(self, id_error): + id_error.destroy() + self._swipe_error_shown = False diff --git a/src/core/handle_check_in.py b/src/core/handle_check_in.py index 8fc70a7..f2e4ea2 100644 --- a/src/core/handle_check_in.py +++ b/src/core/handle_check_in.py @@ -19,7 +19,7 @@ def update_ui(): logging.error("API error during check-in") ctx.traffic_light.set_red() error_label = Label( - ctx.app.canvas, + ctx.window.canvas, text="System error, please let staff know.", bg="#153246", fg="white", font=("Arial", 25), ) @@ -30,23 +30,23 @@ def update_ui(): if status == "no_account": logging.info(f"User {tag} not found.") ctx.traffic_light.set_red() - ctx.app.show_frame(NoAccNoWaiver) - ctx.app.after(3000, lambda: ctx.app.show_frame(NoAccNoWaiverSwipe)) + ctx.nav.show_frame(NoAccNoWaiver) + ctx.window.after(3000, lambda: ctx.nav.show_frame(NoAccNoWaiverSwipe)) return if status == "no_waiver": logging.info(f"User {tag} does not have waiver.") ctx.traffic_light.set_yellow() - ctx.app.show_frame(AccNoWaiver) - ctx.app.after(3000, lambda: ctx.app.show_frame(AccNoWaiverSwipe)) + ctx.nav.show_frame(AccNoWaiver) + ctx.window.after(3000, lambda: ctx.nav.show_frame(AccNoWaiverSwipe)) return logging.info(f"User found: {result['name']}") ctx.traffic_light.set_green() - ctx.app.get_frame(UserWelcome).displayName(result["name"]) + ctx.nav.get_frame(UserWelcome).displayName(result["name"]) write_checkin({ "Name": result["name"], "Student ID": result["student_id"], }, tag) - ctx.app.after(0, update_ui) + ctx.window.after(0, update_ui) diff --git a/src/gui.py b/src/gui.py deleted file mode 100644 index 522e0bd..0000000 --- a/src/gui.py +++ /dev/null @@ -1,113 +0,0 @@ -import uuid -import tkinter as tk -from pathlib import Path -from app_context import AppContext - -ASSETS_PATH = Path(__file__).parent / "assets" / "shared" - - -################################################# -# Acts as the controller and the user interface # -################################################# -class Gui(tk.Tk): - def __init__(self, ctx: AppContext, *args, **kwargs): - tk.Tk.__init__(self, *args, **kwargs) - self.ctx = ctx - - self.title("Check-In") - self.geometry("1280x720") - self.bind("", self._on_map) - - # Single shared canvas — background is always painted here, never redrawn - self.canvas = tk.Canvas( - self, - bg="#153246", - height=720, - width=1280, - bd=0, - highlightthickness=0, - ) - self.canvas.pack(fill="both", expand=True) - - # Load background images once and draw them permanently - self._bg_photos = [] - bg1 = tk.PhotoImage(file=str(ASSETS_PATH / "image_1.png")) - self._bg_photos.append(bg1) - self.canvas.create_image(640.0, 360.0, image=bg1) - - bg2 = tk.PhotoImage(file=str(ASSETS_PATH / "image_2.png")) - self._bg_photos.append(bg2) - self.canvas.create_image(639.333984375, 359.333984375, image=bg2) - - self.frames = {} - self.curr_frame = None - self.frame_uuid = uuid.uuid4().hex - - from screens.main_page import MainPage - from screens.acc_no_waiver import AccNoWaiver - from screens.acc_no_waiver_swipe import AccNoWaiverSwipe - from screens.manual_fill import ManualFill - from screens.check_in_no_id import CheckInNoId - from screens.no_acc_check_in_only import NoAccCheckInOnly - from screens.no_acc_no_waiver import NoAccNoWaiver - from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe - from screens.qr_codes import QRCodes - from screens.user_thank import UserThank - from screens.user_welcome import UserWelcome - from screens.waiver_no_acc import WaiverNoAcc - from screens.waiver_no_acc_swipe import WaiverNoAccSwipe - - self._timeouts = { - AccNoWaiverSwipe: 30000, - QRCodes: 30000, - NoAccNoWaiverSwipe: 30000, - } - - for F in ( - MainPage, - AccNoWaiver, - AccNoWaiverSwipe, - ManualFill, - CheckInNoId, - NoAccCheckInOnly, - NoAccNoWaiver, - NoAccNoWaiverSwipe, - QRCodes, - UserThank, - UserWelcome, - WaiverNoAcc, - WaiverNoAccSwipe, - ): - self.frames[F] = F(self.canvas, self) - - self.show_frame(MainPage) - - def _on_map(self, event): - self.unbind("") - self.attributes("-fullscreen", True) - - def timeout_fn(self, curr_uuid): - from screens.main_page import MainPage - if curr_uuid == self.frame_uuid: - self.show_frame(MainPage) - self.ctx.traffic_light.set_off() - - def show_frame(self, cont): - if self.curr_frame is not None: - self.frames[self.curr_frame].hide() - self.curr_frame = cont - self.frame_uuid = uuid.uuid4().hex - self.frames[cont].show() - - if cont in self._timeouts: - curr_uuid = self.frame_uuid - self.after(self._timeouts[cont], lambda: self.timeout_fn(curr_uuid)) - - def get_frame(self, cont): - return self.frames[cont] - - def get_curr_frame(self): - return self.curr_frame - - def start(self): - self.mainloop() diff --git a/src/main.py b/src/main.py index 5faa0c1..06a0009 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,8 @@ from tkinter import Label -from gui import Gui +from window import CheckInWindow +from controllers.navigation_controller import NavigationController from reader import * -from swipe import Swipe +from controllers.swipe_controller import SwipeController from threading import Thread from screens.main_page import MainPage from screens.manual_fill import ManualFill @@ -16,25 +17,19 @@ import argparse from sys import stdout + def is_connected(host="8.8.8.8", port=53, timeout=3): - """ - Host: 8.8.8.8 (google-public-dns-a.google.com) - OpenPort: 53/tcp - Service: domain (DNS/TCP) - """ try: socket.setdefaulttimeout(timeout) socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port)) return True - except socket.error as ex: + except socket.error: return False -############################################################## -# This acts as the main loop of the program, ran in a thread # -############################################################## no_wifi_shown = False + def myLoop(ctx: AppContext, reader): global no_wifi_shown, no_wifi logging.info("Now reading ID cards") @@ -63,7 +58,7 @@ def myLoop(ctx: AppContext, reader): if not no_wifi_shown: no_wifi_shown = True no_wifi = Label( - ctx.app.canvas, + ctx.window.canvas, text="ERROR! Connection cannot be established, please let staff know.", bg="#153246", fg="white", font=("Arial", 25), ) @@ -71,7 +66,7 @@ def myLoop(ctx: AppContext, reader): no_wifi.after(4000, lambda: destroyNoWifiError(no_wifi)) continue - ctx.app.get_frame(ManualFill).clearEntries() + ctx.nav.get_frame(ManualFill).clearEntries() tag = reader.grabRFID() if " " in tag: @@ -95,6 +90,7 @@ def myLoop(ctx: AppContext, reader): last_tag = tag last_time = time.time() + def trafficLightPoller(ctx: AppContext): last_color = None light = ctx.traffic_light._light @@ -118,29 +114,23 @@ def destroyNoWifiError(no_wifi): no_wifi.destroy() no_wifi_shown = False + def clearAndReturn(ctx: AppContext): ctx.traffic_light.set_off() - ctx.app.show_frame(MainPage) - ctx.app.get_frame(ManualFill).clearEntries() - ctx.app.get_frame(CheckInNoId).clearEntries() + ctx.nav.show_frame(MainPage) + ctx.nav.get_frame(ManualFill).clearEntries() + ctx.nav.get_frame(CheckInNoId).clearEntries() + if __name__ == "__main__": parser = argparse.ArgumentParser( description="Makerspace Check-in System", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) - - parser.add_argument( - "-v", - "--verbose", - action="store_true", - help="Increase verbosity (print debug info)", - ) - + parser.add_argument("-v", "--verbose", action="store_true", help="Increase verbosity (print debug info)") args = parser.parse_args() - config = vars(args) - if config["verbose"]: + if args.verbose: logging.basicConfig(level=logging.DEBUG, stream=stdout) else: logging.basicConfig(level=logging.INFO) @@ -148,20 +138,21 @@ def clearAndReturn(ctx: AppContext): reader_usb_id, traffic_usb_id = get_usb_ids() check_api_health() ctx = AppContext.create(traffic_usb_id) - app = Gui(ctx) - ctx.app = app + window = CheckInWindow() + nav = NavigationController(window, ctx) + ctx.window = window + ctx.nav = nav ctx.traffic_light.set_off() - sw = Swipe(ctx) + sw = SwipeController(ctx) reader = Reader(reader_usb_id) - util = Utils() thread = Thread(target=myLoop, args=(ctx, reader)) logging.info("Starting thread") thread.start() if ctx.traffic_light.connected: poller = Thread(target=trafficLightPoller, args=(ctx,), daemon=True) poller.start() - app.bind("", lambda i: sw.keyboardPress(i)) - app.bind("", lambda i: clearAndReturn(ctx)) + window.bind("", lambda i: sw.keyboardPress(i)) + window.bind("", lambda i: clearAndReturn(ctx)) logging.info("Made it to app start") - app.start() + window.start() diff --git a/src/swipe.py b/src/swipe.py deleted file mode 100644 index 70f1777..0000000 --- a/src/swipe.py +++ /dev/null @@ -1,129 +0,0 @@ -import tkinter -from screens.manual_fill import ManualFill -from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe -from screens.waiver_no_acc_swipe import WaiverNoAccSwipe -from screens.acc_no_waiver_swipe import AccNoWaiverSwipe -from screens.check_in_no_id import CheckInNoId -from get_info_from_pid import contact_client -from utils import Utils -import logging - -############################################ -# This class helps handle reading magswipe # -############################################ - -swipe_error_shown = False - - -class Swipe: - def __init__(self, ctx): - global id_string - id_string = "" - self.ctx = ctx - - def keyboardPress(self, key): - util = Utils() - global id_string, swipe_error_shown - curr_frame = self.ctx.app.get_curr_frame() - - if curr_frame not in (NoAccNoWaiverSwipe, WaiverNoAccSwipe, CheckInNoId): - return - - id_string += key.char - logging.debug("The array is now: " + repr(str(id_string))) - - if id_string.endswith("\r"): - if util.IDVet(id_string) == "bad": - id_string = "" - if not swipe_error_shown: - swipe_error_shown = True - canvas = self.ctx.app.canvas - id_error = tkinter.Label( - canvas, text="Error, please scan again", - bg="#153246", fg="white", font=("Arial", 20), - ) - id_error.place(relx=0.5, rely=0.85, anchor="center") - id_error_2 = id_error # single label serves both swipe screens - id_error.after(1500, lambda: self.destroySwipeError(id_error)) - id_error_2.after(1500, lambda: self.destroySwipeError(id_error_2)) - return - - self.swipeCard(id_string) - id_string = "" - - def pullUser(self, barcode, u_type): - # This function takes in the User's ID and - # if they are a Student or Staff - # and runs David's query funciton accordingly - # It returns a list containing: - # [fname, lname, [emails]] - u_info = [] - - logging.info(f"Card barcode read is: {barcode}. Trying to pull user...") - - contact = contact_client() - try: - if u_type == "Staff": - u_info = contact.get_staff_info(barcode) - elif u_type == "Student": - u_info = contact.get_student_info(barcode) - except Exception as e: - logging.warning( - "An exception has ocurred with pulling user information", exc_info=True - ) - return None - if not u_info: - logging.info("Student search returned False, returning...") - return - - logging.info(f"Info pull succeeded:\n {u_info[0]}, {u_info[1]}, {u_info[3]}") - return u_info - - def swipeCard(self, id_string): - user_card_number = id_string.strip() - - u_data = self.pullUser(user_card_number, "Student") - if not u_data: - logging.info("Student search returned False, returning...") - return - - if self.ctx.app.get_curr_frame() == CheckInNoId: - self.ctx.app.get_frame(CheckInNoId).clearEntries() - self.ctx.app.get_frame(CheckInNoId).updateEntries(u_data[3]) - return - - email_to_use = "" if len(u_data[2]) == 0 else u_data[2][0] - for email in u_data[2]: - if email.endswith("@ucsd.edu"): - email_to_use = email - - manfill = self.ctx.app.get_frame(ManualFill) - manfill.clearEntries() - logging.info( - f"Filling data with {u_data[0]} {u_data[1]} {email_to_use} {u_data[3]}" - ) - manfill.updateEntries(u_data[0], u_data[1], email_to_use, u_data[3]) - - self.ctx.app.show_frame(ManualFill) - - def magSwipe(self, ID): - # Makes a new empty string - # Takes only chars 3-11 from the card swipe text - # Returns student or staff ID - - u_type = "" - - if ID[2] == "9": - u_type = "Student" - elif ID[2] == "0": - u_type = "Staff" - - s = "" - for c in range(3, 11): - s += ID[c] - return [u_type, s] - - def destroySwipeError(self, id_error): - global swipe_error_shown - id_error.destroy() - swipe_error_shown = False diff --git a/src/utils.py b/src/utils.py index 123c2f8..8a00f42 100644 --- a/src/utils.py +++ b/src/utils.py @@ -56,7 +56,7 @@ def createAccount(self, ctx, fname, lname, email, pid, ManualFill): emailValid = self.emailCheck(email) nameValid = self.nameCheck(fname, lname) - canvas = ctx.app.canvas + canvas = ctx.window.canvas for validation in (idValid, emailValid, nameValid): if validation != "good": @@ -76,7 +76,7 @@ def createAccount(self, ctx, fname, lname, email, pid, ManualFill): bg="#153246", fg="white", font=("Arial", 25), ) inProgress.place(relx=0.5, rely=0.87, anchor="center") - ctx.app.update() + ctx.window.update() full_name = fname + " " + lname logging.info(f"Creating user account for {full_name}") @@ -105,14 +105,14 @@ def createAccount(self, ctx, fname, lname, email, pid, ManualFill): logging.warning("Exception occurred while in account creation") logging.exception("Exception occurred while in account creation") no_wifi.place(relx=0.5, rely=0.91, anchor="center") - ctx.app.update() + ctx.window.update() time.sleep(retries) retries += 1 no_wifi.destroy() if retries == 6: - ctx.app.show_frame(MainPage) + ctx.nav.show_frame(MainPage) inProgress.destroy() return @@ -125,5 +125,5 @@ def createAccount(self, ctx, fname, lname, email, pid, ManualFill): end5 = time.perf_counter() logging.debug(f"Time to check waiver via check-in: {end5 - end4}") - ctx.app.get_frame(UserThank).displayName(full_name, toGoTo) + ctx.nav.get_frame(UserThank).displayName(full_name, toGoTo) inProgress.destroy() diff --git a/src/window.py b/src/window.py new file mode 100644 index 0000000..e8f4738 --- /dev/null +++ b/src/window.py @@ -0,0 +1,38 @@ +import tkinter as tk +from pathlib import Path + +ASSETS_PATH = Path(__file__).parent / "assets" / "shared" + + +class CheckInWindow(tk.Tk): + def __init__(self): + super().__init__() + self.title("Check-In") + self.geometry("1280x720") + self.bind("", self._on_map) + + self.canvas = tk.Canvas( + self, + bg="#153246", + height=720, + width=1280, + bd=0, + highlightthickness=0, + ) + self.canvas.pack(fill="both", expand=True) + + self._bg_photos = [] + bg1 = tk.PhotoImage(file=str(ASSETS_PATH / "image_1.png")) + self._bg_photos.append(bg1) + self.canvas.create_image(640.0, 360.0, image=bg1) + + bg2 = tk.PhotoImage(file=str(ASSETS_PATH / "image_2.png")) + self._bg_photos.append(bg2) + self.canvas.create_image(639.333984375, 359.333984375, image=bg2) + + def _on_map(self, event): + self.unbind("") + self.attributes("-fullscreen", True) + + def start(self): + self.mainloop() From 8d2b9239506a7571304031e99e058f5862faec08 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 16:31:05 -0700 Subject: [PATCH 07/14] separate controller logic 2 --- src/api/__init__.py | 0 src/{ => api}/get_info_from_pid.py | 0 src/{ => api}/sheets.py | 0 src/app_context.py | 6 +- .../account_controller.py} | 78 +++++------ src/controllers/card_reader_controller.py | 107 +++++++++++++++ src/controllers/check_in_controller.py | 77 +++++++++++ src/controllers/navigation_controller.py | 19 ++- src/controllers/swipe_controller.py | 13 +- src/core/__init__.py | 2 - src/core/handle_check_in.py | 52 -------- src/core/write_checkin.py | 27 ---- src/hardware/__init__.py | 0 src/{ => hardware}/reader.py | 0 src/{core => hardware}/render_ports.py | 0 src/{ => hardware}/traffic.py | 0 src/main.py | 126 ++---------------- src/screens/acc_no_waiver_swipe.py | 4 +- src/screens/check_in_no_id.py | 25 +--- src/screens/main_page.py | 5 +- src/screens/manual_fill.py | 4 +- src/screens/no_acc_no_waiver_swipe.py | 4 +- src/screens/user_thank.py | 12 +- src/screens/user_welcome.py | 5 +- src/screens/waiver_no_acc_swipe.py | 4 +- 25 files changed, 267 insertions(+), 303 deletions(-) create mode 100644 src/api/__init__.py rename src/{ => api}/get_info_from_pid.py (100%) rename src/{ => api}/sheets.py (100%) rename src/{utils.py => controllers/account_controller.py} (65%) create mode 100644 src/controllers/card_reader_controller.py create mode 100644 src/controllers/check_in_controller.py delete mode 100644 src/core/__init__.py delete mode 100644 src/core/handle_check_in.py delete mode 100644 src/core/write_checkin.py create mode 100644 src/hardware/__init__.py rename src/{ => hardware}/reader.py (100%) rename src/{core => hardware}/render_ports.py (100%) rename src/{ => hardware}/traffic.py (100%) diff --git a/src/api/__init__.py b/src/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/get_info_from_pid.py b/src/api/get_info_from_pid.py similarity index 100% rename from src/get_info_from_pid.py rename to src/api/get_info_from_pid.py diff --git a/src/sheets.py b/src/api/sheets.py similarity index 100% rename from src/sheets.py rename to src/api/sheets.py diff --git a/src/app_context.py b/src/app_context.py index 154c59d..19782c1 100644 --- a/src/app_context.py +++ b/src/app_context.py @@ -1,7 +1,7 @@ import threading -from sheets import SheetManager -from traffic import TrafficLight +from api.sheets import SheetManager +from hardware.traffic import TrafficLight class _TrafficProxy: @@ -39,6 +39,8 @@ def __init__(self, sheets: SheetManager, traffic_light: _TrafficProxy): self.traffic_light = traffic_light self.window = None self.nav = None + self.check_in = None + self.account = None self._rfid_lock = threading.Lock() self._rfid: str = "" diff --git a/src/utils.py b/src/controllers/account_controller.py similarity index 65% rename from src/utils.py rename to src/controllers/account_controller.py index 8a00f42..7a09d59 100644 --- a/src/utils.py +++ b/src/controllers/account_controller.py @@ -1,62 +1,37 @@ -from datetime import datetime import time import tkinter +import logging from screens.main_page import MainPage from screens.acc_no_waiver_swipe import AccNoWaiverSwipe from screens.user_thank import UserThank -import logging -###################################################### -# Utilities that I couldn't get to fit anywhere else # -###################################################### +class AccountController: + def __init__(self, ctx): + self.ctx = ctx -class Utils: - def __init__(self) -> None: - pass - - def emailCheck(self, email): - validations = ( - (lambda s: "@" in s, "Email is invalid"), - (lambda s: "." in s, "Email is invalid"), - ) - - for valid, message in validations: - if not valid(email): - return message - + def _email_check(self, email): + if "@" not in email or "." not in email: + return "Email is invalid" return "good" - def nameCheck(self, fname, lname): + def _name_check(self, fname, lname): if len(fname) == 0 or len(lname) == 0: return "Name was not entered" - return "good" - def IDCheck(self, user_id): + def _id_check(self, user_id): if len(user_id) <= 2 or len(user_id) > 12: return "PID was not entered correctly" return "good" - def IDVet(self, id_check): - if any(i.isalpha() for i in id_check): - return "bad" - - if len(id_check) >= 16: - return "bad" - - return "good" - - def getDatetime(self): - return datetime.now().strftime("%m/%d/%Y %H:%M:%S") - - def createAccount(self, ctx, fname, lname, email, pid, ManualFill): + def create_account(self, fname, lname, email, pid): start = time.perf_counter() - idValid = self.IDCheck(pid) - emailValid = self.emailCheck(email) - nameValid = self.nameCheck(fname, lname) + idValid = self._id_check(pid) + emailValid = self._email_check(email) + nameValid = self._name_check(fname, lname) - canvas = ctx.window.canvas + canvas = self.ctx.window.canvas for validation in (idValid, emailValid, nameValid): if validation != "good": @@ -76,7 +51,7 @@ def createAccount(self, ctx, fname, lname, email, pid, ManualFill): bg="#153246", fg="white", font=("Arial", 25), ) inProgress.place(relx=0.5, rely=0.87, anchor="center") - ctx.window.update() + self.ctx.window.update() full_name = fname + " " + lname logging.info(f"Creating user account for {full_name}") @@ -93,7 +68,7 @@ def createAccount(self, ctx, fname, lname, email, pid, ManualFill): retries = 1 while retries < 6: try: - result = ctx.sheets.create_account(fname, lname, email, pid, ctx.rfid) + result = self.ctx.sheets.create_account(fname, lname, email, pid, self.ctx.rfid) end3 = time.perf_counter() logging.debug(f"Time to create account: {end3 - end2}") @@ -105,25 +80,38 @@ def createAccount(self, ctx, fname, lname, email, pid, ManualFill): logging.warning("Exception occurred while in account creation") logging.exception("Exception occurred while in account creation") no_wifi.place(relx=0.5, rely=0.91, anchor="center") - ctx.window.update() + self.ctx.window.update() time.sleep(retries) retries += 1 no_wifi.destroy() if retries == 6: - ctx.nav.show_frame(MainPage) + self.ctx.nav.show_frame(MainPage) inProgress.destroy() return end4 = time.perf_counter() logging.debug(f"Total time to send data: {end4 - end2}") - checkin_result = ctx.sheets.checkin_by_uuid(ctx.rfid) + checkin_result = self.ctx.sheets.checkin_by_uuid(self.ctx.rfid) toGoTo = AccNoWaiverSwipe if checkin_result.get("status") == "no_waiver" else MainPage end5 = time.perf_counter() logging.debug(f"Time to check waiver via check-in: {end5 - end4}") - ctx.nav.get_frame(UserThank).displayName(full_name, toGoTo) + self.ctx.nav.get_frame(UserThank).displayName(full_name, toGoTo) inProgress.destroy() + + def on_thank_start(self, next_page): + from screens.main_page import MainPage + if next_page == MainPage: + self.ctx.traffic_light.set_green() + else: + self.ctx.traffic_light.set_yellow() + + def on_thank_done(self, next_page): + from screens.main_page import MainPage + self.ctx.nav.show_frame(next_page) + if next_page == MainPage: + self.ctx.traffic_light.set_off() diff --git a/src/controllers/card_reader_controller.py b/src/controllers/card_reader_controller.py new file mode 100644 index 0000000..67e03ea --- /dev/null +++ b/src/controllers/card_reader_controller.py @@ -0,0 +1,107 @@ +import time +import socket +import logging +from tkinter import Label +from threading import Thread +from screens.manual_fill import ManualFill + + +class CardReaderController: + def __init__(self, ctx): + self.ctx = ctx + self._no_wifi_shown = False + + def start(self, reader): + thread = Thread(target=self._run, args=(reader,)) + thread.start() + if self.ctx.traffic_light.connected: + poller = Thread(target=self._poll_traffic_light, daemon=True) + poller.start() + + def _run(self, reader): + logging.info("Now reading ID cards") + last_tag = 0 + last_time = 0 + scanner_error = False + while True: + if scanner_error: + time.sleep(0.1) + if reader.reconnect(): + logging.info("Card reader reconnected") + scanner_error = False + continue + + try: + in_waiting = reader.getSerInWaiting() + except OSError as e: + if not scanner_error: + logging.error("Card reader disconnected, disabling until reconnection: %s", e) + scanner_error = True + continue + + if in_waiting >= 14: + if not self._is_connected(): + logging.info("ERROR wifi is not connected") + if not self._no_wifi_shown: + self._no_wifi_shown = True + no_wifi = Label( + self.ctx.window.canvas, + text="ERROR! Connection cannot be established, please let staff know.", + bg="#153246", fg="white", font=("Arial", 25), + ) + no_wifi.place(relx=0.5, rely=0.1, anchor="center") + no_wifi.after(4000, lambda: self._destroy_wifi_error(no_wifi)) + continue + + self.ctx.nav.get_frame(ManualFill).clearEntries() + tag = reader.grabRFID() + + if " " in tag: + continue + + if tag == last_tag and not reader.canScanAgain(last_time): + logging.debug("Suppressing repeat scan") + continue + + s_reason = reader.checkRFID(tag) + + if s_reason != "good": + logging.debug(s_reason) + continue + else: + logging.debug("RFID Check Succeeded") + + self.ctx.rfid = tag + self.ctx.check_in.handle_by_uuid(tag) + + last_tag = tag + last_time = time.time() + + def _poll_traffic_light(self): + last_color = None + light = self.ctx.traffic_light._light + while True: + time.sleep(0.1) + color = self.ctx.sheets.get_traffic_light() + if color != last_color: + last_color = color + if color == "red": + light.set_red() + elif color == "green": + light.set_green() + elif color == "yellow": + light.set_yellow() + else: + light.set_off() + + def _is_connected(self, host="8.8.8.8", port=53, timeout=3): + try: + socket.setdefaulttimeout(timeout) + socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port)) + return True + except socket.error: + return False + + def _destroy_wifi_error(self, label): + label.destroy() + self._no_wifi_shown = False diff --git a/src/controllers/check_in_controller.py b/src/controllers/check_in_controller.py new file mode 100644 index 0000000..dd58140 --- /dev/null +++ b/src/controllers/check_in_controller.py @@ -0,0 +1,77 @@ +import logging +from tkinter import Label + + +class CheckInController: + def __init__(self, ctx): + self.ctx = ctx + + def handle_by_uuid(self, tag): + result = self.ctx.sheets.checkin_by_uuid(tag) + status = result.get("status") + + def update_ui(): + from screens.main_page import MainPage + from screens.no_acc_no_waiver import NoAccNoWaiver + from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe + from screens.acc_no_waiver import AccNoWaiver + from screens.acc_no_waiver_swipe import AccNoWaiverSwipe + from screens.user_welcome import UserWelcome + + if status == "api_error": + logging.error("API error during check-in") + self.ctx.traffic_light.set_red() + error_label = Label( + self.ctx.window.canvas, + text="System error, please let staff know.", + bg="#153246", fg="white", font=("Arial", 25), + ) + error_label.place(relx=0.5, rely=0.1, anchor="center") + error_label.after(4000, error_label.destroy) + return + + if status == "no_account": + logging.info(f"User {tag} not found.") + self.ctx.traffic_light.set_red() + self.ctx.nav.show_frame(NoAccNoWaiver) + self.ctx.window.after(3000, lambda: self.ctx.nav.show_frame(NoAccNoWaiverSwipe)) + return + + if status == "no_waiver": + logging.info(f"User {tag} does not have waiver.") + self.ctx.traffic_light.set_yellow() + self.ctx.nav.show_frame(AccNoWaiver) + self.ctx.window.after(3000, lambda: self.ctx.nav.show_frame(AccNoWaiverSwipe)) + return + + logging.info(f"User found: {result['name']}") + self.ctx.traffic_light.set_green() + self.ctx.nav.get_frame(UserWelcome).displayName(result["name"]) + + self.ctx.window.after(0, update_ui) + + def handle_by_pid(self, pid): + from screens.no_acc_check_in_only import NoAccCheckInOnly + from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe + from screens.user_welcome import UserWelcome + from screens.acc_no_waiver import AccNoWaiver + from screens.main_page import MainPage + + result = self.ctx.sheets.checkin_by_pid(pid) + status = result.get("status") + + if status == "no_account": + logging.info("Manual check-in: user account not found") + self.ctx.nav.show_frame(NoAccCheckInOnly) + self.ctx.nav.after(5000, lambda: self.ctx.nav.show_frame(MainPage)) + return + + if status == "no_waiver": + logging.info(f"Manual check-in: no waiver for {result.get('name', pid)}") + self.ctx.nav.show_frame(AccNoWaiver) + self.ctx.nav.after(3000, lambda: self.ctx.nav.show_frame(NoAccNoWaiverSwipe)) + return + + logging.info(f"Manual check-in for {result['name']}") + self.ctx.traffic_light.set_green() + self.ctx.nav.get_frame(UserWelcome).displayName(result["name"]) diff --git a/src/controllers/navigation_controller.py b/src/controllers/navigation_controller.py index fa8a37f..bbd6ec3 100644 --- a/src/controllers/navigation_controller.py +++ b/src/controllers/navigation_controller.py @@ -71,8 +71,21 @@ def get_curr_frame(self): def after(self, ms, fn): self._window.after(ms, fn) - def _on_timeout(self, uid): + def back_to_main(self): from screens.main_page import MainPage + self.ctx.traffic_light.set_off() + self.show_frame(MainPage) + + def go_to_no_id(self): + from screens.check_in_no_id import CheckInNoId + self.get_frame(CheckInNoId).clearEntries() + self.show_frame(CheckInNoId) + + def go_to_manual_fill(self): + from screens.manual_fill import ManualFill + self.get_frame(ManualFill).clearEntries() + self.show_frame(ManualFill) + + def _on_timeout(self, uid): if uid == self._frame_uuid: - self.show_frame(MainPage) - self.ctx.traffic_light.set_off() + self.back_to_main() diff --git a/src/controllers/swipe_controller.py b/src/controllers/swipe_controller.py index 11193e3..308d043 100644 --- a/src/controllers/swipe_controller.py +++ b/src/controllers/swipe_controller.py @@ -4,8 +4,7 @@ from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe from screens.waiver_no_acc_swipe import WaiverNoAccSwipe from screens.check_in_no_id import CheckInNoId -from get_info_from_pid import contact_client -from utils import Utils +from api.get_info_from_pid import contact_client class SwipeController: @@ -14,8 +13,14 @@ def __init__(self, ctx): self._id_string = "" self._swipe_error_shown = False + def _id_vet(self, id_check): + if any(i.isalpha() for i in id_check): + return "bad" + if len(id_check) >= 16: + return "bad" + return "good" + def keyboardPress(self, key): - util = Utils() curr_frame = self.ctx.nav.get_curr_frame() if curr_frame not in (NoAccNoWaiverSwipe, WaiverNoAccSwipe, CheckInNoId): @@ -25,7 +30,7 @@ def keyboardPress(self, key): logging.debug("The array is now: " + repr(str(self._id_string))) if self._id_string.endswith("\r"): - if util.IDVet(self._id_string) == "bad": + if self._id_vet(self._id_string) == "bad": self._id_string = "" if not self._swipe_error_shown: self._swipe_error_shown = True diff --git a/src/core/__init__.py b/src/core/__init__.py deleted file mode 100644 index 32f632f..0000000 --- a/src/core/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# This is empty to ensure the directory is recognized as a package. -# You can add any necessary imports or initializations here if needed. \ No newline at end of file diff --git a/src/core/handle_check_in.py b/src/core/handle_check_in.py deleted file mode 100644 index f2e4ea2..0000000 --- a/src/core/handle_check_in.py +++ /dev/null @@ -1,52 +0,0 @@ -import logging -from tkinter import Label -from core.write_checkin import write_checkin - - -def handle_check_in(ctx, tag): - result = ctx.sheets.checkin_by_uuid(tag) - status = result.get("status") - - def update_ui(): - from screens.main_page import MainPage - from screens.no_acc_no_waiver import NoAccNoWaiver - from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe - from screens.acc_no_waiver import AccNoWaiver - from screens.acc_no_waiver_swipe import AccNoWaiverSwipe - from screens.user_welcome import UserWelcome - - if status == "api_error": - logging.error("API error during check-in") - ctx.traffic_light.set_red() - error_label = Label( - ctx.window.canvas, - text="System error, please let staff know.", - bg="#153246", fg="white", font=("Arial", 25), - ) - error_label.place(relx=0.5, rely=0.1, anchor="center") - error_label.after(4000, error_label.destroy) - return - - if status == "no_account": - logging.info(f"User {tag} not found.") - ctx.traffic_light.set_red() - ctx.nav.show_frame(NoAccNoWaiver) - ctx.window.after(3000, lambda: ctx.nav.show_frame(NoAccNoWaiverSwipe)) - return - - if status == "no_waiver": - logging.info(f"User {tag} does not have waiver.") - ctx.traffic_light.set_yellow() - ctx.nav.show_frame(AccNoWaiver) - ctx.window.after(3000, lambda: ctx.nav.show_frame(AccNoWaiverSwipe)) - return - - logging.info(f"User found: {result['name']}") - ctx.traffic_light.set_green() - ctx.nav.get_frame(UserWelcome).displayName(result["name"]) - write_checkin({ - "Name": result["name"], - "Student ID": result["student_id"], - }, tag) - - ctx.window.after(0, update_ui) diff --git a/src/core/write_checkin.py b/src/core/write_checkin.py deleted file mode 100644 index 720dc98..0000000 --- a/src/core/write_checkin.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import json -from datetime import datetime as dt - -LOG_BASE_PATH = "assets/logs" - -def write_checkin(curr_user, tag): - now = dt.now() - year = now.strftime("%Y") - month = now.strftime("%m") - day = now.strftime("%d") - timestamp = now.isoformat(timespec="seconds") - - log_dir = os.path.join(LOG_BASE_PATH, year, month) - os.makedirs(log_dir, exist_ok=True) - - log_path = os.path.join(log_dir, f"{day}.log") - log_entry = { - "tag": tag, - "name": curr_user.get("Name", ""), - "pid": curr_user.get("Student ID", ""), - "timestamp": timestamp, - } - - with open(log_path, "a", encoding="utf-8") as f: - json.dump(log_entry, f) - f.write("\n") \ No newline at end of file diff --git a/src/hardware/__init__.py b/src/hardware/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/reader.py b/src/hardware/reader.py similarity index 100% rename from src/reader.py rename to src/hardware/reader.py diff --git a/src/core/render_ports.py b/src/hardware/render_ports.py similarity index 100% rename from src/core/render_ports.py rename to src/hardware/render_ports.py diff --git a/src/traffic.py b/src/hardware/traffic.py similarity index 100% rename from src/traffic.py rename to src/hardware/traffic.py diff --git a/src/main.py b/src/main.py index 06a0009..56ada31 100644 --- a/src/main.py +++ b/src/main.py @@ -1,123 +1,22 @@ -from tkinter import Label from window import CheckInWindow from controllers.navigation_controller import NavigationController -from reader import * from controllers.swipe_controller import SwipeController -from threading import Thread -from screens.main_page import MainPage +from controllers.check_in_controller import CheckInController +from controllers.account_controller import AccountController +from controllers.card_reader_controller import CardReaderController +from hardware.reader import Reader from screens.manual_fill import ManualFill from screens.check_in_no_id import CheckInNoId -from utils import Utils -from core.handle_check_in import handle_check_in -from core.render_ports import get_usb_ids +from hardware.render_ports import get_usb_ids from app_context import AppContext -from sheets import check_api_health -import socket +from api.sheets import check_api_health import logging import argparse from sys import stdout -def is_connected(host="8.8.8.8", port=53, timeout=3): - try: - socket.setdefaulttimeout(timeout) - socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port)) - return True - except socket.error: - return False - - -no_wifi_shown = False - - -def myLoop(ctx: AppContext, reader): - global no_wifi_shown, no_wifi - logging.info("Now reading ID cards") - last_tag = 0 - last_time = 0 - scanner_error = False - while True: - if scanner_error: - time.sleep(0.1) - if reader.reconnect(): - logging.info("Card reader reconnected") - scanner_error = False - continue - - try: - in_waiting = reader.getSerInWaiting() - except OSError as e: - if not scanner_error: - logging.error("Card reader disconnected, disabling until reconnection: %s", e) - scanner_error = True - continue - - if in_waiting >= 14: - if not is_connected(): - logging.info("ERROR wifi is not connected") - if not no_wifi_shown: - no_wifi_shown = True - no_wifi = Label( - ctx.window.canvas, - text="ERROR! Connection cannot be established, please let staff know.", - bg="#153246", fg="white", font=("Arial", 25), - ) - no_wifi.place(relx=0.5, rely=0.1, anchor="center") - no_wifi.after(4000, lambda: destroyNoWifiError(no_wifi)) - continue - - ctx.nav.get_frame(ManualFill).clearEntries() - tag = reader.grabRFID() - - if " " in tag: - continue - - if tag == last_tag and not reader.canScanAgain(last_time): - logging.debug("Suppressing repeat scan") - continue - - s_reason = reader.checkRFID(tag) - - if s_reason != "good": - logging.debug(s_reason) - continue - else: - logging.debug("RFID Check Succeeded") - - ctx.rfid = tag - handle_check_in(ctx, tag) - - last_tag = tag - last_time = time.time() - - -def trafficLightPoller(ctx: AppContext): - last_color = None - light = ctx.traffic_light._light - while True: - time.sleep(0.1) - color = ctx.sheets.get_traffic_light() - if color != last_color: - last_color = color - if color == "red": - light.set_red() - elif color == "green": - light.set_green() - elif color == "yellow": - light.set_yellow() - else: - light.set_off() - - -def destroyNoWifiError(no_wifi): - global no_wifi_shown - no_wifi.destroy() - no_wifi_shown = False - - def clearAndReturn(ctx: AppContext): - ctx.traffic_light.set_off() - ctx.nav.show_frame(MainPage) + ctx.nav.back_to_main() ctx.nav.get_frame(ManualFill).clearEntries() ctx.nav.get_frame(CheckInNoId).clearEntries() @@ -142,16 +41,15 @@ def clearAndReturn(ctx: AppContext): nav = NavigationController(window, ctx) ctx.window = window ctx.nav = nav + ctx.check_in = CheckInController(ctx) + ctx.account = AccountController(ctx) ctx.traffic_light.set_off() sw = SwipeController(ctx) reader = Reader(reader_usb_id) - thread = Thread(target=myLoop, args=(ctx, reader)) - logging.info("Starting thread") - thread.start() - if ctx.traffic_light.connected: - poller = Thread(target=trafficLightPoller, args=(ctx,), daemon=True) - poller.start() + card_reader = CardReaderController(ctx) + card_reader.start(reader) + window.bind("", lambda i: sw.keyboardPress(i)) window.bind("", lambda i: clearAndReturn(ctx)) logging.info("Made it to app start") diff --git a/src/screens/acc_no_waiver_swipe.py b/src/screens/acc_no_waiver_swipe.py index fe85e28..70ed8d0 100644 --- a/src/screens/acc_no_waiver_swipe.py +++ b/src/screens/acc_no_waiver_swipe.py @@ -47,6 +47,4 @@ def _build(self, controller): self._window(875.0, 581.0, btn, width=344, height=71) def _back_to_main(self): - from .main_page import MainPage - self.controller.ctx.traffic_light.set_off() - self.controller.show_frame(MainPage) + self.controller.back_to_main() diff --git a/src/screens/check_in_no_id.py b/src/screens/check_in_no_id.py index 5d53898..0e95262 100644 --- a/src/screens/check_in_no_id.py +++ b/src/screens/check_in_no_id.py @@ -62,12 +62,6 @@ def updateEntries(self, pid): self.pid_entry.insert(0, pid) def _call_check_in(self, controller): - from .no_acc_check_in_only import NoAccCheckInOnly - from .no_acc_no_waiver_swipe import NoAccNoWaiverSwipe - from .user_welcome import UserWelcome - from .acc_no_waiver import AccNoWaiver - from .main_page import MainPage - pid = self.pid_entry.get() if not pid: return @@ -76,25 +70,8 @@ def _call_check_in(self, controller): self.canvas.update_idletasks() self.clearEntries() - result = self.controller.ctx.sheets.checkin_by_pid(pid) - status = result.get("status") + self.controller.ctx.check_in.handle_by_pid(pid) if self.loading_text_id is not None: self.canvas.delete(self.loading_text_id) self.loading_text_id = None - - if status == "no_account": - logging.info("Manual check-in: user account not found") - controller.show_frame(NoAccCheckInOnly) - controller.after(5000, lambda: controller.show_frame(MainPage)) - return - - if status == "no_waiver": - logging.info(f"Manual check-in: no waiver for {result.get('name', pid)}") - controller.show_frame(AccNoWaiver) - controller.after(3000, lambda: controller.show_frame(NoAccNoWaiverSwipe)) - return - - logging.info(f"Manual check-in for {result['name']}") - self.controller.ctx.traffic_light.set_green() - self.controller.get_frame(UserWelcome).displayName(result["name"]) diff --git a/src/screens/main_page.py b/src/screens/main_page.py index b14d1a9..ecedd04 100644 --- a/src/screens/main_page.py +++ b/src/screens/main_page.py @@ -46,7 +46,4 @@ def _build(self, controller): self._window(1130.0, 40.0, btn2) def _go_to_no_id(self, controller): - from .check_in_no_id import CheckInNoId - no_id = controller.get_frame(CheckInNoId) - no_id.clearEntries() - controller.show_frame(CheckInNoId) + controller.go_to_no_id() diff --git a/src/screens/manual_fill.py b/src/screens/manual_fill.py index 2c2fa17..51d6442 100644 --- a/src/screens/manual_fill.py +++ b/src/screens/manual_fill.py @@ -1,7 +1,6 @@ from pathlib import Path from tkinter import Button, Entry, StringVar, END from .screen import Screen -from utils import Utils import logging import timeit @@ -109,12 +108,11 @@ def updateEntries(self, fname, lname, email, pid): self.pid_entry.insert(0, pid) def _call_account_creation(self): - util = Utils() data = self.getEntries() self.clearEntries() try: delay = timeit.timeit( - lambda: util.createAccount(self.controller.ctx, data[0], data[1], data[2], data[3], ManualFill), + lambda: self.controller.ctx.account.create_account(data[0], data[1], data[2], data[3]), number=1, ) logging.debug(f"Time to create account: {delay}") diff --git a/src/screens/no_acc_no_waiver_swipe.py b/src/screens/no_acc_no_waiver_swipe.py index 8ddeb3d..0deadc8 100644 --- a/src/screens/no_acc_no_waiver_swipe.py +++ b/src/screens/no_acc_no_waiver_swipe.py @@ -41,6 +41,4 @@ def _build(self, controller): self._window(465.0, 554.0, btn, width=349, height=71) def _go_to_manual_fill(self, controller): - from .manual_fill import ManualFill - controller.get_frame(ManualFill).clearEntries() - controller.show_frame(ManualFill) + controller.go_to_manual_fill() diff --git a/src/screens/user_thank.py b/src/screens/user_thank.py index 65b1c20..2946c87 100644 --- a/src/screens/user_thank.py +++ b/src/screens/user_thank.py @@ -19,13 +19,8 @@ def hide(self): self.canvas.delete("thank") def displayName(self, name, nextPage): - from .main_page import MainPage self.controller.show_frame(UserThank) - - if nextPage == MainPage: - self.controller.ctx.traffic_light.set_green() - else: - self.controller.ctx.traffic_light.set_yellow() + self.controller.ctx.account.on_thank_start(nextPage) self.canvas.create_text( 99.0, 323.0, anchor="nw", @@ -39,7 +34,4 @@ def displayName(self, name, nextPage): self.controller.after(4000, lambda: self._go_to_next(nextPage)) def _go_to_next(self, nextPage): - from .main_page import MainPage - self.controller.show_frame(nextPage) - if nextPage == MainPage: - self.controller.ctx.traffic_light.set_off() + self.controller.ctx.account.on_thank_done(nextPage) diff --git a/src/screens/user_welcome.py b/src/screens/user_welcome.py index 82d9da7..689f91e 100644 --- a/src/screens/user_welcome.py +++ b/src/screens/user_welcome.py @@ -25,7 +25,6 @@ def displayName(self, name): self.last_name = name - from .main_page import MainPage self.controller.show_frame(UserWelcome) text_id = self.canvas.create_text( @@ -42,7 +41,6 @@ def displayName(self, name): self.canvas.after(3000, lambda: self._remove_name(text_id)) def _remove_name(self, text_id): - from .main_page import MainPage self.canvas.delete(text_id) self.offset -= 73 @@ -53,5 +51,4 @@ def _remove_name(self, text_id): if not self.canvas.find_withtag("welcome"): self.last_name = None - self.controller.ctx.traffic_light.set_off() - self.controller.show_frame(MainPage) + self.controller.back_to_main() diff --git a/src/screens/waiver_no_acc_swipe.py b/src/screens/waiver_no_acc_swipe.py index 5f8e0c2..b449a28 100644 --- a/src/screens/waiver_no_acc_swipe.py +++ b/src/screens/waiver_no_acc_swipe.py @@ -41,6 +41,4 @@ def _build(self, controller): self._window(465.0, 554.0, btn, width=349, height=71) def _go_to_manual_fill(self, controller): - from .manual_fill import ManualFill - controller.get_frame(ManualFill).clearEntries() - controller.show_frame(ManualFill) + controller.go_to_manual_fill() From 8971b7d93ee0e65fedf6760c949e1359035a0697 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 17:15:27 -0700 Subject: [PATCH 08/14] traffic light api refactoring, remove dynamic imports --- src/api/traffic_light_api.py | 33 ++++++++++++++++++++++ src/app_context.py | 36 +++--------------------- src/controllers/account_controller.py | 2 -- src/controllers/check_in_controller.py | 21 ++++++-------- src/controllers/navigation_controller.py | 31 +++++++++----------- src/screens/check_in_no_id.py | 5 ---- src/screens/main_page.py | 4 +-- src/screens/qr_codes.py | 4 +-- 8 files changed, 62 insertions(+), 74 deletions(-) create mode 100644 src/api/traffic_light_api.py diff --git a/src/api/traffic_light_api.py b/src/api/traffic_light_api.py new file mode 100644 index 0000000..e2896f1 --- /dev/null +++ b/src/api/traffic_light_api.py @@ -0,0 +1,33 @@ +import threading + +from api.sheets import SheetManager +from hardware.traffic import TrafficLight + + +class TrafficLightApi: + def __init__(self, light: TrafficLight, sheets: SheetManager): + self._light = light + self._sheets = sheets + + @property + def connected(self) -> bool: + return self._light.ser is not None + + def _post(self, color: str) -> None: + threading.Thread( + target=self._sheets.set_traffic_light, + args=(color,), + daemon=True, + ).start() + + def set_red(self) -> None: + self._post("red") + + def set_green(self) -> None: + self._post("green") + + def set_yellow(self) -> None: + self._post("yellow") + + def set_off(self) -> None: + self._post("off") diff --git a/src/app_context.py b/src/app_context.py index 19782c1..c8e6cfd 100644 --- a/src/app_context.py +++ b/src/app_context.py @@ -1,40 +1,12 @@ import threading from api.sheets import SheetManager +from api.traffic_light_api import TrafficLightApi from hardware.traffic import TrafficLight -class _TrafficProxy: - def __init__(self, light: TrafficLight, sheets: SheetManager): - self._light = light - self._sheets = sheets - - @property - def connected(self) -> bool: - return self._light.ser is not None - - def _post(self, color: str) -> None: - threading.Thread( - target=self._sheets.set_traffic_light, - args=(color,), - daemon=True, - ).start() - - def set_red(self) -> None: - self._post("red") - - def set_green(self) -> None: - self._post("green") - - def set_yellow(self) -> None: - self._post("yellow") - - def set_off(self) -> None: - self._post("off") - - class AppContext: - def __init__(self, sheets: SheetManager, traffic_light: _TrafficProxy): + def __init__(self, sheets: SheetManager, traffic_light: TrafficLightApi): self.sheets = sheets self.traffic_light = traffic_light self.window = None @@ -58,5 +30,5 @@ def rfid(self, value: str) -> None: def create(cls, traffic_usb_id=None) -> "AppContext": sheets = SheetManager() light = TrafficLight(traffic_usb_id) - proxy = _TrafficProxy(light, sheets) - return cls(sheets, proxy) + traffic = TrafficLightApi(light, sheets) + return cls(sheets, traffic) diff --git a/src/controllers/account_controller.py b/src/controllers/account_controller.py index 7a09d59..3f10be7 100644 --- a/src/controllers/account_controller.py +++ b/src/controllers/account_controller.py @@ -104,14 +104,12 @@ def create_account(self, fname, lname, email, pid): inProgress.destroy() def on_thank_start(self, next_page): - from screens.main_page import MainPage if next_page == MainPage: self.ctx.traffic_light.set_green() else: self.ctx.traffic_light.set_yellow() def on_thank_done(self, next_page): - from screens.main_page import MainPage self.ctx.nav.show_frame(next_page) if next_page == MainPage: self.ctx.traffic_light.set_off() diff --git a/src/controllers/check_in_controller.py b/src/controllers/check_in_controller.py index dd58140..1a9856f 100644 --- a/src/controllers/check_in_controller.py +++ b/src/controllers/check_in_controller.py @@ -1,6 +1,14 @@ import logging from tkinter import Label +from screens.main_page import MainPage +from screens.no_acc_no_waiver import NoAccNoWaiver +from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe +from screens.no_acc_check_in_only import NoAccCheckInOnly +from screens.acc_no_waiver import AccNoWaiver +from screens.acc_no_waiver_swipe import AccNoWaiverSwipe +from screens.user_welcome import UserWelcome + class CheckInController: def __init__(self, ctx): @@ -11,13 +19,6 @@ def handle_by_uuid(self, tag): status = result.get("status") def update_ui(): - from screens.main_page import MainPage - from screens.no_acc_no_waiver import NoAccNoWaiver - from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe - from screens.acc_no_waiver import AccNoWaiver - from screens.acc_no_waiver_swipe import AccNoWaiverSwipe - from screens.user_welcome import UserWelcome - if status == "api_error": logging.error("API error during check-in") self.ctx.traffic_light.set_red() @@ -51,12 +52,6 @@ def update_ui(): self.ctx.window.after(0, update_ui) def handle_by_pid(self, pid): - from screens.no_acc_check_in_only import NoAccCheckInOnly - from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe - from screens.user_welcome import UserWelcome - from screens.acc_no_waiver import AccNoWaiver - from screens.main_page import MainPage - result = self.ctx.sheets.checkin_by_pid(pid) status = result.get("status") diff --git a/src/controllers/navigation_controller.py b/src/controllers/navigation_controller.py index bbd6ec3..ea4dc7e 100644 --- a/src/controllers/navigation_controller.py +++ b/src/controllers/navigation_controller.py @@ -1,5 +1,19 @@ import uuid +from screens.main_page import MainPage +from screens.acc_no_waiver import AccNoWaiver +from screens.acc_no_waiver_swipe import AccNoWaiverSwipe +from screens.manual_fill import ManualFill +from screens.check_in_no_id import CheckInNoId +from screens.no_acc_check_in_only import NoAccCheckInOnly +from screens.no_acc_no_waiver import NoAccNoWaiver +from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe +from screens.qr_codes import QRCodes +from screens.user_thank import UserThank +from screens.user_welcome import UserWelcome +from screens.waiver_no_acc import WaiverNoAcc +from screens.waiver_no_acc_swipe import WaiverNoAccSwipe + class NavigationController: def __init__(self, window, ctx): @@ -9,20 +23,6 @@ def __init__(self, window, ctx): self._curr = None self._frame_uuid = uuid.uuid4().hex - from screens.main_page import MainPage - from screens.acc_no_waiver import AccNoWaiver - from screens.acc_no_waiver_swipe import AccNoWaiverSwipe - from screens.manual_fill import ManualFill - from screens.check_in_no_id import CheckInNoId - from screens.no_acc_check_in_only import NoAccCheckInOnly - from screens.no_acc_no_waiver import NoAccNoWaiver - from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe - from screens.qr_codes import QRCodes - from screens.user_thank import UserThank - from screens.user_welcome import UserWelcome - from screens.waiver_no_acc import WaiverNoAcc - from screens.waiver_no_acc_swipe import WaiverNoAccSwipe - self._timeouts = { AccNoWaiverSwipe: 30000, QRCodes: 30000, @@ -72,17 +72,14 @@ def after(self, ms, fn): self._window.after(ms, fn) def back_to_main(self): - from screens.main_page import MainPage self.ctx.traffic_light.set_off() self.show_frame(MainPage) def go_to_no_id(self): - from screens.check_in_no_id import CheckInNoId self.get_frame(CheckInNoId).clearEntries() self.show_frame(CheckInNoId) def go_to_manual_fill(self): - from screens.manual_fill import ManualFill self.get_frame(ManualFill).clearEntries() self.show_frame(ManualFill) diff --git a/src/screens/check_in_no_id.py b/src/screens/check_in_no_id.py index 0e95262..4a62fdb 100644 --- a/src/screens/check_in_no_id.py +++ b/src/screens/check_in_no_id.py @@ -12,11 +12,6 @@ class CheckInNoId(Screen): def _build(self, controller): - from .no_acc_check_in_only import NoAccCheckInOnly - from .no_acc_no_waiver_swipe import NoAccNoWaiverSwipe - from .user_welcome import UserWelcome - from .acc_no_waiver import AccNoWaiver - self.loading_text_id = None self.pid = StringVar() diff --git a/src/screens/main_page.py b/src/screens/main_page.py index ecedd04..85c79d2 100644 --- a/src/screens/main_page.py +++ b/src/screens/main_page.py @@ -1,13 +1,13 @@ from pathlib import Path from tkinter import Button from .screen import Screen +from .qr_codes import QRCodes + ASSETS_PATH = Path(__file__).parent.parent / "assets" / "main_page_assets" class MainPage(Screen): def _build(self, controller): - from .qr_codes import QRCodes - from .check_in_no_id import CheckInNoId logo = self._photo(ASSETS_PATH / "image_3.png") self._image(88.0, 90.0, image=logo) diff --git a/src/screens/qr_codes.py b/src/screens/qr_codes.py index e9cd16c..a25d1e4 100644 --- a/src/screens/qr_codes.py +++ b/src/screens/qr_codes.py @@ -7,8 +7,6 @@ class QRCodes(Screen): def _build(self, controller): - from .main_page import MainPage - img3 = self._photo(ASSETS_PATH / "image_3.png") self._image(88.0, 90.0, image=img3) @@ -30,7 +28,7 @@ def _build(self, controller): btn_img = self._photo(ASSETS_PATH / "image_6.png") btn = Button( self.canvas, image=btn_img, bg="#153246", - command=lambda: controller.show_frame(MainPage), + command=lambda: controller.back_to_main(), relief="flat", ) self._window(53.0, 55.0, btn) From 430f8b06b1bf55a35246b8750c08b441bf91f5ce Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 17:17:34 -0700 Subject: [PATCH 09/14] refactor method names --- src/controllers/card_reader_controller.py | 8 ++++---- src/hardware/reader.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/controllers/card_reader_controller.py b/src/controllers/card_reader_controller.py index 67e03ea..5908334 100644 --- a/src/controllers/card_reader_controller.py +++ b/src/controllers/card_reader_controller.py @@ -32,7 +32,7 @@ def _run(self, reader): continue try: - in_waiting = reader.getSerInWaiting() + in_waiting = reader.get_ser_in_waiting() except OSError as e: if not scanner_error: logging.error("Card reader disconnected, disabling until reconnection: %s", e) @@ -54,16 +54,16 @@ def _run(self, reader): continue self.ctx.nav.get_frame(ManualFill).clearEntries() - tag = reader.grabRFID() + tag = reader.grab_rfid() if " " in tag: continue - if tag == last_tag and not reader.canScanAgain(last_time): + if tag == last_tag and not reader.can_scan_again(last_time): logging.debug("Suppressing repeat scan") continue - s_reason = reader.checkRFID(tag) + s_reason = reader.check_rfid(tag) if s_reason != "good": logging.debug(s_reason) diff --git a/src/hardware/reader.py b/src/hardware/reader.py index 7b83ea5..06bfa6b 100644 --- a/src/hardware/reader.py +++ b/src/hardware/reader.py @@ -40,7 +40,7 @@ def reconnect(self): self._pn532 = None return False - def getSerInWaiting(self): + def get_ser_in_waiting(self): try: uid = self._pn532.read_passive_target(timeout=0.1) except Exception as e: @@ -53,16 +53,16 @@ def getSerInWaiting(self): self._pending_tag = None return 0 - def grabRFID(self): + def grab_rfid(self): tag = self._pending_tag self._pending_tag = None logging.info("Parsed tag: " + str(tag)) return str(tag) - def checkRFID(self, tag): + def check_rfid(self, tag): if not tag or len(tag) != expected_characters: return "Tag was not the expected number of chars" return "good" - def canScanAgain(self, lastTime): + def can_scan_again(self, lastTime): return time.time() - lastTime > 3 From 738e436d4f098854f299f737ab85b74c0846edc8 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 17:46:57 -0700 Subject: [PATCH 10/14] refactoring --- src/api/_client.py | 12 +++++++ src/api/get_info_from_pid.py | 25 +++++++------- src/api/sheets.py | 12 ++----- src/api/traffic_light_api.py | 21 +++++++++--- src/controllers/account_controller.py | 8 ++--- src/controllers/card_reader_controller.py | 12 ++----- src/controllers/check_in_controller.py | 14 ++++---- src/controllers/navigation_controller.py | 6 ++-- src/controllers/swipe_controller.py | 34 +++++++++--------- src/hardware/render_ports.py | 20 +++++++---- src/hardware/traffic.py | 4 +++ src/main.py | 18 +++++----- src/screens/check_in_no_id.py | 10 +++--- src/screens/manual_fill.py | 42 +++++++++-------------- src/screens/user_thank.py | 2 +- src/screens/user_welcome.py | 2 +- 16 files changed, 126 insertions(+), 116 deletions(-) create mode 100644 src/api/_client.py diff --git a/src/api/_client.py b/src/api/_client.py new file mode 100644 index 0000000..a1f848e --- /dev/null +++ b/src/api/_client.py @@ -0,0 +1,12 @@ +import logging +import time + +import requests + + +def _req(method, url, **kwargs): + start = time.time() + resp = requests.request(method, url, **kwargs) + ms = (time.time() - start) * 1000 + logging.info(f"[CLIENT] {method.upper()} {url} -> {resp.status_code} ({ms:.0f}ms)") + return resp diff --git a/src/api/get_info_from_pid.py b/src/api/get_info_from_pid.py index 76b4924..af28c96 100644 --- a/src/api/get_info_from_pid.py +++ b/src/api/get_info_from_pid.py @@ -1,27 +1,28 @@ import logging -import time - -import requests +from dataclasses import dataclass from config import API_BASE_URL +from api._client import _req -def _req(method, url, **kwargs): - start = time.time() - resp = requests.request(method, url, **kwargs) - ms = (time.time() - start) * 1000 - logging.info(f"[CLIENT] {method.upper()} {url} -> {resp.status_code} ({ms:.0f}ms)") - return resp +@dataclass +class StudentInfo: + first_name: str + last_name: str + emails: list + pid: str + first_enr_term: str + last_enr_term: str -class contact_client: +class ContactClient: def get_student_info(self, barcode): try: resp = _req("GET", f"{API_BASE_URL}/students/barcode/{barcode}", timeout=5) if not resp.ok: return False d = resp.json() - return [d["first_name"], d["last_name"], d["emails"], d["pid"], d["first_enr_term"], d["last_enr_term"]] + return StudentInfo(d["first_name"], d["last_name"], d["emails"], d["pid"], d["first_enr_term"], d["last_enr_term"]) except Exception as e: logging.error(f"Error fetching student by barcode: {e}") return False @@ -32,7 +33,7 @@ def get_student_info_pid(self, pid): if not resp.ok: return False d = resp.json() - return [d["first_name"], d["last_name"], d["emails"], d["pid"], d["first_enr_term"], d["last_enr_term"]] + return StudentInfo(d["first_name"], d["last_name"], d["emails"], d["pid"], d["first_enr_term"], d["last_enr_term"]) except Exception as e: logging.error(f"Error fetching student by pid: {e}") return False diff --git a/src/api/sheets.py b/src/api/sheets.py index 307f2af..7bc3b67 100644 --- a/src/api/sheets.py +++ b/src/api/sheets.py @@ -2,18 +2,10 @@ import sys import time -import requests - from config import API_BASE_URL +from api._client import _req -def _req(method, url, **kwargs): - start = time.time() - resp = requests.request(method, url, **kwargs) - ms = (time.time() - start) * 1000 - logging.info(f"[CLIENT] {method.upper()} {url} -> {resp.status_code} ({ms:.0f}ms)") - return resp - def check_api_health(retries=3, delay=3): logging.info(API_BASE_URL) @@ -58,7 +50,7 @@ def set_traffic_light(self, color): def get_traffic_light(self): try: - resp = requests.get(f"{API_BASE_URL}/traffic-light", timeout=5) + resp = _req("GET", f"{API_BASE_URL}/traffic-light", timeout=5) return resp.json().get("color", "off") except Exception as e: logging.error(f"Error getting traffic light: {e}") diff --git a/src/api/traffic_light_api.py b/src/api/traffic_light_api.py index e2896f1..da94ce9 100644 --- a/src/api/traffic_light_api.py +++ b/src/api/traffic_light_api.py @@ -11,7 +11,18 @@ def __init__(self, light: TrafficLight, sheets: SheetManager): @property def connected(self) -> bool: - return self._light.ser is not None + return self._light.connected + + def drive(self, color: str) -> None: + """Directly set the physical traffic light without posting to the API.""" + if color == "red": + self._light.set_red() + elif color == "green": + self._light.set_green() + elif color == "yellow": + self._light.set_yellow() + else: + self._light.set_off() def _post(self, color: str) -> None: threading.Thread( @@ -20,14 +31,14 @@ def _post(self, color: str) -> None: daemon=True, ).start() - def set_red(self) -> None: + def request_red(self) -> None: self._post("red") - def set_green(self) -> None: + def request_green(self) -> None: self._post("green") - def set_yellow(self) -> None: + def request_yellow(self) -> None: self._post("yellow") - def set_off(self) -> None: + def request_off(self) -> None: self._post("off") diff --git a/src/controllers/account_controller.py b/src/controllers/account_controller.py index 3f10be7..ed851c3 100644 --- a/src/controllers/account_controller.py +++ b/src/controllers/account_controller.py @@ -100,16 +100,16 @@ def create_account(self, fname, lname, email, pid): end5 = time.perf_counter() logging.debug(f"Time to check waiver via check-in: {end5 - end4}") - self.ctx.nav.get_frame(UserThank).displayName(full_name, toGoTo) + self.ctx.nav.get_frame(UserThank).display_name(full_name, toGoTo) inProgress.destroy() def on_thank_start(self, next_page): if next_page == MainPage: - self.ctx.traffic_light.set_green() + self.ctx.traffic_light.request_green() else: - self.ctx.traffic_light.set_yellow() + self.ctx.traffic_light.request_yellow() def on_thank_done(self, next_page): self.ctx.nav.show_frame(next_page) if next_page == MainPage: - self.ctx.traffic_light.set_off() + self.ctx.traffic_light.request_off() diff --git a/src/controllers/card_reader_controller.py b/src/controllers/card_reader_controller.py index 5908334..4a1472f 100644 --- a/src/controllers/card_reader_controller.py +++ b/src/controllers/card_reader_controller.py @@ -53,7 +53,7 @@ def _run(self, reader): no_wifi.after(4000, lambda: self._destroy_wifi_error(no_wifi)) continue - self.ctx.nav.get_frame(ManualFill).clearEntries() + self.ctx.nav.get_frame(ManualFill).clear_entries() tag = reader.grab_rfid() if " " in tag: @@ -79,20 +79,12 @@ def _run(self, reader): def _poll_traffic_light(self): last_color = None - light = self.ctx.traffic_light._light while True: time.sleep(0.1) color = self.ctx.sheets.get_traffic_light() if color != last_color: last_color = color - if color == "red": - light.set_red() - elif color == "green": - light.set_green() - elif color == "yellow": - light.set_yellow() - else: - light.set_off() + self.ctx.traffic_light.drive(color) def _is_connected(self, host="8.8.8.8", port=53, timeout=3): try: diff --git a/src/controllers/check_in_controller.py b/src/controllers/check_in_controller.py index 1a9856f..c4cd557 100644 --- a/src/controllers/check_in_controller.py +++ b/src/controllers/check_in_controller.py @@ -21,7 +21,7 @@ def handle_by_uuid(self, tag): def update_ui(): if status == "api_error": logging.error("API error during check-in") - self.ctx.traffic_light.set_red() + self.ctx.traffic_light.request_red() error_label = Label( self.ctx.window.canvas, text="System error, please let staff know.", @@ -33,21 +33,21 @@ def update_ui(): if status == "no_account": logging.info(f"User {tag} not found.") - self.ctx.traffic_light.set_red() + self.ctx.traffic_light.request_red() self.ctx.nav.show_frame(NoAccNoWaiver) self.ctx.window.after(3000, lambda: self.ctx.nav.show_frame(NoAccNoWaiverSwipe)) return if status == "no_waiver": logging.info(f"User {tag} does not have waiver.") - self.ctx.traffic_light.set_yellow() + self.ctx.traffic_light.request_yellow() self.ctx.nav.show_frame(AccNoWaiver) self.ctx.window.after(3000, lambda: self.ctx.nav.show_frame(AccNoWaiverSwipe)) return logging.info(f"User found: {result['name']}") - self.ctx.traffic_light.set_green() - self.ctx.nav.get_frame(UserWelcome).displayName(result["name"]) + self.ctx.traffic_light.request_green() + self.ctx.nav.get_frame(UserWelcome).display_name(result["name"]) self.ctx.window.after(0, update_ui) @@ -68,5 +68,5 @@ def handle_by_pid(self, pid): return logging.info(f"Manual check-in for {result['name']}") - self.ctx.traffic_light.set_green() - self.ctx.nav.get_frame(UserWelcome).displayName(result["name"]) + self.ctx.traffic_light.request_green() + self.ctx.nav.get_frame(UserWelcome).display_name(result["name"]) diff --git a/src/controllers/navigation_controller.py b/src/controllers/navigation_controller.py index ea4dc7e..8389678 100644 --- a/src/controllers/navigation_controller.py +++ b/src/controllers/navigation_controller.py @@ -72,15 +72,15 @@ def after(self, ms, fn): self._window.after(ms, fn) def back_to_main(self): - self.ctx.traffic_light.set_off() + self.ctx.traffic_light.request_off() self.show_frame(MainPage) def go_to_no_id(self): - self.get_frame(CheckInNoId).clearEntries() + self.get_frame(CheckInNoId).clear_entries() self.show_frame(CheckInNoId) def go_to_manual_fill(self): - self.get_frame(ManualFill).clearEntries() + self.get_frame(ManualFill).clear_entries() self.show_frame(ManualFill) def _on_timeout(self, uid): diff --git a/src/controllers/swipe_controller.py b/src/controllers/swipe_controller.py index 308d043..c4b00eb 100644 --- a/src/controllers/swipe_controller.py +++ b/src/controllers/swipe_controller.py @@ -4,7 +4,7 @@ from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe from screens.waiver_no_acc_swipe import WaiverNoAccSwipe from screens.check_in_no_id import CheckInNoId -from api.get_info_from_pid import contact_client +from api.get_info_from_pid import ContactClient class SwipeController: @@ -20,7 +20,7 @@ def _id_vet(self, id_check): return "bad" return "good" - def keyboardPress(self, key): + def keyboard_press(self, key): curr_frame = self.ctx.nav.get_curr_frame() if curr_frame not in (NoAccNoWaiverSwipe, WaiverNoAccSwipe, CheckInNoId): @@ -39,16 +39,16 @@ def keyboardPress(self, key): bg="#153246", fg="white", font=("Arial", 20), ) id_error.place(relx=0.5, rely=0.85, anchor="center") - id_error.after(1500, lambda: self._destroySwipeError(id_error)) + id_error.after(1500, lambda: self._destroy_swipe_error(id_error)) return - self._swipeCard(self._id_string) + self._swipe_card(self._id_string) self._id_string = "" - def _pullUser(self, barcode, u_type): + def _pull_user(self, barcode, u_type): logging.info(f"Card barcode read is: {barcode}. Trying to pull user...") - contact = contact_client() + contact = ContactClient() try: if u_type == "Staff": u_info = contact.get_staff_info(barcode) @@ -62,32 +62,32 @@ def _pullUser(self, barcode, u_type): logging.info("Student search returned False, returning...") return None - logging.info(f"Info pull succeeded:\n {u_info[0]}, {u_info[1]}, {u_info[3]}") + logging.info(f"Info pull succeeded:\n {u_info.first_name}, {u_info.last_name}, {u_info.pid}") return u_info - def _swipeCard(self, id_string): - u_data = self._pullUser(id_string.strip(), "Student") + def _swipe_card(self, id_string): + u_data = self._pull_user(id_string.strip(), "Student") if not u_data: logging.info("Student search returned False, returning...") return if self.ctx.nav.get_curr_frame() == CheckInNoId: - self.ctx.nav.get_frame(CheckInNoId).clearEntries() - self.ctx.nav.get_frame(CheckInNoId).updateEntries(u_data[3]) + self.ctx.nav.get_frame(CheckInNoId).clear_entries() + self.ctx.nav.get_frame(CheckInNoId).update_entries(u_data.pid) return - email_to_use = "" if len(u_data[2]) == 0 else u_data[2][0] - for email in u_data[2]: + email_to_use = "" if len(u_data.emails) == 0 else u_data.emails[0] + for email in u_data.emails: if email.endswith("@ucsd.edu"): email_to_use = email manfill = self.ctx.nav.get_frame(ManualFill) - manfill.clearEntries() - logging.info(f"Filling data with {u_data[0]} {u_data[1]} {email_to_use} {u_data[3]}") - manfill.updateEntries(u_data[0], u_data[1], email_to_use, u_data[3]) + manfill.clear_entries() + logging.info(f"Filling data with {u_data.first_name} {u_data.last_name} {email_to_use} {u_data.pid}") + manfill.update_entries(u_data.first_name, u_data.last_name, email_to_use, u_data.pid) self.ctx.nav.show_frame(ManualFill) - def _destroySwipeError(self, id_error): + def _destroy_swipe_error(self, id_error): id_error.destroy() self._swipe_error_shown = False diff --git a/src/hardware/render_ports.py b/src/hardware/render_ports.py index 6609188..2ad7cca 100644 --- a/src/hardware/render_ports.py +++ b/src/hardware/render_ports.py @@ -1,16 +1,24 @@ +from dataclasses import dataclass + import serial.tools.list_ports SHARED_VID = 0x1A86 TRAFFIC_LOCATION = "1-1.1.2" -def get_usb_ids(): - reader_usb_id = None - traffic_usb_id = None +@dataclass +class UsbIds: + reader: str | None + traffic_light: str | None + + +def get_usb_ids() -> UsbIds: + reader = None + traffic_light = None for p in serial.tools.list_ports.comports(): if p.vid == SHARED_VID: if p.location == TRAFFIC_LOCATION: - traffic_usb_id = p.device + traffic_light = p.device else: - reader_usb_id = p.device - return reader_usb_id, traffic_usb_id + reader = p.device + return UsbIds(reader, traffic_light) diff --git a/src/hardware/traffic.py b/src/hardware/traffic.py index ace092a..6f3ce61 100644 --- a/src/hardware/traffic.py +++ b/src/hardware/traffic.py @@ -11,6 +11,10 @@ def __init__(self, addr=None, baud=115200): self.ser = serial.Serial(addr, baud) self.ser.reset_input_buffer() + @property + def connected(self) -> bool: + return self.ser is not None + def set_off(self): if self.ser: self.ser.write(b"off\n") diff --git a/src/main.py b/src/main.py index 56ada31..d7bb907 100644 --- a/src/main.py +++ b/src/main.py @@ -15,10 +15,10 @@ from sys import stdout -def clearAndReturn(ctx: AppContext): +def clear_and_return(ctx: AppContext): ctx.nav.back_to_main() - ctx.nav.get_frame(ManualFill).clearEntries() - ctx.nav.get_frame(CheckInNoId).clearEntries() + ctx.nav.get_frame(ManualFill).clear_entries() + ctx.nav.get_frame(CheckInNoId).clear_entries() if __name__ == "__main__": @@ -34,23 +34,23 @@ def clearAndReturn(ctx: AppContext): else: logging.basicConfig(level=logging.INFO) - reader_usb_id, traffic_usb_id = get_usb_ids() + usb = get_usb_ids() check_api_health() - ctx = AppContext.create(traffic_usb_id) + ctx = AppContext.create(usb.traffic_light) window = CheckInWindow() nav = NavigationController(window, ctx) ctx.window = window ctx.nav = nav ctx.check_in = CheckInController(ctx) ctx.account = AccountController(ctx) - ctx.traffic_light.set_off() + ctx.traffic_light.request_off() sw = SwipeController(ctx) - reader = Reader(reader_usb_id) + reader = Reader(usb.reader) card_reader = CardReaderController(ctx) card_reader.start(reader) - window.bind("", lambda i: sw.keyboardPress(i)) - window.bind("", lambda i: clearAndReturn(ctx)) + window.bind("", lambda i: sw.keyboard_press(i)) + window.bind("", lambda i: clear_and_return(ctx)) logging.info("Made it to app start") window.start() diff --git a/src/screens/check_in_no_id.py b/src/screens/check_in_no_id.py index 4a62fdb..135186d 100644 --- a/src/screens/check_in_no_id.py +++ b/src/screens/check_in_no_id.py @@ -42,7 +42,7 @@ def _build(self, controller): self.pid_entry = Entry(self.canvas, textvariable=self.pid, width=40, font=52) self._window(420.0, 412.0, self.pid_entry) - def displayLoading(self): + def display_loading(self): if self.loading_text_id is None: self.loading_text_id = self.canvas.create_text( 420.0, 545.0, anchor="nw", @@ -50,10 +50,10 @@ def displayLoading(self): fill="#FF0000", font=("Montserrat", 36 * -1, "bold"), justify="center", ) - def clearEntries(self): + def clear_entries(self): self.pid_entry.delete(0, END) - def updateEntries(self, pid): + def update_entries(self, pid): self.pid_entry.insert(0, pid) def _call_check_in(self, controller): @@ -61,9 +61,9 @@ def _call_check_in(self, controller): if not pid: return - self.displayLoading() + self.display_loading() self.canvas.update_idletasks() - self.clearEntries() + self.clear_entries() self.controller.ctx.check_in.handle_by_pid(pid) diff --git a/src/screens/manual_fill.py b/src/screens/manual_fill.py index 51d6442..a1145c9 100644 --- a/src/screens/manual_fill.py +++ b/src/screens/manual_fill.py @@ -2,7 +2,6 @@ from tkinter import Button, Entry, StringVar, END from .screen import Screen import logging -import timeit ASSETS_PATH = Path(__file__).parent.parent / "assets" / "manual_fill_assets" @@ -13,10 +12,10 @@ class ManualFill(Screen): def _build(self, controller): - self.first_name = StringVar() - self.last_name = StringVar() - self.email = StringVar() - self.pid = StringVar() + self.first_name_var = StringVar() + self.last_name_var = StringVar() + self.email_var = StringVar() + self.pid_var = StringVar() img2 = self._photo(ASSETS_PATH / "image_2.png") self._image(640.0, 76.0, image=img2) @@ -75,46 +74,37 @@ def _build(self, controller): ) self._window(465.0, 598.0, btn, width=349, height=71) - self.first_name_entry = Entry(self.canvas, textvariable=self.first_name, width=40, font=52) + self.first_name_entry = Entry(self.canvas, textvariable=self.first_name_var, width=40, font=52) self._window(420.0, 227.0, self.first_name_entry) - self.last_name_entry = Entry(self.canvas, textvariable=self.last_name, width=40, font=52) + self.last_name_entry = Entry(self.canvas, textvariable=self.last_name_var, width=40, font=52) self._window(420.0, 327.0, self.last_name_entry) - self.email_entry = Entry(self.canvas, textvariable=self.email, width=40, font=52) + self.email_entry = Entry(self.canvas, textvariable=self.email_var, width=40, font=52) self._window(420.0, 428.0, self.email_entry) - self.pid_entry = Entry(self.canvas, textvariable=self.pid, width=40, font=52) + self.pid_entry = Entry(self.canvas, textvariable=self.pid_var, width=40, font=52) self._window(420.0, 530.0, self.pid_entry) - def getEntries(self): - return [ - self.first_name.get(), - self.last_name.get(), - self.email.get(), - self.pid.get(), - ] - - def clearEntries(self): + def clear_entries(self): self.first_name_entry.delete(0, END) self.last_name_entry.delete(0, END) self.email_entry.delete(0, END) self.pid_entry.delete(0, END) - def updateEntries(self, fname, lname, email, pid): + def update_entries(self, fname, lname, email, pid): self.first_name_entry.insert(0, fname) self.last_name_entry.insert(0, lname) self.email_entry.insert(0, email) self.pid_entry.insert(0, pid) def _call_account_creation(self): - data = self.getEntries() - self.clearEntries() + first_name, last_name, email, pid = ( + self.first_name_var.get(), self.last_name_var.get(), + self.email_var.get(), self.pid_var.get(), + ) + self.clear_entries() try: - delay = timeit.timeit( - lambda: self.controller.ctx.account.create_account(data[0], data[1], data[2], data[3]), - number=1, - ) - logging.debug(f"Time to create account: {delay}") + self.controller.ctx.account.create_account(first_name, last_name, email, pid) except Exception: logging.warning("Error occurred trying to create a user account", exc_info=True) diff --git a/src/screens/user_thank.py b/src/screens/user_thank.py index 2946c87..00828ce 100644 --- a/src/screens/user_thank.py +++ b/src/screens/user_thank.py @@ -18,7 +18,7 @@ def hide(self): super().hide() self.canvas.delete("thank") - def displayName(self, name, nextPage): + def display_name(self, name, nextPage): self.controller.show_frame(UserThank) self.controller.ctx.account.on_thank_start(nextPage) diff --git a/src/screens/user_welcome.py b/src/screens/user_welcome.py index 689f91e..d8d31c2 100644 --- a/src/screens/user_welcome.py +++ b/src/screens/user_welcome.py @@ -19,7 +19,7 @@ def hide(self): self.last_name = None self.offset = 0 - def displayName(self, name): + def display_name(self, name): if name == self.last_name: return From c83a8f7dbd5f60080e36564304cfb2ebc3b58401 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 21:05:20 -0700 Subject: [PATCH 11/14] reorganize the ui --- run_dev.sh | 7 ++ .../button_check_in.png} | Bin src/assets/check_in_no_id_assets/image_2.png | Bin 6666 -> 0 bytes .../icon_check_in.png} | Bin .../button_fill_manually.png} | Bin .../outline_1.png} | Bin .../outline_2.png} | Bin .../outline_1.png} | Bin .../outline_2.png} | Bin .../register.png} | Bin src/assets/manual_fill_assets/image_4.png | Bin 1031 -> 0 bytes src/assets/manual_fill_assets/image_5.png | Bin 1031 -> 0 bytes src/assets/manual_fill_assets/image_6.png | Bin 2070 -> 0 bytes src/assets/manual_fill_assets/image_7.png | Bin 2070 -> 0 bytes src/assets/manual_fill_assets/image_8.png | Bin 2070 -> 0 bytes src/assets/manual_fill_assets/image_9.png | Bin 2070 -> 0 bytes .../no_acc_no_waiver_swipe_assets/image_4.png | Bin 1031 -> 0 bytes .../no_acc_no_waiver_swipe_assets/image_5.png | Bin 1031 -> 0 bytes .../image_5.png => qr_codes/qr_waiver.png} | Bin .../image_4.png => qr_codes/qr_website.png} | Bin src/assets/qr_codes_assets/image_3.png | Bin 1285 -> 0 bytes .../{image_1.png => background_main.png} | Bin .../image_3.png => shared/button_generic.png} | Bin .../image_3.png => shared/field.png} | Bin .../icon_checked_box.png} | Bin .../image_6.png => shared/icon_home.png} | Bin .../icon_unchecked_box.png} | Bin .../shared/{image_2.png => outline_full.png} | Bin .../button_done_scanning.png} | Bin .../image_2.png => sign_waiver/outline_1.png} | Bin .../image_3.png => sign_waiver/outline_2.png} | Bin .../image_4.png => sign_waiver/outline_3.png} | Bin .../image_7.png => sign_waiver/qr_waiver.png} | Bin .../waiver_no_acc_swipe_assets/button_1.png | Bin 7548 -> 0 bytes .../waiver_no_acc_swipe_assets/image_2.png | Bin 2522 -> 0 bytes .../waiver_no_acc_swipe_assets/image_3.png | Bin 5651 -> 0 bytes .../waiver_no_acc_swipe_assets/image_4.png | Bin 1031 -> 0 bytes .../waiver_no_acc_swipe_assets/image_5.png | Bin 1738 -> 0 bytes src/controllers/account_controller.py | 26 +---- src/controllers/card_reader_controller.py | 4 +- src/controllers/check_in_controller.py | 82 +++++-------- src/controllers/navigation_controller.py | 104 ++++++++++++----- src/controllers/swipe_controller.py | 19 ++- src/main.py | 14 ++- src/screens/acc_no_waiver.py | 10 -- src/screens/acc_no_waiver_swipe.py | 50 -------- src/screens/{screen.py => base.py} | 15 +-- .../{check_in_no_id.py => check_in_manual.py} | 47 +++++--- .../{main_page.py => check_in_rfid.py} | 18 ++- src/screens/components/__init__.py | 0 src/screens/components/canvas_entry.py | 73 ++++++++++++ src/screens/create_account_barcode.py | 54 +++++++++ src/screens/create_account_manual.py | 102 ++++++++++++++++ src/screens/dev_overlay.py | 107 +++++++++++++++++ src/screens/manual_fill.py | 110 ------------------ src/screens/no_acc_check_in_only.py | 10 -- src/screens/no_acc_no_waiver.py | 10 -- src/screens/no_acc_no_waiver_swipe.py | 44 ------- src/screens/qr_codes.py | 23 ++-- src/screens/sign_waiver.py | 49 ++++++++ src/screens/transition_screen.py | 15 +++ src/screens/user_thank.py | 37 ------ src/screens/user_welcome.py | 10 +- src/screens/waiver_no_acc.py | 10 -- src/screens/waiver_no_acc_swipe.py | 44 ------- src/window.py | 4 +- 66 files changed, 596 insertions(+), 502 deletions(-) rename src/assets/{check_in_no_id_assets/button_1.png => check_in_manual/button_check_in.png} (100%) delete mode 100644 src/assets/check_in_no_id_assets/image_2.png rename src/assets/{main_page_assets/image_4.png => check_in_rfid/icon_check_in.png} (100%) rename src/assets/{no_acc_no_waiver_swipe_assets/button_1.png => create_account_barcode/button_fill_manually.png} (100%) rename src/assets/{manual_fill_assets/image_2.png => create_account_barcode/outline_1.png} (100%) rename src/assets/{manual_fill_assets/image_3.png => create_account_barcode/outline_2.png} (100%) rename src/assets/{no_acc_no_waiver_swipe_assets/image_2.png => create_account_manual/outline_1.png} (100%) rename src/assets/{no_acc_no_waiver_swipe_assets/image_3.png => create_account_manual/outline_2.png} (100%) rename src/assets/{manual_fill_assets/button_1.png => create_account_manual/register.png} (100%) delete mode 100644 src/assets/manual_fill_assets/image_4.png delete mode 100644 src/assets/manual_fill_assets/image_5.png delete mode 100644 src/assets/manual_fill_assets/image_6.png delete mode 100644 src/assets/manual_fill_assets/image_7.png delete mode 100644 src/assets/manual_fill_assets/image_8.png delete mode 100644 src/assets/manual_fill_assets/image_9.png delete mode 100644 src/assets/no_acc_no_waiver_swipe_assets/image_4.png delete mode 100644 src/assets/no_acc_no_waiver_swipe_assets/image_5.png rename src/assets/{qr_codes_assets/image_5.png => qr_codes/qr_waiver.png} (100%) rename src/assets/{qr_codes_assets/image_4.png => qr_codes/qr_website.png} (100%) delete mode 100644 src/assets/qr_codes_assets/image_3.png rename src/assets/shared/{image_1.png => background_main.png} (100%) rename src/assets/{main_page_assets/image_3.png => shared/button_generic.png} (100%) rename src/assets/{check_in_no_id_assets/image_3.png => shared/field.png} (100%) rename src/assets/{acc_no_waiver_swipe_assets/image_5.png => shared/icon_checked_box.png} (100%) rename src/assets/{qr_codes_assets/image_6.png => shared/icon_home.png} (100%) rename src/assets/{acc_no_waiver_swipe_assets/image_6.png => shared/icon_unchecked_box.png} (100%) rename src/assets/shared/{image_2.png => outline_full.png} (100%) rename src/assets/{acc_no_waiver_swipe_assets/button_1.png => sign_waiver/button_done_scanning.png} (100%) rename src/assets/{acc_no_waiver_swipe_assets/image_2.png => sign_waiver/outline_1.png} (100%) rename src/assets/{acc_no_waiver_swipe_assets/image_3.png => sign_waiver/outline_2.png} (100%) rename src/assets/{acc_no_waiver_swipe_assets/image_4.png => sign_waiver/outline_3.png} (100%) rename src/assets/{acc_no_waiver_swipe_assets/image_7.png => sign_waiver/qr_waiver.png} (100%) delete mode 100644 src/assets/waiver_no_acc_swipe_assets/button_1.png delete mode 100644 src/assets/waiver_no_acc_swipe_assets/image_2.png delete mode 100644 src/assets/waiver_no_acc_swipe_assets/image_3.png delete mode 100644 src/assets/waiver_no_acc_swipe_assets/image_4.png delete mode 100644 src/assets/waiver_no_acc_swipe_assets/image_5.png delete mode 100644 src/screens/acc_no_waiver.py delete mode 100644 src/screens/acc_no_waiver_swipe.py rename src/screens/{screen.py => base.py} (79%) rename src/screens/{check_in_no_id.py => check_in_manual.py} (60%) rename src/screens/{main_page.py => check_in_rfid.py} (73%) create mode 100644 src/screens/components/__init__.py create mode 100644 src/screens/components/canvas_entry.py create mode 100644 src/screens/create_account_barcode.py create mode 100644 src/screens/create_account_manual.py create mode 100644 src/screens/dev_overlay.py delete mode 100644 src/screens/manual_fill.py delete mode 100644 src/screens/no_acc_check_in_only.py delete mode 100644 src/screens/no_acc_no_waiver.py delete mode 100644 src/screens/no_acc_no_waiver_swipe.py create mode 100644 src/screens/sign_waiver.py create mode 100644 src/screens/transition_screen.py delete mode 100644 src/screens/user_thank.py delete mode 100644 src/screens/waiver_no_acc.py delete mode 100644 src/screens/waiver_no_acc_swipe.py diff --git a/run_dev.sh b/run_dev.sh index 7d28756..e190a06 100755 --- a/run_dev.sh +++ b/run_dev.sh @@ -4,6 +4,13 @@ set -a source "$(dirname "$0")/.env" set +a +for arg in "$@"; do + if [ "$arg" = "--dev" ] || [ "$arg" = "-d" ]; then + export DEV_MODE=1 + break + fi +done + output_file="log.txt" echo "" >> "$output_file" diff --git a/src/assets/check_in_no_id_assets/button_1.png b/src/assets/check_in_manual/button_check_in.png similarity index 100% rename from src/assets/check_in_no_id_assets/button_1.png rename to src/assets/check_in_manual/button_check_in.png diff --git a/src/assets/check_in_no_id_assets/image_2.png b/src/assets/check_in_no_id_assets/image_2.png deleted file mode 100644 index eb574c4269c9f5971ad39fc0214315fcc73e0c57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6666 zcmeI1`%_bA9>%l1;nE^@-9f5AICBw-zR!Hm zbKdtopLxDFzln`eKp@=Cd>x5HAl_RnY1mcqyXCnU`ceQ49 zHl^vc?pxW0uAqoERz)+ckVU^Od%r_D_!;)eASUxxL%eHX!w2=Ho<|$z%exNr{;n5) z;J*ak)}svZx#}ODe)D@bo~Q}9Q~Lbaxt8qDD$<&TCx2e6!!XjuYBbQsMcRKpJ%j() zbY*V4`|y3uN1I~iCbN#O{RbV20CEF(9Icv$`O?Rm`ItkEg0zFU;c7?Ki1okaWdSLA zruipgtxp1mxwe6ee&db_duf4?w~>!zDE7md0Sa@$Ty8Q`Du_@ucS?1CKS6sLJf=AH zd8tF@A&>E#gXl_V3hH&;oq9VN5kFR}Pmg6r(#uy~)^5ZdC1woE~pBx2%WZtdqbLS518JJP}x3fDL^X(NG2`)Cf;bz*7#t%Z1Xu70}J@+2HG z@e|2chvj2wZF5UYO-ODw02=tPaHMp4d3PWk(+i5XNtPbQcR=pdOuT>-oDj8ZYE_%Y5{ru{>FHSjsJ5Kq%BSb3CSyxwks=E`1}c>dsQ*N} zI3{$#7iq1QlMA8IL{^#hN+%Kf=DMTeU$Rcb;*rsQM|1+s&P$!dXhL?I%nVklXL2`1 z0Gw|gCv^!cl4G$7|Jm#e;O~m|!ug+D(VDYlQA9P>p|CEchOosHuBT+@wN!HX6q^-6 zLP>+Mct3G3pII&6TUa)i;fLC|n1a!P;MDz&S?~P&wBxexfPKd!5TE|(zGK)mg!7@( z1m{X{ZU^U?;5-BuuPsyaD4`eS=iZ;ukELuRIt!=}bVQVUB; zgG0%t>pkvyFdf)7#$_RulhG+YMfJyFNv?i5V+YrqyCj~p$K_)9)ur>YoG`PlQL=HU zcxx%8*dX+kns-io#Jly~T5Owhll2{<@9#C!HONv*v)Q9z4{8=X0(-;2P0Qj?qxn0tdukj3_4u8)*J&7f@a?~0J>W|D!HLk;YOY! zUCuOj4uH65KFAhh!fMcrwHMYhxm5q_*qU!*Hp@4*&TDbAfP}5w0}LqA?$?-Z)gDya zn;*idSyRbys0FjJe3UR`GX}GE*ZU*Hwq1Mg!GN;rym`0b302XLa1q{58+T#P+RpB+ xUoQe$KA$6-Olv!P=@Qp!!6lyt={r-!@7~ywFF%_k>{Wc`ud$JWzg+m&{{RVnLP-Ds diff --git a/src/assets/main_page_assets/image_4.png b/src/assets/check_in_rfid/icon_check_in.png similarity index 100% rename from src/assets/main_page_assets/image_4.png rename to src/assets/check_in_rfid/icon_check_in.png diff --git a/src/assets/no_acc_no_waiver_swipe_assets/button_1.png b/src/assets/create_account_barcode/button_fill_manually.png similarity index 100% rename from src/assets/no_acc_no_waiver_swipe_assets/button_1.png rename to src/assets/create_account_barcode/button_fill_manually.png diff --git a/src/assets/manual_fill_assets/image_2.png b/src/assets/create_account_barcode/outline_1.png similarity index 100% rename from src/assets/manual_fill_assets/image_2.png rename to src/assets/create_account_barcode/outline_1.png diff --git a/src/assets/manual_fill_assets/image_3.png b/src/assets/create_account_barcode/outline_2.png similarity index 100% rename from src/assets/manual_fill_assets/image_3.png rename to src/assets/create_account_barcode/outline_2.png diff --git a/src/assets/no_acc_no_waiver_swipe_assets/image_2.png b/src/assets/create_account_manual/outline_1.png similarity index 100% rename from src/assets/no_acc_no_waiver_swipe_assets/image_2.png rename to src/assets/create_account_manual/outline_1.png diff --git a/src/assets/no_acc_no_waiver_swipe_assets/image_3.png b/src/assets/create_account_manual/outline_2.png similarity index 100% rename from src/assets/no_acc_no_waiver_swipe_assets/image_3.png rename to src/assets/create_account_manual/outline_2.png diff --git a/src/assets/manual_fill_assets/button_1.png b/src/assets/create_account_manual/register.png similarity index 100% rename from src/assets/manual_fill_assets/button_1.png rename to src/assets/create_account_manual/register.png diff --git a/src/assets/manual_fill_assets/image_4.png b/src/assets/manual_fill_assets/image_4.png deleted file mode 100644 index 37f2074c2bc6d7dbce23662733a569449c28b791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmV+i1o-=jP)6`yzpu zs9A?k-Byk;gH$>4mTF7yy(TQZQI55KnIwJ5ZgGk>PMEb@c(l-W?zA=dOjX|Vu3?hAF5a}C( zNZ%Ml`o$>P5xb)W05=cjx`JCFW^i|7Ox!#yk~y>t_%;!cs;Z(O zhfKKzsK;B5*i1SB-JN2rhzJh9%y4`q5)fj5nQf)i=h$sQie_s1bVv~*0yxey$WOOz ztMggyDW*q@`ZJ7fCzm2X5dhSWApuCZ8vj~~KNVAYXkO2~>#_>-*Qz-Oqo}7A;i;yj=Zfxbfm?UCm}kdgRlTNWy0X3&rn`IRVCBN~3de26cyBza z4m3@xA|k5#xvHHarf=9v4TR-L1b54}NH7SDi=fJ+Ylnu$)8jPuNzh~-A{><_kfD4Q4%jE4XK002ovPDHLkV1kXt B=XC%8 diff --git a/src/assets/manual_fill_assets/image_5.png b/src/assets/manual_fill_assets/image_5.png deleted file mode 100644 index 37f2074c2bc6d7dbce23662733a569449c28b791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmV+i1o-=jP)6`yzpu zs9A?k-Byk;gH$>4mTF7yy(TQZQI55KnIwJ5ZgGk>PMEb@c(l-W?zA=dOjX|Vu3?hAF5a}C( zNZ%Ml`o$>P5xb)W05=cjx`JCFW^i|7Ox!#yk~y>t_%;!cs;Z(O zhfKKzsK;B5*i1SB-JN2rhzJh9%y4`q5)fj5nQf)i=h$sQie_s1bVv~*0yxey$WOOz ztMggyDW*q@`ZJ7fCzm2X5dhSWApuCZ8vj~~KNVAYXkO2~>#_>-*Qz-Oqo}7A;i;yj=Zfxbfm?UCm}kdgRlTNWy0X3&rn`IRVCBN~3de26cyBza z4m3@xA|k5#xvHHarf=9v4TR-L1b54}NH7SDi=fJ+Ylnu$)8jPuNzh~-A{><_kfD4Q4%jE4XK002ovPDHLkV1kXt B=XC%8 diff --git a/src/assets/manual_fill_assets/image_6.png b/src/assets/manual_fill_assets/image_6.png deleted file mode 100644 index 581d067a64c5c5a465a64b1aca1052fee1de2d62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2070 zcmds&`#;m|AICp(XgMU^5#4`N@iu(JSlyU&%J>Ku@{eHe)*Za!4atWazdrB4n z00pEg+zS9CHo(0TL6tGf&pkh5PI?6zzWx+B>1Q1k5b1<^4k_9#IA>C;;uPqnG?891qGFg< zp#L(gpqVNpEU&K-%^zv&2SN1NF+Y-gw@b-S@$8@^m?FnMgQSJ!A+f*>E0aWHvhaAD z*qhDQWnR;bfRa1PbaZsq9M5EV)t1e+GMiD?*rw8tr{hq$9Y+8x&Mx8HwAu26HMVyt zsWI@*_RH2*?o_!9xxrEW=oNX^{Bc7)%1sj;omf_0#kOXAkR&_QYP{vpS`XGiykB-! zYrmBW16*R?_O&`$L(!rTtM-@)vY5*bG4qSOrM;j|JbH|d*G=eRUWCKvcxKQ-uMn)# z8E1iyy?O|&m=o)Ec>|T}(7fKpC9HpYP%=7<`QD~QM3iR~_e&X8-jJDI*`}{+4)uQl zi~%FvXRJ6SrTpTIu_z-ant!y}sm6-5okTPcrsX`WvDG+6vm535*`?Yp`(YN`O0X_= z;&-zzt|r{snZ%2RyMl|OqI7}UnvcX9+4YMIw6XXIZ{*hMegO!IiY zf%5%>UzuH<5%JdXI<^$si=FKZ@)e?7cZ_bbR5?BLjv&gYQJQj!rrCY$1ek4K z3zC;I-<_xzM(mUpm)F$fau%`!eeTIv23C;2b@ff@0v;w$#ls=j=b3yG)YnJ(VG&D@ z7d@9^-PWv336Bke!^7(Hn2ek!YQ*?dvD%{V*H~)CVZ{gIq`e{Lw?@PyX0QG?fDjBV zQ3Y1oa5?kgD}RhC<3aLC?~SkHjnWhBdp10Le02G={>Rj!dUP{sE6p8dZ=Y!2W@hF;r?G6| zM+468~g-(|U#6r<(+@W>N2l6L#w`NfkD%qHs|DEsQvL z-&Y=sO*rBr;sk;$9CgCe6*8TVV`UH{%e@P=i&f^J6NIPhlbcSx7I?zp71AdTY^QSN z6Iflq1*elrO5qaHAjbcP1H@X3Czc$me=sG2cR9X~(u}YQNQzoWOq_3Yhth%Qedky` zH^;V&ZILG!z4;^qDUO#cB#yB&i`ABB+W!j#0pC&?bi>T&7S!U|kg1>Elj{oCT&kBT z)Smqq*9N*V3^DVdnFJ;qzZ%vLJKtrn+f@c-jJ>dRiQ#@09AAV0Fx<1TB<@`t;5Yt# zNCmt#!hcm+rm~R3D|}-OL);e)7k#~J%blR5&vaoNOxu_<<+q#nFz^W}wXLmgP!}oS zf(*y(L9>wDHkH+rh<1jAT2awQ25^)2y86(>xQ^P4vo!xK^MKzw%h`KKj)m_RFltLx z&4OjKK5< z3Y)ke{o^DkjTzti=VjQfLX4vzH#u<52G2%S$YuUVjt7#AQiek1UZklfb(MV=$gzcp ze+v$u%&y8a78OZ`wj}U31T#yQsnl|+5`cWnpC^_s)z>8@wKV4g4K=BXWu33W(LXaf z3R9oha}(ARj^w$LMcD_o)dgI!WN3>=5x&@duGCR--{P94bedI%)ekK#?FJl?*^nVW2|zkpu>O=GQi zds8zeE-hZzrR~n94D!<9N432-V=~7_ig3d6v(r+?Bvg@qmz=kT7FYs)9#drc-1d3V z-43cpzE^I_y|8(D*ROB0l|jjidj|5{%_({SWD2#p2+;UpvSUAS|9kl9#LiSyYXbKu zp?cc1O*qm=ZetQ3&U3ai$|e6L?KyO2L4N3EDA;6_T7kUwvmri^OSzfMnH!|2=@bcnMzT9FqD!Vj0n3 diff --git a/src/assets/manual_fill_assets/image_7.png b/src/assets/manual_fill_assets/image_7.png deleted file mode 100644 index 581d067a64c5c5a465a64b1aca1052fee1de2d62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2070 zcmds&`#;m|AICp(XgMU^5#4`N@iu(JSlyU&%J>Ku@{eHe)*Za!4atWazdrB4n z00pEg+zS9CHo(0TL6tGf&pkh5PI?6zzWx+B>1Q1k5b1<^4k_9#IA>C;;uPqnG?891qGFg< zp#L(gpqVNpEU&K-%^zv&2SN1NF+Y-gw@b-S@$8@^m?FnMgQSJ!A+f*>E0aWHvhaAD z*qhDQWnR;bfRa1PbaZsq9M5EV)t1e+GMiD?*rw8tr{hq$9Y+8x&Mx8HwAu26HMVyt zsWI@*_RH2*?o_!9xxrEW=oNX^{Bc7)%1sj;omf_0#kOXAkR&_QYP{vpS`XGiykB-! zYrmBW16*R?_O&`$L(!rTtM-@)vY5*bG4qSOrM;j|JbH|d*G=eRUWCKvcxKQ-uMn)# z8E1iyy?O|&m=o)Ec>|T}(7fKpC9HpYP%=7<`QD~QM3iR~_e&X8-jJDI*`}{+4)uQl zi~%FvXRJ6SrTpTIu_z-ant!y}sm6-5okTPcrsX`WvDG+6vm535*`?Yp`(YN`O0X_= z;&-zzt|r{snZ%2RyMl|OqI7}UnvcX9+4YMIw6XXIZ{*hMegO!IiY zf%5%>UzuH<5%JdXI<^$si=FKZ@)e?7cZ_bbR5?BLjv&gYQJQj!rrCY$1ek4K z3zC;I-<_xzM(mUpm)F$fau%`!eeTIv23C;2b@ff@0v;w$#ls=j=b3yG)YnJ(VG&D@ z7d@9^-PWv336Bke!^7(Hn2ek!YQ*?dvD%{V*H~)CVZ{gIq`e{Lw?@PyX0QG?fDjBV zQ3Y1oa5?kgD}RhC<3aLC?~SkHjnWhBdp10Le02G={>Rj!dUP{sE6p8dZ=Y!2W@hF;r?G6| zM+468~g-(|U#6r<(+@W>N2l6L#w`NfkD%qHs|DEsQvL z-&Y=sO*rBr;sk;$9CgCe6*8TVV`UH{%e@P=i&f^J6NIPhlbcSx7I?zp71AdTY^QSN z6Iflq1*elrO5qaHAjbcP1H@X3Czc$me=sG2cR9X~(u}YQNQzoWOq_3Yhth%Qedky` zH^;V&ZILG!z4;^qDUO#cB#yB&i`ABB+W!j#0pC&?bi>T&7S!U|kg1>Elj{oCT&kBT z)Smqq*9N*V3^DVdnFJ;qzZ%vLJKtrn+f@c-jJ>dRiQ#@09AAV0Fx<1TB<@`t;5Yt# zNCmt#!hcm+rm~R3D|}-OL);e)7k#~J%blR5&vaoNOxu_<<+q#nFz^W}wXLmgP!}oS zf(*y(L9>wDHkH+rh<1jAT2awQ25^)2y86(>xQ^P4vo!xK^MKzw%h`KKj)m_RFltLx z&4OjKK5< z3Y)ke{o^DkjTzti=VjQfLX4vzH#u<52G2%S$YuUVjt7#AQiek1UZklfb(MV=$gzcp ze+v$u%&y8a78OZ`wj}U31T#yQsnl|+5`cWnpC^_s)z>8@wKV4g4K=BXWu33W(LXaf z3R9oha}(ARj^w$LMcD_o)dgI!WN3>=5x&@duGCR--{P94bedI%)ekK#?FJl?*^nVW2|zkpu>O=GQi zds8zeE-hZzrR~n94D!<9N432-V=~7_ig3d6v(r+?Bvg@qmz=kT7FYs)9#drc-1d3V z-43cpzE^I_y|8(D*ROB0l|jjidj|5{%_({SWD2#p2+;UpvSUAS|9kl9#LiSyYXbKu zp?cc1O*qm=ZetQ3&U3ai$|e6L?KyO2L4N3EDA;6_T7kUwvmri^OSzfMnH!|2=@bcnMzT9FqD!Vj0n3 diff --git a/src/assets/manual_fill_assets/image_8.png b/src/assets/manual_fill_assets/image_8.png deleted file mode 100644 index 581d067a64c5c5a465a64b1aca1052fee1de2d62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2070 zcmds&`#;m|AICp(XgMU^5#4`N@iu(JSlyU&%J>Ku@{eHe)*Za!4atWazdrB4n z00pEg+zS9CHo(0TL6tGf&pkh5PI?6zzWx+B>1Q1k5b1<^4k_9#IA>C;;uPqnG?891qGFg< zp#L(gpqVNpEU&K-%^zv&2SN1NF+Y-gw@b-S@$8@^m?FnMgQSJ!A+f*>E0aWHvhaAD z*qhDQWnR;bfRa1PbaZsq9M5EV)t1e+GMiD?*rw8tr{hq$9Y+8x&Mx8HwAu26HMVyt zsWI@*_RH2*?o_!9xxrEW=oNX^{Bc7)%1sj;omf_0#kOXAkR&_QYP{vpS`XGiykB-! zYrmBW16*R?_O&`$L(!rTtM-@)vY5*bG4qSOrM;j|JbH|d*G=eRUWCKvcxKQ-uMn)# z8E1iyy?O|&m=o)Ec>|T}(7fKpC9HpYP%=7<`QD~QM3iR~_e&X8-jJDI*`}{+4)uQl zi~%FvXRJ6SrTpTIu_z-ant!y}sm6-5okTPcrsX`WvDG+6vm535*`?Yp`(YN`O0X_= z;&-zzt|r{snZ%2RyMl|OqI7}UnvcX9+4YMIw6XXIZ{*hMegO!IiY zf%5%>UzuH<5%JdXI<^$si=FKZ@)e?7cZ_bbR5?BLjv&gYQJQj!rrCY$1ek4K z3zC;I-<_xzM(mUpm)F$fau%`!eeTIv23C;2b@ff@0v;w$#ls=j=b3yG)YnJ(VG&D@ z7d@9^-PWv336Bke!^7(Hn2ek!YQ*?dvD%{V*H~)CVZ{gIq`e{Lw?@PyX0QG?fDjBV zQ3Y1oa5?kgD}RhC<3aLC?~SkHjnWhBdp10Le02G={>Rj!dUP{sE6p8dZ=Y!2W@hF;r?G6| zM+468~g-(|U#6r<(+@W>N2l6L#w`NfkD%qHs|DEsQvL z-&Y=sO*rBr;sk;$9CgCe6*8TVV`UH{%e@P=i&f^J6NIPhlbcSx7I?zp71AdTY^QSN z6Iflq1*elrO5qaHAjbcP1H@X3Czc$me=sG2cR9X~(u}YQNQzoWOq_3Yhth%Qedky` zH^;V&ZILG!z4;^qDUO#cB#yB&i`ABB+W!j#0pC&?bi>T&7S!U|kg1>Elj{oCT&kBT z)Smqq*9N*V3^DVdnFJ;qzZ%vLJKtrn+f@c-jJ>dRiQ#@09AAV0Fx<1TB<@`t;5Yt# zNCmt#!hcm+rm~R3D|}-OL);e)7k#~J%blR5&vaoNOxu_<<+q#nFz^W}wXLmgP!}oS zf(*y(L9>wDHkH+rh<1jAT2awQ25^)2y86(>xQ^P4vo!xK^MKzw%h`KKj)m_RFltLx z&4OjKK5< z3Y)ke{o^DkjTzti=VjQfLX4vzH#u<52G2%S$YuUVjt7#AQiek1UZklfb(MV=$gzcp ze+v$u%&y8a78OZ`wj}U31T#yQsnl|+5`cWnpC^_s)z>8@wKV4g4K=BXWu33W(LXaf z3R9oha}(ARj^w$LMcD_o)dgI!WN3>=5x&@duGCR--{P94bedI%)ekK#?FJl?*^nVW2|zkpu>O=GQi zds8zeE-hZzrR~n94D!<9N432-V=~7_ig3d6v(r+?Bvg@qmz=kT7FYs)9#drc-1d3V z-43cpzE^I_y|8(D*ROB0l|jjidj|5{%_({SWD2#p2+;UpvSUAS|9kl9#LiSyYXbKu zp?cc1O*qm=ZetQ3&U3ai$|e6L?KyO2L4N3EDA;6_T7kUwvmri^OSzfMnH!|2=@bcnMzT9FqD!Vj0n3 diff --git a/src/assets/manual_fill_assets/image_9.png b/src/assets/manual_fill_assets/image_9.png deleted file mode 100644 index 581d067a64c5c5a465a64b1aca1052fee1de2d62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2070 zcmds&`#;m|AICp(XgMU^5#4`N@iu(JSlyU&%J>Ku@{eHe)*Za!4atWazdrB4n z00pEg+zS9CHo(0TL6tGf&pkh5PI?6zzWx+B>1Q1k5b1<^4k_9#IA>C;;uPqnG?891qGFg< zp#L(gpqVNpEU&K-%^zv&2SN1NF+Y-gw@b-S@$8@^m?FnMgQSJ!A+f*>E0aWHvhaAD z*qhDQWnR;bfRa1PbaZsq9M5EV)t1e+GMiD?*rw8tr{hq$9Y+8x&Mx8HwAu26HMVyt zsWI@*_RH2*?o_!9xxrEW=oNX^{Bc7)%1sj;omf_0#kOXAkR&_QYP{vpS`XGiykB-! zYrmBW16*R?_O&`$L(!rTtM-@)vY5*bG4qSOrM;j|JbH|d*G=eRUWCKvcxKQ-uMn)# z8E1iyy?O|&m=o)Ec>|T}(7fKpC9HpYP%=7<`QD~QM3iR~_e&X8-jJDI*`}{+4)uQl zi~%FvXRJ6SrTpTIu_z-ant!y}sm6-5okTPcrsX`WvDG+6vm535*`?Yp`(YN`O0X_= z;&-zzt|r{snZ%2RyMl|OqI7}UnvcX9+4YMIw6XXIZ{*hMegO!IiY zf%5%>UzuH<5%JdXI<^$si=FKZ@)e?7cZ_bbR5?BLjv&gYQJQj!rrCY$1ek4K z3zC;I-<_xzM(mUpm)F$fau%`!eeTIv23C;2b@ff@0v;w$#ls=j=b3yG)YnJ(VG&D@ z7d@9^-PWv336Bke!^7(Hn2ek!YQ*?dvD%{V*H~)CVZ{gIq`e{Lw?@PyX0QG?fDjBV zQ3Y1oa5?kgD}RhC<3aLC?~SkHjnWhBdp10Le02G={>Rj!dUP{sE6p8dZ=Y!2W@hF;r?G6| zM+468~g-(|U#6r<(+@W>N2l6L#w`NfkD%qHs|DEsQvL z-&Y=sO*rBr;sk;$9CgCe6*8TVV`UH{%e@P=i&f^J6NIPhlbcSx7I?zp71AdTY^QSN z6Iflq1*elrO5qaHAjbcP1H@X3Czc$me=sG2cR9X~(u}YQNQzoWOq_3Yhth%Qedky` zH^;V&ZILG!z4;^qDUO#cB#yB&i`ABB+W!j#0pC&?bi>T&7S!U|kg1>Elj{oCT&kBT z)Smqq*9N*V3^DVdnFJ;qzZ%vLJKtrn+f@c-jJ>dRiQ#@09AAV0Fx<1TB<@`t;5Yt# zNCmt#!hcm+rm~R3D|}-OL);e)7k#~J%blR5&vaoNOxu_<<+q#nFz^W}wXLmgP!}oS zf(*y(L9>wDHkH+rh<1jAT2awQ25^)2y86(>xQ^P4vo!xK^MKzw%h`KKj)m_RFltLx z&4OjKK5< z3Y)ke{o^DkjTzti=VjQfLX4vzH#u<52G2%S$YuUVjt7#AQiek1UZklfb(MV=$gzcp ze+v$u%&y8a78OZ`wj}U31T#yQsnl|+5`cWnpC^_s)z>8@wKV4g4K=BXWu33W(LXaf z3R9oha}(ARj^w$LMcD_o)dgI!WN3>=5x&@duGCR--{P94bedI%)ekK#?FJl?*^nVW2|zkpu>O=GQi zds8zeE-hZzrR~n94D!<9N432-V=~7_ig3d6v(r+?Bvg@qmz=kT7FYs)9#drc-1d3V z-43cpzE^I_y|8(D*ROB0l|jjidj|5{%_({SWD2#p2+;UpvSUAS|9kl9#LiSyYXbKu zp?cc1O*qm=ZetQ3&U3ai$|e6L?KyO2L4N3EDA;6_T7kUwvmri^OSzfMnH!|2=@bcnMzT9FqD!Vj0n3 diff --git a/src/assets/no_acc_no_waiver_swipe_assets/image_4.png b/src/assets/no_acc_no_waiver_swipe_assets/image_4.png deleted file mode 100644 index 37f2074c2bc6d7dbce23662733a569449c28b791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmV+i1o-=jP)6`yzpu zs9A?k-Byk;gH$>4mTF7yy(TQZQI55KnIwJ5ZgGk>PMEb@c(l-W?zA=dOjX|Vu3?hAF5a}C( zNZ%Ml`o$>P5xb)W05=cjx`JCFW^i|7Ox!#yk~y>t_%;!cs;Z(O zhfKKzsK;B5*i1SB-JN2rhzJh9%y4`q5)fj5nQf)i=h$sQie_s1bVv~*0yxey$WOOz ztMggyDW*q@`ZJ7fCzm2X5dhSWApuCZ8vj~~KNVAYXkO2~>#_>-*Qz-Oqo}7A;i;yj=Zfxbfm?UCm}kdgRlTNWy0X3&rn`IRVCBN~3de26cyBza z4m3@xA|k5#xvHHarf=9v4TR-L1b54}NH7SDi=fJ+Ylnu$)8jPuNzh~-A{><_kfD4Q4%jE4XK002ovPDHLkV1kXt B=XC%8 diff --git a/src/assets/no_acc_no_waiver_swipe_assets/image_5.png b/src/assets/no_acc_no_waiver_swipe_assets/image_5.png deleted file mode 100644 index 37f2074c2bc6d7dbce23662733a569449c28b791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmV+i1o-=jP)6`yzpu zs9A?k-Byk;gH$>4mTF7yy(TQZQI55KnIwJ5ZgGk>PMEb@c(l-W?zA=dOjX|Vu3?hAF5a}C( zNZ%Ml`o$>P5xb)W05=cjx`JCFW^i|7Ox!#yk~y>t_%;!cs;Z(O zhfKKzsK;B5*i1SB-JN2rhzJh9%y4`q5)fj5nQf)i=h$sQie_s1bVv~*0yxey$WOOz ztMggyDW*q@`ZJ7fCzm2X5dhSWApuCZ8vj~~KNVAYXkO2~>#_>-*Qz-Oqo}7A;i;yj=Zfxbfm?UCm}kdgRlTNWy0X3&rn`IRVCBN~3de26cyBza z4m3@xA|k5#xvHHarf=9v4TR-L1b54}NH7SDi=fJ+Ylnu$)8jPuNzh~-A{><_kfD4Q4%jE4XK002ovPDHLkV1kXt B=XC%8 diff --git a/src/assets/qr_codes_assets/image_5.png b/src/assets/qr_codes/qr_waiver.png similarity index 100% rename from src/assets/qr_codes_assets/image_5.png rename to src/assets/qr_codes/qr_waiver.png diff --git a/src/assets/qr_codes_assets/image_4.png b/src/assets/qr_codes/qr_website.png similarity index 100% rename from src/assets/qr_codes_assets/image_4.png rename to src/assets/qr_codes/qr_website.png diff --git a/src/assets/qr_codes_assets/image_3.png b/src/assets/qr_codes_assets/image_3.png deleted file mode 100644 index 7eeb581ac0f7eddef0ae356cf0990d38161bc220..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1285 zcmeAS@N?(olHy`uVBq!ia0vp^DIm4(U{h;~(#T z-uM2D+T@h~r?0hn2`<%G!S1@@)}rf|Uf&a)m}}d4rMW_Yzo}iRB4C%t$49>XTf+~E z?$u~YTDU^wR$lJgyvcV{)K5OCy?@-pOL{>SkRE+ zNp|@BCXp`p!w+VuJpX~(|TdbEUJ z+87k|rRu}I_g_A~_c~{}WT{Zq+&j!ARi%F~&Hnn!*wR?oa5JO9wanPxn_^~LO!(@{o0fH_w3uaEvUg#vQ1)5 z@M5z)=gbfPIQ1lF)5Vy`HGWt71NT+^R1n(klEA0<&sfyZt?b;yi;9x6Oi3vf?{7*N z?|E@k;;@2&_}}I}UD@-S&g5)~YRp^a9Htj+eZHChqO{VB4R_>~8YFXcRkwPjSL#_$RJwx3dil|dFf@R6|&MZ1QJa;45VBnc$IqC1l^iB7CW|FR5&zjgg2)OEMB>- zcjxJorRBFyoaqoOICtr;*y`%!p2p&7zZ4A^_FcB)YdEf(sAL?I^!Cb+ichcg_pkLo ze|A|be|gNBcfG~aW?c`f_P5zpf5+;z(*3%qnU(>&ch)Etp8Qw0|GbNfp_|@R?=PQs z+Mk>MBjMwj`R3QEuSGFE;``fs_mIM|4qIX2mnX}``s}{`ILvNU+1_h-e7uey?k~DYXZzq!qk%;UV)6+TIZ_DN!Nifpd zy6Wuhx93;xEc$rU#CIQab5f6s$qnseC(cC$5Kar}?!7MjTIyzHs(r0>eHd%h@?5;bNk5rm8%QQR<~9rk*$ diff --git a/src/assets/shared/image_1.png b/src/assets/shared/background_main.png similarity index 100% rename from src/assets/shared/image_1.png rename to src/assets/shared/background_main.png diff --git a/src/assets/main_page_assets/image_3.png b/src/assets/shared/button_generic.png similarity index 100% rename from src/assets/main_page_assets/image_3.png rename to src/assets/shared/button_generic.png diff --git a/src/assets/check_in_no_id_assets/image_3.png b/src/assets/shared/field.png similarity index 100% rename from src/assets/check_in_no_id_assets/image_3.png rename to src/assets/shared/field.png diff --git a/src/assets/acc_no_waiver_swipe_assets/image_5.png b/src/assets/shared/icon_checked_box.png similarity index 100% rename from src/assets/acc_no_waiver_swipe_assets/image_5.png rename to src/assets/shared/icon_checked_box.png diff --git a/src/assets/qr_codes_assets/image_6.png b/src/assets/shared/icon_home.png similarity index 100% rename from src/assets/qr_codes_assets/image_6.png rename to src/assets/shared/icon_home.png diff --git a/src/assets/acc_no_waiver_swipe_assets/image_6.png b/src/assets/shared/icon_unchecked_box.png similarity index 100% rename from src/assets/acc_no_waiver_swipe_assets/image_6.png rename to src/assets/shared/icon_unchecked_box.png diff --git a/src/assets/shared/image_2.png b/src/assets/shared/outline_full.png similarity index 100% rename from src/assets/shared/image_2.png rename to src/assets/shared/outline_full.png diff --git a/src/assets/acc_no_waiver_swipe_assets/button_1.png b/src/assets/sign_waiver/button_done_scanning.png similarity index 100% rename from src/assets/acc_no_waiver_swipe_assets/button_1.png rename to src/assets/sign_waiver/button_done_scanning.png diff --git a/src/assets/acc_no_waiver_swipe_assets/image_2.png b/src/assets/sign_waiver/outline_1.png similarity index 100% rename from src/assets/acc_no_waiver_swipe_assets/image_2.png rename to src/assets/sign_waiver/outline_1.png diff --git a/src/assets/acc_no_waiver_swipe_assets/image_3.png b/src/assets/sign_waiver/outline_2.png similarity index 100% rename from src/assets/acc_no_waiver_swipe_assets/image_3.png rename to src/assets/sign_waiver/outline_2.png diff --git a/src/assets/acc_no_waiver_swipe_assets/image_4.png b/src/assets/sign_waiver/outline_3.png similarity index 100% rename from src/assets/acc_no_waiver_swipe_assets/image_4.png rename to src/assets/sign_waiver/outline_3.png diff --git a/src/assets/acc_no_waiver_swipe_assets/image_7.png b/src/assets/sign_waiver/qr_waiver.png similarity index 100% rename from src/assets/acc_no_waiver_swipe_assets/image_7.png rename to src/assets/sign_waiver/qr_waiver.png diff --git a/src/assets/waiver_no_acc_swipe_assets/button_1.png b/src/assets/waiver_no_acc_swipe_assets/button_1.png deleted file mode 100644 index 6771e90ff6f66e8f6be74d71741392b70002759f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7548 zcmV-?9fRVDP) zdyr*EdDwsbIQKDk?!0$byQ>wFR$56Q2_cLmB#=N7!Vs`i;1oi^MW8CK#E^2iaN-oM zq#U~v2yqfZRbm4HVgm-nCIU%i8IUa@ArN{SA+eHnSNqMOx8Fw74z)AG46T%Wva5o)O3|rt-`>-08$9F zR@CYZ3i%u&_mR&z)!&a)3fE|08x8Eh5Lfm{K5^TX{Q6~s43_iYpp_VmbwS_;0)YS^ z04W8L5=bE!o~-lazDd6N$ZqzGwf(ScC4s!KTN7IsY`K9zAh1dZA&8A-`=&B-Q%?b7T`(;K0)fCPApq-OuoNLf@tQy& z5LkVjbL5mn27wz01OlfA2gozsi9jF_SWg6@8wdmfrxAUoJFr%;ZFU+VSkN)qZqn)W zxW$eQ1WuLkJf2>H&|Lu>P*k9Ve=K7wXt}u`N&)D$FN~W%Oj@@IZw-$$xw~$3)`8@1B@Y{NaukLpev{W@Ge9 z8lIR`d|^*SUP05q?_3=7_5njRIZ*&g0bkk|@ywJ&3)oh4{D&P)^c*ilr#PS}L8UP( z&IxK}WtWK#=u#lla~LaV*sv5m+c3@x&F>$J`09a(ZtZXmK7C1x*Ox7IJ3Cp5^!Pv7 zo#Q}5kT|%u$MUZ)X|0$dvz}P-#M6Jf@H(p96>l{PeiuP(xsWOG}ok%hqe`LGqq;B-t}2!P$l1jaGVW;+Ro70!wG@ z3tb?R@KROszxG9RDX(=3mY#)|nFl_7sbhP|P`CaBgQ-T_a$qbdffvAYJeUIiy-NRG zs1Gy8uD}xAkAt5e{_;>nSwlfN{`2KcwwGRO3Rx;P73xQtg3rGiF`Wpe6S!+@%$s`+ zO)G-7dlu6EJi0}0PvOMdfGrXo;Vk^uj3%a;{s!twWag zZ%UB(??1dH;o9DGi8ja6>DoSv(dluWl9y1{&>uNk7QS^j0#@cF(h~mau%?>$_RHFz zFK~8qCZEUk$eP!5Gcs?ha}ZnDly?k74#Uy}PGY?+%sMZ0or8jOeDb204{T0Q!nd8A zrFP8Bp_n)yaq-lIpY`DpEi-5q$fQ z_LEVYcDvMJaP9xP*)cwq;an{KbVh4i#(9<#ySfs;U5Rm8305kGx6_#6bD6OCTLA># z^pFvqBco~^6cp^KN$wxk6x7mVFHNfD{{5)p#fqA-nP+ytmYoezn~(OGPc6KOrYL9O!L-$!v$TgV@=)RU-rXw$@hjed3loaP(bYF zcUii&HX`Y08t;5N9t*0856de^=#3l$IY+m4#8%Lp$5uXb+LPi>1<;%GH>aRL3r8gp z9BT=VxBPiZ%F!P=26Eo)QZqA`upkq$AWoMD(Gu_bRP3)M>tkPT?vZk&W~{XD4Ldtd zm2{lC)Bg8FKK8q1eT?_%5L^y8)|@?&6wse@i%z!f2>goicx$%mVVCm0mviGy>YN~S zm&v&r!`SYDmI9_?Z)W)YOX7}~b}t_q%-@Z)r#A*q?!ang84=6QL?n|ckm4H-5U{5Y-V z!Yf-XefOy5!BM}`0YBasl{%Zit%Gh2-y2qZ?Lb6Vnk@A1b~M>hax)XtS|JGC72=$V zVHAF%=+lmCdM!U3QFLqg$#KokjvL-OV5lWx!I#CRj<^@66@NFXC@cSh-Lb*&)TCr8 z5iC@xSqd0w3GO+fd1PEMoy>;Ewg1ZE?d5yxf|sfhKO9kfY+J&uLuSF3(ctd~4pbU- za{p1ypC8mz5-;Sk-wNT+)3EUDl;oL7$#s2-e|cfdK;&q-`2|;Y-?RT-3R#S-O8a~A z&s{oB2kU}+hc%DQGfo1C@!^WQtAZD%BOV%6{MPGYZs@nC>>%wF0lZ_#@WW9>!$QTt z4@NYfD8)1v`E7QMH3N@~DPjx9t>A6_2|G%brV(o{zWW@pKA!!w$JCzf&q%hMZ8^cO zZA#eQ>?ZSQ9Q8R-7wkP86M`!KF^IfA9UZCa#+MN%>i0aUc)L# zF97QXO6lKza$NE!yQ6em=3nA2=f1c9R?Rg=_%Z&%s}c7OE3EUAg>hc1?T!364dfgJ zl_t;#AFekV3qHFm#}gBh;sWEeT%_<5$7LnUHNBRm^<((tq~vH*5Y3a~p5Yu6mE*Z7 z$;%ZAFbaI=T(5TAxi#UBUy0~eu&XBcn_saUtyvCDh8lB+QSlp=;-r00R zf5MIZik}?!Lhb%zni~fUoAd4j%@F+cYXK86{NE#*oP>%IyuCl+tpkPwb%_!SYgQ5; zR(C_c;g-RK3%eZe-I!2P4(q&QDEoO)!8->HZ|O69_LUsZPD#2I9Bm378&`aA^Zb)T zMi!}{1bpJcn0E{s9DdSq_=)HIqT_~s%eM|WzHvw+g?HpUIHI|I$j~3n*3md!2;qg$ z&4Y$#CKYc=ZQ4cUFwXlA+MQ0@x)R>pXZWL+bL_54O3IsMo|#nKGMG?ZK=>_(OxwMx z$8vF(V{cuMmySor6~DT{kef%_wfNP>-kRc>2}Myt)p%1ym-3UwT8v;=x8{Y|LCg4u zrPI$@Z>v2;9B}8Bm>%VY%2*5j`mmmJdFQ?8j_)1S9B2p>P*#o)ZAq{W^3u&@>@9|I z@Y@&0eBeBvR8mQN0iMM9?~L>RJ(c*-sDHLCrXaojQv$ndUhpg`NQ5_0{PPQ9-Z5mD zPN16j`rT_PcpK8a!-@w-RYxyUX6S!wzu~toNan4TdoCgbG>zbrlH+~nC4A$c zrmW%NamCvQ4ZqT7sihi+b-t?Fo~q=5W15ok!s^3Y6SkMUy)}y!?Sw3XqHbmdXBHlH zLLc!O(5AfObzP2Es@_(3r0FY|Eu=YQ?Z2Vha!Xs*-N}6vVpcG`b1>nFiCk)e5$taW zglLcVIQ>`$H9K1%f38)RzzgI{N|sG|$4JxL=8rZcO|!6Lbh)ThD}PI$<-9x`kNr45 zl4d)ee2*O7K0VTuJT@*VNUzx3JYX2iSt^S-a?fSfozR8sB^FeY&W`sPS6-7?i%8)G z)B86j{B&G#py99cFAr(n(A_#gNw$kQ0uJx*WfLUP9f_v4-1DcDxo4DUf1; z76fd{S$0+wIpMS5l2ogjFP&T9Q@5|}GvuYCmCT=kD1{fY+lr1Z<$srj(>Z%)@4W>O zX_(z&XbC6T;=yAz# zOH!1M!JOmfK|{-}OTcnti0;Skr6J!!1d5ju>2r_wXNTh?$zgGj?Oa zyf-J)s5ay}tB&RHalTskFf4Gm1%uyKAB!JbExdNWwuoOh`o8hvaM*Tu3;?~)}hc9_+`-TLx$`7 z4UG<$UB<#tetu8+xsDW2PvFLW%Z>e($5N--caLhW?>B79Ii?c9caEsk+#O3J$NKj0S{B8+OqH~cf^k;kO zRQr1BRQu_1#REq)4;)hzm7`(=zp**yvTj2qwUI96{mx#x|G76}UtQ1_`TKF(2E$wX zEF1E6CLTA#;g{H7+NXJZLUmLi=5*6(mjqs*E~bTQ;T>atx-Vip_R#??3-8`&xS`K5 zl$*OYfdl^g?i{;n;S%02;)N(JxxHYPkat~l5bYGJMLDX}L>5{I|{&KL7}Cdi&4od36y_*xE&?PDkK=8?mx zeR?h@mvKnE`_SGcak)xKt2RA2t?1BVq=ij;4;tL2l&Wx6jWfAUN|x*T40Y4F0(363ZsT+V^OHEAVtQv9dpjdsg!kxwXmdh; z2bpJc;qaSQzFLzsop(@mNqE;#LTm*UV?xvT5W1F|dz`Fmb26TR87iB&-_&c^ z-er9%HSqlr?_4|9gddOl^yJtHZXPtNnyP%Z5rl5wHAvy(Z*LkfGl>Fu>BHuJrO)z? z!R2cuO*>onRZE|lWZi5HgZ58W$!eq}*jZUjP@NvE^P7@hP;^+l##%|d2A5L~+jf1~ z$D|su5^&$J^2KiEiM$Z_zZ*9joS}p_v1BzWP97(dI^Bkwf`bjY>`hpgL*k$(@--A% zR!~r|t15V@DtTs7G1`(i2b=Sjw-4Aw%XO|Lg3t}jN%pedwI!xo`(XDj?GqugM2Y3l z7p1AWcwzOzlq9dcS-~<}vJiaz0ps8+`y)nM;*1KD41wQX{>rkYAicI*P;l>2&7)(Q zp0qBn7PAG%%i22#|NMaF(eZ@~_BcRDM_=U41WLek0>7*(ijkXfdbW>KS$q5FSN2Ct zB?}koS#6Z?NjEq2TQ(Q$Ov>_K9oGC{RFjjAx`mtiEdx0_QM!bY z{L`&54;<6nHk5E#w>_m0vG#UjBe=3`C}~aNyj|}bha$Q)T-RsP`mw&gK-0o6rX}Ax zr1|-@B$8*xGBg{X%eyVt^;sSnm-Iz`S^Ym=jd*cd^5$N{mV!eI$7CYdQ&l`PruapA z9kH8##|R-Hv4X36EJfvJB1FErUsIHhHq_XhFU8Je+pBeKr*zt;#0!@9Y&5)U zgPF0_EhcamYF_|N2Nx858OX06)bvK)-0)xbL~QTUTv)Ve@`d0)L$bFfs2D+6JD+LQ z5FPb@Ib*k!-+pXc%>G7>eKkRk_FDNpM^zf_F&l1JHM81hNx>U?438gIb4JBvMS-s9 zw%k5sxaWvsFz z3EIa&cGzZ;zAT+;m?w1mocU9q}4(Uc$>&!HX4*OXE2u-ey{mj*nju zGtm-!d4EK=UN})@x%YdqYsjvnWnp99@duZ*_`<6Z&rkcf?=0l4Zca>fZduq|aD3|G z7Q;=+gQLE>)|}Up9{Y35vycKnOiwRowbxaX)M zzsSm!)mvkGh?=$>hRMDeT8@=?C`MOeZQ`Uvhb);EII<*hNirWQ? zY4PjKwl^PHKk{w%zWK;XZ|04M)XO~2i)v2l zN4EdfTx4NVu=va02?8Hxl-Sw-&#RT!H5zT%P+Ba!J94&Pn8Q2k#?s9e1>W&AkaP5F z|9vAZ|2NZr;Weg{nd^S%7s%dP?lnLf3Wv`iYg)m0JiBpZBw$0{(Wf2V+CM9qw%F?6 zUS!W`N%^8OlZlVy$P!cfBFA9lC~Ia4fhTr$m9)_07duXHVl6aIoj&93SbSN|CnqGI z*_9)bUivy}?R^GaMp)*t6)!@`tcCQUl4du!ZmW@D$< zl5z4l$+q7am?^R@@ZX6gj#GB6r}{i*|JyFJv)U2=X;+S)9`|8vTML%YT+t%8!mG`5 zg>_+GfLYsB#!72^QN&wX_S_Q|V(z9@ z&rT|?Ec*>VClkTb6OxMY^*btwPc83F>qx9eEf<2&4Fm$Gh;xwF@ZhN8m8zsSa*VbF zKNwXrDczX^Z#seBJU`)5W{Pnk>><5WdiQ zf8_YYg)w(-Nqjx(bu4gU9Yrq$0)f>*rB1Z*7+#!~?5#_h>E3nc7adoXErXHs8*85F z&YlGXp&JMUmPHmSr=>S12;pX9_0qZ!*(RTXG$ATT-9R94s$`*Z#`*G}4l$E;n0*%n zu15g6WgR(rW_LRc1On$i?QzP1*CG(Kj6oh6ucB2Dx`99-uv$3$gfv-CkS`paL@R|o z8;X<$0)fC9#5stx;NVyT`NE_DXT!1}5C{ZT4TlhdCto>?{F|S>!dNvXQXynF5C{ZT z10@8Nre*Im$mc3>?=Oy!&uNliVh98RffdDAN1>qk*(=95IN3tlYK70=_cT-Wn4Fep zd|#zNAP`t9I0sruDz%vZ^YCtJwHO(VkMjJ%Nj~@EeH0X!Aaw(QKww3%&QZ)M{`Z3~ zv-?<$>51b=F*rz}l;g7xJkK|uAEBo^Phwrrcmshz;1qEVjIoqU5%)fMfWLn7FoQh> zj7gBTR-@40Pghrg-@Sh)Uwe2bJzY7J6khnA>D~tdfxtO}!`pDBgmSsa1J54izudo* ze58;nV)N!L=zLcRqa@NAYSR@y{>>-ZIc@m#Ef>?>5=3`5b)~s#sL1c$ zcmcO>A7oQ+5v-$WEH-Grfj}StFr$p-BEP%p@k)b-Up&U&{$h;1;|&T*($%FIJ3hg- zt(!S=bR3z zxzFeM_o=uTj~zZc5D0|FiI0f@0y4Qb<_5FlEy#4J7;;~QiD(1D69|ei$ z+O6p#rbX<=LQU=j_|%KY!F~AL_f~-*;{~Fj9Inq5R#XsjpiPT6;RfV^3~R{4(?S zF52x!AMEvgIPmtdq8;j=5C5q?y7uqw?TD|kdV2Bc)lz~EYAMB6-g>kvt@q+Roh2i{ zsSS5FZ?b_|0i+hU=AChqI--7dRCXxE_ zx1L_ed3vFjTUXq+3_=y?^?~OYymOeWV&W*=Y@Gfx-ncw}mSP_F1*819@$n8v4R0`p zNWuy7PItZq!=$8$AR@63&oqaRHuAY@ zr7;N-ATj7mz82lYsEkctB3EZe-Ju7YD%7E&w(0XgiE=pNo(KAZOmI*H~FAJ9G@HC4{`XXvp8{7%{A@ z_e|ET-oBs9UP%PaTFXkoprZgF)$#xl9I<>Kq-1XbQ{Y*()h)ZH@15gE>ncm}QTm&<08V8m0=gNkJ!z#!@!xN?%#{ z71?5_#F|&(y||iC;Is6K-h7(}h#|GuZ2Od^0N0-o*mS!WZRBjP@lr!_iTbTKWJM2x z1i7dW#v-MzhIsb!3xf03A!H6kqiv!E2Jw~O;TC#NrwMuGT9Fb@!YvA`R|mh5wN`nD z7+^k=;H3cvbnR+xHE2|-Bx{@j!}=4JfLG)r>Cu@M;gI&tj{M*toc=G~x-Z{6<>yCB z;p)7(^|zy2hTqiq|15@-4||7;4*T3DhQps^1-!Svi<#XvI=Kd1{Fyaz=^+yi_UPE?EIO|{ zN$2Vyb7x#=^W#K%f3uqG zww;u}?N)hW2NSeoud7P?GKg+ZiA5sK<>1;YIl(qk7!PoO!PYGwS#y227GCyx=?mffdq9(M7-hY|RI^eHaCe>fs{ZjU; zm7EI(G=>4=a&8qG9gQ=5b+s8FDHwB~G9NWMMvTuf;Gc)dFYI$*tN&JW>K@NR;b+q8 z)d_Q0ad$JBIxh=kQgZ+MN0?d4u~d?soeGdvSGG{=Aq+Q0o6#TxoUT~2kn;-o9y0QP zQI=~^t_}@}Dyg}XE?s?3U@p%_p&<44bDl5+=iML~5SSr?FkiR8TrOL{){dIh# zXPtsQKC9(o8k_u3cY!%-q^|NvE9>R!v2R%1Wh7dK3kqp$l)G&rTm5SX-*z`U&ODX% zi`$dv{nDC^)^2wk__FJCpq}OG`?iXp6BMG|la8mOjU7Ma>fqUe+%rgYXI#M>H7C`; wlpNR9wbMG3d2nUsbF*C$JrM!jSRdI$IM#cM|1Li0cyZ`_I diff --git a/src/assets/waiver_no_acc_swipe_assets/image_3.png b/src/assets/waiver_no_acc_swipe_assets/image_3.png deleted file mode 100644 index d42add79ab4ea08ebf1c91c6f823d2cb31f85217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5651 zcmeHL{Z~?X9%g4|$Jz01=gdwuV%g44Tb7B9jf|_p)Ty;_%tA9mldLTr5%Fbw0g~I< zHa)J1r-|BJUsz9zX6%IwFKUX8OCy_viU=ek*s#bTD)|DWye&BMSM1@pd%oX$p7VU4 z`+Pt5^Lc7dpGpjTKjeKF3>J9u#4#)k_Rc&E_SXLQ-u0|pfA_c+2Kz{L^4O8I%I2k6 z{KNkmE{IkPWLoK1W2tpE55nKHJzH ze&`)9Mpi10aD_+&0|UvU^%O34l{Lhm03dJW~r&CW`c_I@%eG6;bmG)!4? z(bH5CWXldlI7HK*1>mzYnX`{GB1GkTM(=8=V_XpO82GT3h8fyXz*2_WIeFHPF62k`6-bGn^j9!h9{rAL>Da z=^`^p*qC6Cb#E~mw*XArW(77A-3P%PB1)XF+i@Pl7C3qi}0 z?_&iZcAWK< zk=bWyM>-t^F>rcAgI&%8AqC2Ux;I^vnb7;@MGc?dL(^R(0sn%65QU?6C#xU$xgF7A zlsT6l>g<^2N#9`Ar4!>`)55fptE?%py-ru+>J&C4ypb}UdYizG>?m$dmZjx`kPX%P zGB!4Jc?tMu_Tc&poylY(;Ul)Cf>5&TFcDwzD8a=7HhEz~PEN4)4X2-j&Wh+L4ke1W zwEnVrW^XkDy}}(2YtTt2 zR7qyfaWM!|o_2oC`_6an;nm7mkB!S#lJCFmS-1}S=~KM*mss!qd%yPFd>Cx^m%dG( z2tFtHu7j^8_zI(^7xanX6Je`m^Ets&5q%=~MDVrEzS+SyK>c3~ik0#xWl|@c7bxBO z#krW>Sg}7SP|uBgy`Ptp;qEF$UclReM=UuHhqZp*sYu!Ec`h>-J$0%xNqHdgmTY~J z3PRZY=@xl*D=rh?f4M8;D@YMT*L4ec(dxD3iUE^w$TAFM0b(1;6BiYSQVX}DtKOIZ z;c2A-PTY_HeU2ow?RGyuWhubE9g*ybKd4NFLZOmMrFVGPh0DTC)y^wjKe8BD0$Ahc z=j4YC=&Qo0uOFl&6uG%okD3MKQUVnf7kRvS%bFxzp8H~iff?^`qBjLL#{`HJ7>Y2q*Lp4%+XYb6b;UkUhM%F%L zva`~@UdFpohj2W%8hK`}z(%QjrPZWeZE`>6^UEn+8FWz5#yC*Sv1w{@@`MwifASK% zt~_)q^HqnR*26OkKSd zbV77bwAlQIQmgbv-S{k-Tw!iYpOJbbsM)xt!O4DRA;qzq*2r;Pr6qab!ANs9<;T_K z;go^TM(?hoU6MAm6`yzpu zs9A?k-Byk;gH$>4mTF7yy(TQZQI55KnIwJ5ZgGk>PMEb@c(l-W?zA=dOjX|Vu3?hAF5a}C( zNZ%Ml`o$>P5xb)W05=cjx`JCFW^i|7Ox!#yk~y>t_%;!cs;Z(O zhfKKzsK;B5*i1SB-JN2rhzJh9%y4`q5)fj5nQf)i=h$sQie_s1bVv~*0yxey$WOOz ztMggyDW*q@`ZJ7fCzm2X5dhSWApuCZ8vj~~KNVAYXkO2~>#_>-*Qz-Oqo}7A;i;yj=Zfxbfm?UCm}kdgRlTNWy0X3&rn`IRVCBN~3de26cyBza z4m3@xA|k5#xvHHarf=9v4TR-L1b54}NH7SDi=fJ+Ylnu$)8jPuNzh~-A{><_kfD4Q4%jE4XK002ovPDHLkV1kXt B=XC%8 diff --git a/src/assets/waiver_no_acc_swipe_assets/image_5.png b/src/assets/waiver_no_acc_swipe_assets/image_5.png deleted file mode 100644 index 19cd880f8b4503a457fb7027f73c5dd724f666ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1738 zcmV;*1~vJKP)n$dEuuR5o5%%AYO$Lfp-oe1PsFv30&%j_|2+te~pP108;?!owR0r zi_gP_VrBk*Gy1mc(m0M>HUt2W#5hfCyDlX&U(99?l(~Pp@6(-@Vh2EXmQ%zVeU?8} z6ve4#eIxF+cOoLo1h`(iwm1N2Ha22j6sKI~p7JUuXlYc^Vq|)}n%5_rrimirmu&NE zN<@6qG_hWppG?rtd9Q}U$rL9*g*r`aA^JOU9su^d#+EcAg!5hq`IZyi zn5L*cb1Ss;K$=Eifj#ZeK}cFz&$QWY*68G7qOQg%Z4mrmJ)7O#cfEvaU5w(=%~O&$%(Jcn#Y^dAORdykQ*1g=PZ) z4(-TJk|6}?n&w}kdS1keWNr&;_c{$9=$po{AOvuTGeVMiS&F4}AxU~68A@beoW@TU zWl_W!A+q?v^-w@aT3J=vY;OH2%iZ@p-7y$magpz>%KDwn*0!nYE!lD?AcEcVkofZ! z{W<{njK>H*h?IFeP?D?&1X6c-LuKtPZQGdC~2KZK2*);Z|&N4VF$ln z3pqPUWLYMmDji}(v)%AU`3wnq@6oq?ug;%$BW2RqJ8^f4;uAyH-LTyv+Ve<|WR_(Z zr|~b_8yjD1+xF*KmdTCkMkKAMYqgYqs1~0~NOOzQ)9m4DHaj*=)4qX7v;Oi;Z$c!g zssdo5@tlxrF`?2RU#aJ_v(4sKx>mp-X(0;|r}%es@?nX#POk$dzAXgzT{BaXf_+M{ z_dFvaJ4Q|xw@h%t1(^x(M7P~su4gm1>o)*^kYxhFcg)gNQ4P}+9hg2H^4y#4R66$q zV$UEVA}P`GBF}^KU!ds-K+JR$rZ4twd$q`OXJjm^QrqqAKZ5vyr#qI$exp!-|7<>c zd)I95i-Ru=vI7I{yPnl-_AVek?vz{VP>B{V#b}>TW=#k|QcB6W@T60;0L^FNc%nTL zW8y&+Y!(_75s`L#JJs{q$5O&M2mR+T^~ZzHemhRXm-4E1eLwzEIQ=3~SDpAxi}I1# ze15X;d+zsa0DE-y>qU~zIXdU2z8^kIpFfK@ye{|oD=`kAQs>r^O~02cSB!RtD!wmH z$`lW#^tABE?9`N^h#(JHcmip-tH|6|Vq7KM)6Vn{s-g7eMj|~@)@2o^xNqV1zjbTp z8&&E1!SXyi5xo0qZ)r}ZQ5~wNmOBmGG4=O_5Z==^?X}Q-AiCwXs;b&HZ7PfME)VzR zonM(SO^mS%0(mS1f8e#{eQ`PwaZS^vYBqa^6Zcq(Xd`8vkaI;@-rX&jzV69}MVyM9O0VF%MODn~z-!C<3Igm_4(nPVg0^XP3Ok2T9^dy=S45<3+e88)aEEnS zaZowCJM2pey$!EL{zoDLHM8+{h7l2%*`$D6@!sJ`T&V9+BM>nyhu3{^zBen?LBl3LQ=ko-xxx+>RacSl_$Q@JviWa(!yj+a$6 zWe)m{W^?n!x~@IR_}bkpS0rOy*S_6qUIg^(BI;GtWznX8q%l1@^!=53b@kfbl8#aU z*tI1AKz(f0_kDjQj>Dsw$KOOZS`T^7QTxPBKAWLYLv zSvpaP-R9Ph;xK(&RsMYKnUnnI!!tLMeH3-40XYMa(*WKqK(GmZeSK=8bZJQF9Z gsisWSz54L%->G=L_;DKN(*OVf07*qoM6N<$g3pLdqW}N^ diff --git a/src/controllers/account_controller.py b/src/controllers/account_controller.py index ed851c3..e65b292 100644 --- a/src/controllers/account_controller.py +++ b/src/controllers/account_controller.py @@ -1,9 +1,6 @@ import time import tkinter import logging -from screens.main_page import MainPage -from screens.acc_no_waiver_swipe import AccNoWaiverSwipe -from screens.user_thank import UserThank class AccountController: @@ -87,29 +84,14 @@ def create_account(self, fname, lname, email, pid): no_wifi.destroy() if retries == 6: - self.ctx.nav.show_frame(MainPage) + self.ctx.nav.back_to_main() inProgress.destroy() return end4 = time.perf_counter() logging.debug(f"Total time to send data: {end4 - end2}") - checkin_result = self.ctx.sheets.checkin_by_uuid(self.ctx.rfid) - toGoTo = AccNoWaiverSwipe if checkin_result.get("status") == "no_waiver" else MainPage - - end5 = time.perf_counter() - logging.debug(f"Time to check waiver via check-in: {end5 - end4}") - - self.ctx.nav.get_frame(UserThank).display_name(full_name, toGoTo) inProgress.destroy() - - def on_thank_start(self, next_page): - if next_page == MainPage: - self.ctx.traffic_light.request_green() - else: - self.ctx.traffic_light.request_yellow() - - def on_thank_done(self, next_page): - self.ctx.nav.show_frame(next_page) - if next_page == MainPage: - self.ctx.traffic_light.request_off() + # pop() runs the on_done continuation from the nav stack, + # which re-runs _run_check_in to handle waiver check and check-in. + self.ctx.nav.pop() diff --git a/src/controllers/card_reader_controller.py b/src/controllers/card_reader_controller.py index 4a1472f..1d2c74a 100644 --- a/src/controllers/card_reader_controller.py +++ b/src/controllers/card_reader_controller.py @@ -3,7 +3,7 @@ import logging from tkinter import Label from threading import Thread -from screens.manual_fill import ManualFill +from screens.create_account_manual import CreateAccountManual class CardReaderController: @@ -53,7 +53,7 @@ def _run(self, reader): no_wifi.after(4000, lambda: self._destroy_wifi_error(no_wifi)) continue - self.ctx.nav.get_frame(ManualFill).clear_entries() + self.ctx.nav.get_frame(CreateAccountManual).clear_entries() tag = reader.grab_rfid() if " " in tag: diff --git a/src/controllers/check_in_controller.py b/src/controllers/check_in_controller.py index c4cd557..c0abd31 100644 --- a/src/controllers/check_in_controller.py +++ b/src/controllers/check_in_controller.py @@ -1,12 +1,6 @@ import logging from tkinter import Label -from screens.main_page import MainPage -from screens.no_acc_no_waiver import NoAccNoWaiver -from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe -from screens.no_acc_check_in_only import NoAccCheckInOnly -from screens.acc_no_waiver import AccNoWaiver -from screens.acc_no_waiver_swipe import AccNoWaiverSwipe from screens.user_welcome import UserWelcome @@ -15,58 +9,46 @@ def __init__(self, ctx): self.ctx = ctx def handle_by_uuid(self, tag): - result = self.ctx.sheets.checkin_by_uuid(tag) - status = result.get("status") - - def update_ui(): - if status == "api_error": - logging.error("API error during check-in") - self.ctx.traffic_light.request_red() - error_label = Label( - self.ctx.window.canvas, - text="System error, please let staff know.", - bg="#153246", fg="white", font=("Arial", 25), - ) - error_label.place(relx=0.5, rely=0.1, anchor="center") - error_label.after(4000, error_label.destroy) - return - - if status == "no_account": - logging.info(f"User {tag} not found.") - self.ctx.traffic_light.request_red() - self.ctx.nav.show_frame(NoAccNoWaiver) - self.ctx.window.after(3000, lambda: self.ctx.nav.show_frame(NoAccNoWaiverSwipe)) - return - - if status == "no_waiver": - logging.info(f"User {tag} does not have waiver.") - self.ctx.traffic_light.request_yellow() - self.ctx.nav.show_frame(AccNoWaiver) - self.ctx.window.after(3000, lambda: self.ctx.nav.show_frame(AccNoWaiverSwipe)) - return - - logging.info(f"User found: {result['name']}") - self.ctx.traffic_light.request_green() - self.ctx.nav.get_frame(UserWelcome).display_name(result["name"]) - - self.ctx.window.after(0, update_ui) + # Called from background thread — defer to main thread. + self.ctx.window.after( + 0, lambda: self._run_check_in(tag, self.ctx.sheets.checkin_by_uuid) + ) def handle_by_pid(self, pid): - result = self.ctx.sheets.checkin_by_pid(pid) + self._run_check_in(pid, self.ctx.sheets.checkin_by_pid) + + def _run_check_in(self, identifier, check_fn, welcome_message="Welcome back"): + result = check_fn(identifier) status = result.get("status") + if status == "api_error": + logging.error("API error during check-in") + self.ctx.traffic_light.request_red() + error_label = Label( + self.ctx.window.canvas, + text="System error, please let staff know.", + bg="#153246", fg="white", font=("Arial", 25), + ) + error_label.place(relx=0.5, rely=0.1, anchor="center") + error_label.after(4000, error_label.destroy) + return + if status == "no_account": - logging.info("Manual check-in: user account not found") - self.ctx.nav.show_frame(NoAccCheckInOnly) - self.ctx.nav.after(5000, lambda: self.ctx.nav.show_frame(MainPage)) + logging.info(f"No account found for {identifier}") + self.ctx.traffic_light.request_red() + self.ctx.nav.go_to_create_account( + on_done=lambda: self._run_check_in( + identifier, check_fn, welcome_message="Thank you for registering" + ) + ) return if status == "no_waiver": - logging.info(f"Manual check-in: no waiver for {result.get('name', pid)}") - self.ctx.nav.show_frame(AccNoWaiver) - self.ctx.nav.after(3000, lambda: self.ctx.nav.show_frame(NoAccNoWaiverSwipe)) + logging.info(f"No waiver for {identifier}") + self.ctx.traffic_light.request_yellow() + self.ctx.nav.go_to_sign_waiver() return - logging.info(f"Manual check-in for {result['name']}") + logging.info(f"Check-in successful: {result['name']}") self.ctx.traffic_light.request_green() - self.ctx.nav.get_frame(UserWelcome).display_name(result["name"]) + self.ctx.nav.get_frame(UserWelcome).display_name(result["name"], welcome_message) diff --git a/src/controllers/navigation_controller.py b/src/controllers/navigation_controller.py index 8389678..1794f53 100644 --- a/src/controllers/navigation_controller.py +++ b/src/controllers/navigation_controller.py @@ -1,52 +1,51 @@ import uuid -from screens.main_page import MainPage -from screens.acc_no_waiver import AccNoWaiver -from screens.acc_no_waiver_swipe import AccNoWaiverSwipe -from screens.manual_fill import ManualFill -from screens.check_in_no_id import CheckInNoId -from screens.no_acc_check_in_only import NoAccCheckInOnly -from screens.no_acc_no_waiver import NoAccNoWaiver -from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe +from screens.check_in_rfid import CheckInRFID +from screens.transition_screen import TransitionScreen +from screens.create_account_barcode import CreateAccountBarcode +from screens.create_account_manual import CreateAccountManual +from screens.sign_waiver import SignWaiver +from screens.check_in_manual import CheckInManual from screens.qr_codes import QRCodes -from screens.user_thank import UserThank from screens.user_welcome import UserWelcome -from screens.waiver_no_acc import WaiverNoAcc -from screens.waiver_no_acc_swipe import WaiverNoAccSwipe class NavigationController: - def __init__(self, window, ctx): + def __init__(self, window, ctx, dev_mode=False): self.ctx = ctx self._window = window self._frames = {} self._curr = None self._frame_uuid = uuid.uuid4().hex + self._on_done_stack = [] + self._dev_overlay = None self._timeouts = { - AccNoWaiverSwipe: 30000, + SignWaiver: 30000, QRCodes: 30000, - NoAccNoWaiverSwipe: 30000, } for F in ( - MainPage, - AccNoWaiver, - AccNoWaiverSwipe, - ManualFill, - CheckInNoId, - NoAccCheckInOnly, - NoAccNoWaiver, - NoAccNoWaiverSwipe, + CheckInRFID, + TransitionScreen, + CreateAccountBarcode, + CreateAccountManual, + SignWaiver, + CheckInManual, QRCodes, - UserThank, UserWelcome, - WaiverNoAcc, - WaiverNoAccSwipe, ): self._frames[F] = F(window.canvas, self) - self.show_frame(MainPage) + if dev_mode: + from screens.dev_overlay import DevOverlay + self._dev_overlay = DevOverlay(window.canvas, self) + + self.show_frame(CheckInRFID) + + # ------------------------------------------------------------------ + # Core frame switching + # ------------------------------------------------------------------ def show_frame(self, screen_class): if self._curr is not None: @@ -55,6 +54,9 @@ def show_frame(self, screen_class): self._frame_uuid = uuid.uuid4().hex self._frames[screen_class].show() + if self._dev_overlay is not None: + self._dev_overlay.update(screen_class) + if screen_class in self._timeouts: uid = self._frame_uuid self._window.after( @@ -71,17 +73,55 @@ def get_curr_frame(self): def after(self, ms, fn): self._window.after(ms, fn) + # ------------------------------------------------------------------ + # Stack-based flow + # ------------------------------------------------------------------ + + def push(self, screen_class, on_done=None): + """Show screen_class and register a continuation to run when pop() is called.""" + self._on_done_stack.append(on_done) + self.show_frame(screen_class) + + def pop(self): + """Signal that the current screen is done; run the stored continuation.""" + cb = self._on_done_stack.pop() if self._on_done_stack else None + if cb: + cb() + else: + self.back_to_main() + + # ------------------------------------------------------------------ + # Named navigations + # ------------------------------------------------------------------ + def back_to_main(self): + self._on_done_stack.clear() self.ctx.traffic_light.request_off() - self.show_frame(MainPage) + self.show_frame(CheckInRFID) def go_to_no_id(self): - self.get_frame(CheckInNoId).clear_entries() - self.show_frame(CheckInNoId) + self.get_frame(CheckInManual).clear_entries() + self.show_frame(CheckInManual) + + def go_to_create_account_manual(self): + self.get_frame(CreateAccountManual).clear_entries() + self.show_frame(CreateAccountManual) + + def go_to_create_account(self, on_done): + self.get_frame(TransitionScreen).display( + "Looks like you don't have an account,\nlet's set one up!" + ) + self._window.after(3000, lambda: self.push(CreateAccountBarcode, on_done=on_done)) + + def go_to_sign_waiver(self): + self.get_frame(TransitionScreen).display( + "Looks like you haven't signed\nthe waiver yet,\nlet's fix that!" + ) + self._window.after(3000, lambda: self.show_frame(SignWaiver)) - def go_to_manual_fill(self): - self.get_frame(ManualFill).clear_entries() - self.show_frame(ManualFill) + # ------------------------------------------------------------------ + # Internal + # ------------------------------------------------------------------ def _on_timeout(self, uid): if uid == self._frame_uuid: diff --git a/src/controllers/swipe_controller.py b/src/controllers/swipe_controller.py index c4b00eb..5d64be0 100644 --- a/src/controllers/swipe_controller.py +++ b/src/controllers/swipe_controller.py @@ -1,9 +1,8 @@ import tkinter import logging -from screens.manual_fill import ManualFill -from screens.no_acc_no_waiver_swipe import NoAccNoWaiverSwipe -from screens.waiver_no_acc_swipe import WaiverNoAccSwipe -from screens.check_in_no_id import CheckInNoId +from screens.create_account_manual import CreateAccountManual +from screens.create_account_barcode import CreateAccountBarcode +from screens.check_in_manual import CheckInManual from api.get_info_from_pid import ContactClient @@ -23,7 +22,7 @@ def _id_vet(self, id_check): def keyboard_press(self, key): curr_frame = self.ctx.nav.get_curr_frame() - if curr_frame not in (NoAccNoWaiverSwipe, WaiverNoAccSwipe, CheckInNoId): + if curr_frame not in (CreateAccountBarcode, CheckInManual): return self._id_string += key.char @@ -71,9 +70,9 @@ def _swipe_card(self, id_string): logging.info("Student search returned False, returning...") return - if self.ctx.nav.get_curr_frame() == CheckInNoId: - self.ctx.nav.get_frame(CheckInNoId).clear_entries() - self.ctx.nav.get_frame(CheckInNoId).update_entries(u_data.pid) + if self.ctx.nav.get_curr_frame() == CheckInManual: + self.ctx.nav.get_frame(CheckInManual).clear_entries() + self.ctx.nav.get_frame(CheckInManual).update_entries(u_data.pid) return email_to_use = "" if len(u_data.emails) == 0 else u_data.emails[0] @@ -81,12 +80,12 @@ def _swipe_card(self, id_string): if email.endswith("@ucsd.edu"): email_to_use = email - manfill = self.ctx.nav.get_frame(ManualFill) + manfill = self.ctx.nav.get_frame(CreateAccountManual) manfill.clear_entries() logging.info(f"Filling data with {u_data.first_name} {u_data.last_name} {email_to_use} {u_data.pid}") manfill.update_entries(u_data.first_name, u_data.last_name, email_to_use, u_data.pid) - self.ctx.nav.show_frame(ManualFill) + self.ctx.nav.show_frame(CreateAccountManual) def _destroy_swipe_error(self, id_error): id_error.destroy() diff --git a/src/main.py b/src/main.py index d7bb907..cf15f5c 100644 --- a/src/main.py +++ b/src/main.py @@ -5,8 +5,8 @@ from controllers.account_controller import AccountController from controllers.card_reader_controller import CardReaderController from hardware.reader import Reader -from screens.manual_fill import ManualFill -from screens.check_in_no_id import CheckInNoId +from screens.create_account_manual import CreateAccountManual +from screens.check_in_manual import CheckInManual from hardware.render_ports import get_usb_ids from app_context import AppContext from api.sheets import check_api_health @@ -17,8 +17,8 @@ def clear_and_return(ctx: AppContext): ctx.nav.back_to_main() - ctx.nav.get_frame(ManualFill).clear_entries() - ctx.nav.get_frame(CheckInNoId).clear_entries() + ctx.nav.get_frame(CreateAccountManual).clear_entries() + ctx.nav.get_frame(CheckInManual).clear_entries() if __name__ == "__main__": @@ -27,6 +27,7 @@ def clear_and_return(ctx: AppContext): formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("-v", "--verbose", action="store_true", help="Increase verbosity (print debug info)") + parser.add_argument("-d", "--dev", action="store_true", help="Enable dev mode with on-screen navigation overlay") args = parser.parse_args() if args.verbose: @@ -34,11 +35,14 @@ def clear_and_return(ctx: AppContext): else: logging.basicConfig(level=logging.INFO) + import os + dev_mode = args.dev or os.environ.get("DEV_MODE") == "1" + usb = get_usb_ids() check_api_health() ctx = AppContext.create(usb.traffic_light) window = CheckInWindow() - nav = NavigationController(window, ctx) + nav = NavigationController(window, ctx, dev_mode=dev_mode) ctx.window = window ctx.nav = nav ctx.check_in = CheckInController(ctx) diff --git a/src/screens/acc_no_waiver.py b/src/screens/acc_no_waiver.py deleted file mode 100644 index 899da8d..0000000 --- a/src/screens/acc_no_waiver.py +++ /dev/null @@ -1,10 +0,0 @@ -from .screen import Screen - - -class AccNoWaiver(Screen): - def _build(self, controller): - self._text( - 169.0, 258.0, anchor="nw", - text="Looks like you haven't signed\n the waiver, let's solve that", - fill="#F5F0E6", font=("Montserrat", 64 * -1), - ) diff --git a/src/screens/acc_no_waiver_swipe.py b/src/screens/acc_no_waiver_swipe.py deleted file mode 100644 index 70ed8d0..0000000 --- a/src/screens/acc_no_waiver_swipe.py +++ /dev/null @@ -1,50 +0,0 @@ -from pathlib import Path -from tkinter import Button -from .screen import Screen -ASSETS_PATH = Path(__file__).parent.parent / "assets" / "acc_no_waiver_swipe_assets" - - -class AccNoWaiverSwipe(Screen): - def _build(self, controller): - img2 = self._photo(ASSETS_PATH / "image_2.png") - self._image(1042.0, 359.0, image=img2) - - img3 = self._photo(ASSETS_PATH / "image_3.png") - self._image(408.0, 76.0, image=img3) - - img4 = self._photo(ASSETS_PATH / "image_4.png") - self._image(408.0, 429.0, image=img4) - - img5 = self._photo(ASSETS_PATH / "image_5.png") - self._image(395.0, 70.0, image=img5) - - img6 = self._photo(ASSETS_PATH / "image_6.png") - self._image(750.0, 70.0, image=img6) - - img7 = self._photo(ASSETS_PATH / "image_7.png") - self._image(1042.0, 328.0, image=img7) - - self._text( - 37.0, 45.0, anchor="nw", - text="Account Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - self._text( - 430.0, 45.0, anchor="nw", - text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - self._text( - 45.0, 270.0, anchor="nw", - text="Please scan the QR code\non the right and sign our \n waiver", - fill="#F5F0E6", font=("Montserrat", 48 * -1), - ) - - btn_img = self._photo(ASSETS_PATH / "button_1.png") - btn = Button( - self.canvas, image=btn_img, - borderwidth=0, highlightthickness=0, - command=self._back_to_main, relief="flat", - ) - self._window(875.0, 581.0, btn, width=344, height=71) - - def _back_to_main(self): - self.controller.back_to_main() diff --git a/src/screens/screen.py b/src/screens/base.py similarity index 79% rename from src/screens/screen.py rename to src/screens/base.py index 054629c..73a2b21 100644 --- a/src/screens/screen.py +++ b/src/screens/base.py @@ -11,10 +11,6 @@ def __init__(self, canvas, controller): self._build(controller) self.hide() - # ------------------------------------------------------------------ - # Helpers for subclasses - # ------------------------------------------------------------------ - def _photo(self, path): img = PhotoImage(file=str(path)) self._photos.append(img) @@ -30,8 +26,13 @@ def _text(self, x, y, **kwargs): self._items.append(item) return item + def _canvas_entry(self, x, y, w, h, font, fg="#F5F0E6"): + from .components.canvas_entry import CanvasEntry + entry = CanvasEntry(self.canvas, x, y, w, h, font, fg) + self._items.extend(entry.item_ids) + return entry + def _window(self, x, y, widget, width=None, height=None): - """Embed a tk widget into the canvas. x, y = top-left corner.""" kw = dict(anchor="nw", window=widget) if width is not None: kw["width"] = width @@ -41,10 +42,6 @@ def _window(self, x, y, widget, width=None, height=None): self._windows.append(item) return item - # ------------------------------------------------------------------ - # Visibility - # ------------------------------------------------------------------ - def show(self): for item in self._items: self.canvas.itemconfigure(item, state="normal") diff --git a/src/screens/check_in_no_id.py b/src/screens/check_in_manual.py similarity index 60% rename from src/screens/check_in_no_id.py rename to src/screens/check_in_manual.py index 135186d..e0df8d6 100644 --- a/src/screens/check_in_no_id.py +++ b/src/screens/check_in_manual.py @@ -1,37 +1,41 @@ from pathlib import Path -from tkinter import Button, Entry, StringVar, END -from .screen import Screen -import logging +from tkinter import Button, END +from .base import Screen +from .components.canvas_entry import CanvasEntry -ASSETS_PATH = Path(__file__).parent.parent / "assets" / "check_in_no_id_assets" +ASSETS_PATH = Path(__file__).parent.parent / "assets" / "check_in_manual" +SHARED_PATH = Path(__file__).parent.parent / "assets" / "shared" -######################################################## -# This is the frame where users will manually check in # -######################################################## - -class CheckInNoId(Screen): +class CheckInManual(Screen): def _build(self, controller): self.loading_text_id = None - self.pid = StringVar() - img2 = self._photo(ASSETS_PATH / "image_2.png") - self._image(640.0, 360.0, image=img2) + logo = self._photo(SHARED_PATH / "button_generic.png") + self._image(88.0, 90.0, image=logo) + + home_img = self._photo(SHARED_PATH / "icon_home.png") + home_btn = Button( + self.canvas, image=home_img, bg="#153246", + command=lambda: controller.back_to_main(), + relief="flat", highlightthickness=0, bd=0, + ) + self._window(53.0, 55.0, home_btn) - img3 = self._photo(ASSETS_PATH / "image_3.png") - self._image(640.0, 424.0, image=img3) + field_img = self._photo(SHARED_PATH / "field.png") + self._image(640.0, 424.0, image=field_img) self._text( - 212.0, 120.0, anchor="nw", + 640.0, 206.0, anchor="center", text="If you have already made an\naccount, scan your UCSD barcode\nor enter your PID manually", fill="#F5F0E6", font=("Montserrat", 48 * -1), justify="center", ) self._text( - 605.0, 480.0, anchor="nw", + 640.0, 492.0, anchor="center", text="PID", fill="#F5F0E6", font=("Montserrat", 24 * -1), ) - btn_img = self._photo(ASSETS_PATH / "button_1.png") + btn_img = self._photo(ASSETS_PATH / "button_check_in.png") btn = Button( self.canvas, image=btn_img, borderwidth=0, highlightthickness=0, @@ -39,8 +43,13 @@ def _build(self, controller): ) self._window(465.0, 598.0, btn, width=349, height=71) - self.pid_entry = Entry(self.canvas, textvariable=self.pid, width=40, font=52) - self._window(420.0, 412.0, self.pid_entry) + self.pid_entry = self._canvas_entry( + 640.0, 424.0, w=800, h=44, font=("Montserrat", 20), + ) + + def hide(self): + CanvasEntry.blur_all() + super().hide() def display_loading(self): if self.loading_text_id is None: diff --git a/src/screens/main_page.py b/src/screens/check_in_rfid.py similarity index 73% rename from src/screens/main_page.py rename to src/screens/check_in_rfid.py index 85c79d2..cdd72ed 100644 --- a/src/screens/main_page.py +++ b/src/screens/check_in_rfid.py @@ -1,19 +1,20 @@ from pathlib import Path from tkinter import Button -from .screen import Screen +from .base import Screen from .qr_codes import QRCodes -ASSETS_PATH = Path(__file__).parent.parent / "assets" / "main_page_assets" +ASSETS_PATH = Path(__file__).parent.parent / "assets" / "check_in_rfid" +SHARED_PATH = Path(__file__).parent.parent / "assets" / "shared" -class MainPage(Screen): +class CheckInRFID(Screen): def _build(self, controller): - logo = self._photo(ASSETS_PATH / "image_3.png") + logo = self._photo(SHARED_PATH / "button_generic.png") self._image(88.0, 90.0, image=logo) self._text( - 336.0, 602.0, anchor="nw", + 640.0, 618.0, anchor="center", text="Please tap ID on the black box to start", fill="#F5F0E6", font=("Montserrat", 32 * -1), ) @@ -28,7 +29,7 @@ def _build(self, controller): fill="#F5F0E6", font=("Montserrat", 73 * -1), ) - btn1_img = self._photo(ASSETS_PATH / "image_4.png") + btn1_img = self._photo(ASSETS_PATH / "icon_check_in.png") btn1 = Button( self.canvas, image=btn1_img, bg="#153246", command=lambda: controller.show_frame(QRCodes), @@ -39,11 +40,8 @@ def _build(self, controller): btn2 = Button( self.canvas, image=logo, text="No\nID", compound="center", bg="#153246", fg="white", - command=lambda: self._go_to_no_id(controller), + command=lambda: controller.go_to_no_id(), relief="flat", highlightthickness=0, bd=0, font=("Montserrat", 36 * -1), ) self._window(1130.0, 40.0, btn2) - - def _go_to_no_id(self, controller): - controller.go_to_no_id() diff --git a/src/screens/components/__init__.py b/src/screens/components/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/screens/components/canvas_entry.py b/src/screens/components/canvas_entry.py new file mode 100644 index 0000000..e46d0d7 --- /dev/null +++ b/src/screens/components/canvas_entry.py @@ -0,0 +1,73 @@ +import tkinter as tk + +_focused = None + + +class CanvasEntry: + def __init__(self, canvas, x, y, w, h, font, fg="#F5F0E6"): + self.canvas = canvas + self._x = x + self._y = y + + canvas.configure(insertbackground=fg, insertontime=600, insertofftime=400) + + self._hit_id = canvas.create_rectangle( + x - w / 2, y - h / 2, x + w / 2, y + h / 2, + fill="", outline="", state="hidden", + ) + self._text_id = canvas.create_text( + x, y, text="", fill=fg, font=font, + anchor="center", state="hidden", + ) + + canvas.tag_bind(self._hit_id, "", self._on_click) + canvas.tag_bind(self._text_id, "", self._on_click) + + @property + def item_ids(self): + return [self._hit_id, self._text_id] + + def _on_click(self, event=None): + global _focused + if _focused and _focused is not self: + _focused._blur() + _focused = self + self.canvas.focus_set() + self.canvas.focus(self._text_id) + self.canvas.bind("", _dispatch_key) + if event: + idx = self.canvas.index(self._text_id, f"@{event.x},{event.y}") + self.canvas.icursor(self._text_id, idx) + else: + self.canvas.icursor(self._text_id, tk.END) + + def _blur(self): + global _focused + if _focused is self: + _focused = None + self.canvas.focus("") + + @classmethod + def blur_all(cls): + global _focused + if _focused: + _focused._blur() + + def get(self): + return self.canvas.itemcget(self._text_id, "text") + + def delete(self, start, end=None): + self.canvas.dchars(self._text_id, 0, tk.END) + + def insert(self, index, text): + self.canvas.insert(self._text_id, index, text) + + +def _dispatch_key(event): + if _focused: + if event.keysym == "BackSpace": + idx = _focused.canvas.index(_focused._text_id, tk.INSERT) + if idx > 0: + _focused.canvas.dchars(_focused._text_id, idx - 1, idx - 1) + elif event.char and event.char.isprintable(): + _focused.canvas.insert(_focused._text_id, tk.INSERT, event.char) diff --git a/src/screens/create_account_barcode.py b/src/screens/create_account_barcode.py new file mode 100644 index 0000000..0aa7b01 --- /dev/null +++ b/src/screens/create_account_barcode.py @@ -0,0 +1,54 @@ +from pathlib import Path +from tkinter import Button +from .base import Screen + +ASSETS_PATH = Path(__file__).parent.parent / "assets" / "create_account_barcode" +SHARED_PATH = Path(__file__).parent.parent / "assets" / "shared" + + +class CreateAccountBarcode(Screen): + def _build(self, controller): + logo = self._photo(SHARED_PATH / "button_generic.png") + self._image(88.0, 90.0, image=logo) + + home_img = self._photo(SHARED_PATH / "icon_home.png") + home_btn = Button( + self.canvas, image=home_img, bg="#153246", + command=lambda: controller.back_to_main(), + relief="flat", highlightthickness=0, bd=0, + ) + self._window(53.0, 55.0, home_btn) + + outline1_img = self._photo(ASSETS_PATH / "outline_1.png") + self._image(640.0, 76.0, image=outline1_img) + + outline2_img = self._photo(ASSETS_PATH / "outline_2.png") + self._image(640.0, 430.0, image=outline2_img) + + icon_unchecked = self._photo(SHARED_PATH / "icon_unchecked_box.png") + self._image(576.0, 65.0, image=icon_unchecked) + + icon_checked = self._photo(SHARED_PATH / "icon_checked_box.png") + self._image(1030.0, 65.0, image=icon_checked) + + self._text( + 640.0, 374.0, anchor="center", + text="Please scan your ID barcode", + fill="#F5F0E6", font=("Montserrat", 48 * -1), + ) + self._text( + 215.0, 45.0, anchor="nw", + text="Account Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), + ) + self._text( + 690.0, 45.0, anchor="nw", + text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), + ) + + btn_img = self._photo(ASSETS_PATH / "button_fill_manually.png") + btn = Button( + self.canvas, image=btn_img, + borderwidth=0, highlightthickness=0, + command=lambda: controller.go_to_create_account_manual(), relief="flat", + ) + self._window(465.0, 554.0, btn, width=349, height=71) diff --git a/src/screens/create_account_manual.py b/src/screens/create_account_manual.py new file mode 100644 index 0000000..7151cb7 --- /dev/null +++ b/src/screens/create_account_manual.py @@ -0,0 +1,102 @@ +from pathlib import Path +from tkinter import Button, END +from .base import Screen +from .components.canvas_entry import CanvasEntry +import logging + +ASSETS_PATH = Path(__file__).parent.parent / "assets" / "create_account_manual" +SHARED_PATH = Path(__file__).parent.parent / "assets" / "shared" + + +class CreateAccountManual(Screen): + def _build(self, controller): + logo = self._photo(SHARED_PATH / "button_generic.png") + self._image(88.0, 90.0, image=logo) + + home_img = self._photo(SHARED_PATH / "icon_home.png") + home_btn = Button( + self.canvas, image=home_img, bg="#153246", + command=lambda: controller.back_to_main(), + relief="flat", highlightthickness=0, bd=0, + ) + self._window(53.0, 55.0, home_btn) + + outline1_img = self._photo(ASSETS_PATH / "outline_1.png") + self._image(640.0, 76.0, image=outline1_img) + + outline2_img = self._photo(ASSETS_PATH / "outline_2.png") + self._image(640.0, 430.0, image=outline2_img) + + icon_unchecked = self._photo(SHARED_PATH / "icon_unchecked_box.png") + self._image(605.0, 77.0, image=icon_unchecked) + self._image(1010.0, 77.0, image=icon_unchecked) + + field_img = self._photo(SHARED_PATH / "field.png") + self._image(640.0, 542.0, image=field_img) + self._image(640.0, 440.0, image=field_img) + self._image(640.0, 339.0, image=field_img) + self._image(640.0, 239.0, image=field_img) + + self._text( + 250.0, 45.0, anchor="nw", + text="Account Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), + ) + self._text( + 670.0, 45.0, anchor="nw", + text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), + ) + self._text( + 640.0, 189.0, anchor="center", + text="First Name", fill="#F5F0E6", font=("Montserrat", 24 * -1), + ) + self._text( + 640.0, 290.0, anchor="center", + text="Last Name", fill="#F5F0E6", font=("Montserrat", 24 * -1), + ) + self._text( + 640.0, 391.0, anchor="center", + text="Email", fill="#F5F0E6", font=("Montserrat", 24 * -1), + ) + self._text( + 640.0, 492.0, anchor="center", + text="PID", fill="#F5F0E6", font=("Montserrat", 24 * -1), + ) + + btn_img = self._photo(ASSETS_PATH / "register.png") + btn = Button( + self.canvas, image=btn_img, + borderwidth=0, highlightthickness=0, + command=self._call_account_creation, relief="flat", + ) + self._window(465.0, 598.0, btn, width=349, height=71) + + self.first_name_entry = self._canvas_entry(640.0, 239.0, w=800, h=44, font=("Montserrat", 20)) + self.last_name_entry = self._canvas_entry(640.0, 339.0, w=800, h=44, font=("Montserrat", 20)) + self.email_entry = self._canvas_entry(640.0, 440.0, w=800, h=44, font=("Montserrat", 20)) + self.pid_entry = self._canvas_entry(640.0, 542.0, w=800, h=44, font=("Montserrat", 20)) + + def hide(self): + CanvasEntry.blur_all() + super().hide() + + def clear_entries(self): + for entry in (self.first_name_entry, self.last_name_entry, + self.email_entry, self.pid_entry): + entry.delete(0, END) + + def update_entries(self, fname, lname, email, pid): + self.first_name_entry.insert(0, fname) + self.last_name_entry.insert(0, lname) + self.email_entry.insert(0, email) + self.pid_entry.insert(0, pid) + + def _call_account_creation(self): + first_name = self.first_name_entry.get() + last_name = self.last_name_entry.get() + email = self.email_entry.get() + pid = self.pid_entry.get() + self.clear_entries() + try: + self.controller.ctx.account.create_account(first_name, last_name, email, pid) + except Exception: + logging.warning("Error occurred trying to create a user account", exc_info=True) diff --git a/src/screens/dev_overlay.py b/src/screens/dev_overlay.py new file mode 100644 index 0000000..8254126 --- /dev/null +++ b/src/screens/dev_overlay.py @@ -0,0 +1,107 @@ +import tkinter as tk + +from screens.check_in_rfid import CheckInRFID +from screens.create_account_barcode import CreateAccountBarcode +from screens.create_account_manual import CreateAccountManual +from screens.sign_waiver import SignWaiver +from screens.check_in_manual import CheckInManual +from screens.qr_codes import QRCodes +from screens.user_welcome import UserWelcome + +_DEV_NAME = "Dev User" +_DEV_EMAIL = "devuser@ucsd.edu" +_DEV_PID = "A12345678" +_THANK_MSG = "Thank you for registering" + + +def _sim_no_account_success(nav): + def on_done(): + nav.ctx.traffic_light.request_green() + nav.get_frame(UserWelcome).display_name(_DEV_NAME, _THANK_MSG) + nav.go_to_create_account(on_done=on_done) + + +def _sim_no_account_needs_waiver(nav): + nav.go_to_create_account(on_done=nav.go_to_sign_waiver) + + +def _sim_fill_and_go(nav): + frm = nav.get_frame(CreateAccountManual) + frm.clear_entries() + frm.update_entries("Dev", "User", _DEV_EMAIL, _DEV_PID) + nav.go_to_create_account_manual() + + +TRANSITIONS = { + CheckInRFID: [ + ("QR Codes", lambda nav: nav.show_frame(QRCodes)), + ("No ID", lambda nav: nav.go_to_no_id()), + ("card: success", lambda nav: nav.get_frame(UserWelcome).display_name(_DEV_NAME)), + ("card: no account [→ success]", _sim_no_account_success), + ("card: no account [→ waiver]", _sim_no_account_needs_waiver), + ("card: no waiver", lambda nav: nav.go_to_sign_waiver()), + ], + QRCodes: [ + ("← Main", lambda nav: nav.back_to_main()), + ], + CheckInManual: [ + ("← Main", lambda nav: nav.back_to_main()), + ("PID: success", lambda nav: nav.get_frame(UserWelcome).display_name(_DEV_NAME)), + ("PID: no account [→ success]", _sim_no_account_success), + ("PID: no account [→ waiver]", _sim_no_account_needs_waiver), + ("PID: no waiver", lambda nav: nav.go_to_sign_waiver()), + ], + CreateAccountBarcode: [ + ("sim barcode swipe", _sim_fill_and_go), + ("manual fill", lambda nav: nav.go_to_create_account_manual()), + ("← Main", lambda nav: nav.back_to_main()), + ], + CreateAccountManual: [ + ("submit", lambda nav: nav.pop()), + ("← Main", lambda nav: nav.back_to_main()), + ], + SignWaiver: [ + ("← Main", lambda nav: nav.back_to_main()), + ], +} + + +class DevOverlay: + def __init__(self, canvas, nav): + self._canvas = canvas + self._nav = nav + self._buttons = [] + + self._frame = tk.Frame(canvas, bg="#1a1a2e", relief="solid", bd=1) + tk.Label( + self._frame, + text="DEV NAV", + bg="#1a1a2e", fg="#aaaaaa", + font=("Courier", 9, "bold"), + ).pack(pady=(4, 2), padx=6) + + self._canvas_window = canvas.create_window( + 1270, 715, anchor="se", window=self._frame, + ) + + def update(self, screen_class): + for btn in self._buttons: + btn.destroy() + self._buttons.clear() + + for label, action in TRANSITIONS.get(screen_class, []): + btn = tk.Label( + self._frame, + text=label, + bg="#2a2a4e", fg="white", + font=("Courier", 9), + padx=6, pady=3, + cursor="hand2", + ) + btn.pack(fill="x", padx=4, pady=1) + btn.bind("", lambda e, a=action: a(self._nav)) + btn.bind("", lambda e, w=btn: w.configure(bg="#4a4a8e")) + btn.bind("", lambda e, w=btn: w.configure(bg="#2a2a4e")) + self._buttons.append(btn) + + self._canvas.tag_raise(self._canvas_window) diff --git a/src/screens/manual_fill.py b/src/screens/manual_fill.py deleted file mode 100644 index a1145c9..0000000 --- a/src/screens/manual_fill.py +++ /dev/null @@ -1,110 +0,0 @@ -from pathlib import Path -from tkinter import Button, Entry, StringVar, END -from .screen import Screen -import logging - -ASSETS_PATH = Path(__file__).parent.parent / "assets" / "manual_fill_assets" - - -####################################################### -# This is the frame where users will type information # -####################################################### - -class ManualFill(Screen): - def _build(self, controller): - self.first_name_var = StringVar() - self.last_name_var = StringVar() - self.email_var = StringVar() - self.pid_var = StringVar() - - img2 = self._photo(ASSETS_PATH / "image_2.png") - self._image(640.0, 76.0, image=img2) - - img3 = self._photo(ASSETS_PATH / "image_3.png") - self._image(640.0, 430.0, image=img3) - - img4 = self._photo(ASSETS_PATH / "image_4.png") - self._image(605.0, 77.0, image=img4) - - img5 = self._photo(ASSETS_PATH / "image_5.png") - self._image(1010.0, 77.0, image=img5) - - img6 = self._photo(ASSETS_PATH / "image_6.png") - self._image(640.0, 542.0, image=img6) - - img7 = self._photo(ASSETS_PATH / "image_7.png") - self._image(640.0, 440.0, image=img7) - - img8 = self._photo(ASSETS_PATH / "image_8.png") - self._image(640.0, 339.0, image=img8) - - img9 = self._photo(ASSETS_PATH / "image_9.png") - self._image(640.0, 239.0, image=img9) - - self._text( - 250.0, 45.0, anchor="nw", - text="Account Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - self._text( - 670.0, 45.0, anchor="nw", - text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - self._text( - 565.0, 177.0, anchor="nw", - text="First Name", fill="#F5F0E6", font=("Montserrat", 24 * -1), - ) - self._text( - 565.0, 278.0, anchor="nw", - text="Last Name", fill="#F5F0E6", font=("Montserrat", 24 * -1), - ) - self._text( - 595.0, 379.0, anchor="nw", - text="Email", fill="#F5F0E6", font=("Montserrat", 24 * -1), - ) - self._text( - 605.0, 480.0, anchor="nw", - text="PID", fill="#F5F0E6", font=("Montserrat", 24 * -1), - ) - - btn_img = self._photo(ASSETS_PATH / "button_1.png") - btn = Button( - self.canvas, image=btn_img, - borderwidth=0, highlightthickness=0, - command=self._call_account_creation, relief="flat", - ) - self._window(465.0, 598.0, btn, width=349, height=71) - - self.first_name_entry = Entry(self.canvas, textvariable=self.first_name_var, width=40, font=52) - self._window(420.0, 227.0, self.first_name_entry) - - self.last_name_entry = Entry(self.canvas, textvariable=self.last_name_var, width=40, font=52) - self._window(420.0, 327.0, self.last_name_entry) - - self.email_entry = Entry(self.canvas, textvariable=self.email_var, width=40, font=52) - self._window(420.0, 428.0, self.email_entry) - - self.pid_entry = Entry(self.canvas, textvariable=self.pid_var, width=40, font=52) - self._window(420.0, 530.0, self.pid_entry) - - def clear_entries(self): - self.first_name_entry.delete(0, END) - self.last_name_entry.delete(0, END) - self.email_entry.delete(0, END) - self.pid_entry.delete(0, END) - - def update_entries(self, fname, lname, email, pid): - self.first_name_entry.insert(0, fname) - self.last_name_entry.insert(0, lname) - self.email_entry.insert(0, email) - self.pid_entry.insert(0, pid) - - def _call_account_creation(self): - first_name, last_name, email, pid = ( - self.first_name_var.get(), self.last_name_var.get(), - self.email_var.get(), self.pid_var.get(), - ) - self.clear_entries() - try: - self.controller.ctx.account.create_account(first_name, last_name, email, pid) - except Exception: - logging.warning("Error occurred trying to create a user account", exc_info=True) diff --git a/src/screens/no_acc_check_in_only.py b/src/screens/no_acc_check_in_only.py deleted file mode 100644 index e9dde38..0000000 --- a/src/screens/no_acc_check_in_only.py +++ /dev/null @@ -1,10 +0,0 @@ -from .screen import Screen - - -class NoAccCheckInOnly(Screen): - def _build(self, controller): - self._text( - 160.0, 180.0, anchor="nw", - text="Looks like you don't have an\n account, please scan your ID\nat the main desk", - fill="#F5F0E6", font=("Montserrat", 64 * -1), - ) diff --git a/src/screens/no_acc_no_waiver.py b/src/screens/no_acc_no_waiver.py deleted file mode 100644 index 5377ebe..0000000 --- a/src/screens/no_acc_no_waiver.py +++ /dev/null @@ -1,10 +0,0 @@ -from .screen import Screen - - -class NoAccNoWaiver(Screen): - def _build(self, controller): - self._text( - 80.0, 180.0, anchor="nw", - text="Looks like your card isn't registered, \n let's set up your account.", - fill="#F5F0E6", font=("Montserrat", 64 * -1), - ) diff --git a/src/screens/no_acc_no_waiver_swipe.py b/src/screens/no_acc_no_waiver_swipe.py deleted file mode 100644 index 0deadc8..0000000 --- a/src/screens/no_acc_no_waiver_swipe.py +++ /dev/null @@ -1,44 +0,0 @@ -from pathlib import Path -from tkinter import Button -from .screen import Screen -ASSETS_PATH = Path(__file__).parent.parent / "assets" / "no_acc_no_waiver_swipe_assets" - - -class NoAccNoWaiverSwipe(Screen): - def _build(self, controller): - img2 = self._photo(ASSETS_PATH / "image_2.png") - self._image(640.0, 76.0, image=img2) - - img3 = self._photo(ASSETS_PATH / "image_3.png") - self._image(640.0, 430.0, image=img3) - - img4 = self._photo(ASSETS_PATH / "image_4.png") - self._image(576.0, 65.0, image=img4) - - img5 = self._photo(ASSETS_PATH / "image_5.png") - self._image(1030.0, 65.0, image=img5) - - self._text( - 303.0, 350.0, anchor="nw", - text="Please scan your ID barcode", - fill="#F5F0E6", font=("Montserrat", 48 * -1), - ) - self._text( - 215.0, 45.0, anchor="nw", - text="Account Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - self._text( - 690.0, 45.0, anchor="nw", - text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - - btn_img = self._photo(ASSETS_PATH / "button_1.png") - btn = Button( - self.canvas, image=btn_img, - borderwidth=0, highlightthickness=0, - command=lambda: self._go_to_manual_fill(controller), relief="flat", - ) - self._window(465.0, 554.0, btn, width=349, height=71) - - def _go_to_manual_fill(self, controller): - controller.go_to_manual_fill() diff --git a/src/screens/qr_codes.py b/src/screens/qr_codes.py index a25d1e4..974ded5 100644 --- a/src/screens/qr_codes.py +++ b/src/screens/qr_codes.py @@ -1,31 +1,32 @@ from pathlib import Path from tkinter import Button -from .screen import Screen +from .base import Screen -ASSETS_PATH = Path(__file__).parent.parent / "assets" / "qr_codes_assets" +ASSETS_PATH = Path(__file__).parent.parent / "assets" / "qr_codes" +SHARED_PATH = Path(__file__).parent.parent / "assets" / "shared" class QRCodes(Screen): def _build(self, controller): - img3 = self._photo(ASSETS_PATH / "image_3.png") - self._image(88.0, 90.0, image=img3) + logo = self._photo(SHARED_PATH / "button_generic.png") + self._image(88.0, 90.0, image=logo) - img4 = self._photo(ASSETS_PATH / "image_4.png") - self._image(421.0, 360.0, image=img4) + qr_website_img = self._photo(ASSETS_PATH / "qr_website.png") + self._image(421.0, 360.0, image=qr_website_img) - img5 = self._photo(ASSETS_PATH / "image_5.png") - self._image(859.0, 360.0, image=img5) + qr_waiver_img = self._photo(ASSETS_PATH / "qr_waiver.png") + self._image(859.0, 360.0, image=qr_waiver_img) self._text( - 335.0, 551.0, anchor="nw", + 421.0, 571.0, anchor="center", text="Website", fill="#F5F0E6", font=("Montserrat", 40 * -1), ) self._text( - 788.0, 557.0, anchor="nw", + 859.0, 571.0, anchor="center", text="Waiver", fill="#F5F0E6", font=("Montserrat", 40 * -1), ) - btn_img = self._photo(ASSETS_PATH / "image_6.png") + btn_img = self._photo(SHARED_PATH / "icon_home.png") btn = Button( self.canvas, image=btn_img, bg="#153246", command=lambda: controller.back_to_main(), diff --git a/src/screens/sign_waiver.py b/src/screens/sign_waiver.py new file mode 100644 index 0000000..3924b90 --- /dev/null +++ b/src/screens/sign_waiver.py @@ -0,0 +1,49 @@ +from pathlib import Path +from tkinter import Button +from .base import Screen + +ASSETS_PATH = Path(__file__).parent.parent / "assets" / "sign_waiver" +SHARED_PATH = Path(__file__).parent.parent / "assets" / "shared" + + +class SignWaiver(Screen): + def _build(self, controller): + outline1_img = self._photo(ASSETS_PATH / "outline_1.png") + self._image(1042.0, 359.0, image=outline1_img) + + outline2_img = self._photo(ASSETS_PATH / "outline_2.png") + self._image(408.0, 76.0, image=outline2_img) + + outline3_img = self._photo(ASSETS_PATH / "outline_3.png") + self._image(408.0, 429.0, image=outline3_img) + + icon_checked = self._photo(SHARED_PATH / "icon_checked_box.png") + self._image(395.0, 70.0, image=icon_checked) + + icon_unchecked = self._photo(SHARED_PATH / "icon_unchecked_box.png") + self._image(750.0, 70.0, image=icon_unchecked) + + qr_waiver_img = self._photo(ASSETS_PATH / "qr_waiver.png") + self._image(1042.0, 328.0, image=qr_waiver_img) + + self._text( + 37.0, 45.0, anchor="nw", + text="Account Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), + ) + self._text( + 430.0, 45.0, anchor="nw", + text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), + ) + self._text( + 45.0, 270.0, anchor="nw", + text="Please scan the QR code\non the right and sign our \n waiver", + fill="#F5F0E6", font=("Montserrat", 48 * -1), + ) + + btn_img = self._photo(ASSETS_PATH / "button_done_scanning.png") + btn = Button( + self.canvas, image=btn_img, + borderwidth=0, highlightthickness=0, + command=lambda: controller.back_to_main(), relief="flat", + ) + self._window(875.0, 581.0, btn, width=344, height=71) diff --git a/src/screens/transition_screen.py b/src/screens/transition_screen.py new file mode 100644 index 0000000..b6df9ac --- /dev/null +++ b/src/screens/transition_screen.py @@ -0,0 +1,15 @@ +from .base import Screen + + +class TransitionScreen(Screen): + def _build(self, controller): + self._msg_id = self._text( + 640, 340, anchor="center", + text="", + fill="#F5F0E6", font=("Montserrat", 64 * -1), + justify="center", + ) + + def display(self, message): + self.canvas.itemconfigure(self._msg_id, text=message) + self.controller.show_frame(TransitionScreen) diff --git a/src/screens/user_thank.py b/src/screens/user_thank.py deleted file mode 100644 index 00828ce..0000000 --- a/src/screens/user_thank.py +++ /dev/null @@ -1,37 +0,0 @@ -from .screen import Screen - - -class UserThank(Screen): - def _build(self, controller): - self._text( - 99.33203125, 259.33203125, anchor="nw", - text="Thank you for registering", - fill="#F5F0E6", font=("Montserrat", 45 * -1), - ) - self._text( - 429.0, 550.0, anchor="nw", - text="UCSD Makerspace", - fill="#F5F0E6", font=("Montserrat", 45 * -1), - ) - - def hide(self): - super().hide() - self.canvas.delete("thank") - - def display_name(self, name, nextPage): - self.controller.show_frame(UserThank) - self.controller.ctx.account.on_thank_start(nextPage) - - self.canvas.create_text( - 99.0, 323.0, anchor="nw", - text=name, - fill="#F5F0E6", - font=("Montserrat", 73 * -1), - tag="thank", - ) - - self.canvas.after(4500, lambda: self.canvas.delete("thank")) - self.controller.after(4000, lambda: self._go_to_next(nextPage)) - - def _go_to_next(self, nextPage): - self.controller.ctx.account.on_thank_done(nextPage) diff --git a/src/screens/user_welcome.py b/src/screens/user_welcome.py index d8d31c2..3804eb3 100644 --- a/src/screens/user_welcome.py +++ b/src/screens/user_welcome.py @@ -1,4 +1,4 @@ -from .screen import Screen +from .base import Screen class UserWelcome(Screen): @@ -6,7 +6,7 @@ def _build(self, controller): self.last_name = None self.offset = 0 - self._text( + self._msg_item = self._text( 99.33203125, 259.33203125, anchor="nw", text="Welcome back", fill="#F5F0E6", font=("Montserrat", 45 * -1), @@ -14,17 +14,17 @@ def _build(self, controller): def hide(self): super().hide() - # Clean up any dynamic name items when leaving this screen self.canvas.delete("welcome") + self.canvas.itemconfigure(self._msg_item, text="Welcome back") self.last_name = None self.offset = 0 - def display_name(self, name): + def display_name(self, name, message="Welcome back"): if name == self.last_name: return self.last_name = name - + self.canvas.itemconfigure(self._msg_item, text=message) self.controller.show_frame(UserWelcome) text_id = self.canvas.create_text( diff --git a/src/screens/waiver_no_acc.py b/src/screens/waiver_no_acc.py deleted file mode 100644 index a1029a6..0000000 --- a/src/screens/waiver_no_acc.py +++ /dev/null @@ -1,10 +0,0 @@ -from .screen import Screen - - -class WaiverNoAcc(Screen): - def _build(self, controller): - self._text( - 191.0, 258.0, anchor="nw", - text="Looks like you don't have an\n account, let's solve that", - fill="#F5F0E6", font=("Montserrat", 64 * -1), - ) diff --git a/src/screens/waiver_no_acc_swipe.py b/src/screens/waiver_no_acc_swipe.py deleted file mode 100644 index b449a28..0000000 --- a/src/screens/waiver_no_acc_swipe.py +++ /dev/null @@ -1,44 +0,0 @@ -from pathlib import Path -from tkinter import Button -from .screen import Screen -ASSETS_PATH = Path(__file__).parent.parent / "assets" / "waiver_no_acc_swipe_assets" - - -class WaiverNoAccSwipe(Screen): - def _build(self, controller): - img2 = self._photo(ASSETS_PATH / "image_2.png") - self._image(640.0, 76.0, image=img2) - - img3 = self._photo(ASSETS_PATH / "image_3.png") - self._image(640.0, 430.0, image=img3) - - img4 = self._photo(ASSETS_PATH / "image_4.png") - self._image(576.0, 65.0, image=img4) - - img5 = self._photo(ASSETS_PATH / "image_5.png") - self._image(1030.0, 65.0, image=img5) - - self._text( - 420.0, 350.0, anchor="nw", - text="Please scan your ID barcode", - fill="#F5F0E6", font=("Montserrat", 48 * -1), - ) - self._text( - 215.0, 45.0, anchor="nw", - text="Account Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - self._text( - 690.0, 45.0, anchor="nw", - text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), - ) - - btn_img = self._photo(ASSETS_PATH / "button_1.png") - btn = Button( - self.canvas, image=btn_img, - borderwidth=0, highlightthickness=0, - command=lambda: self._go_to_manual_fill(controller), relief="flat", - ) - self._window(465.0, 554.0, btn, width=349, height=71) - - def _go_to_manual_fill(self, controller): - controller.go_to_manual_fill() diff --git a/src/window.py b/src/window.py index e8f4738..23be69a 100644 --- a/src/window.py +++ b/src/window.py @@ -22,11 +22,11 @@ def __init__(self): self.canvas.pack(fill="both", expand=True) self._bg_photos = [] - bg1 = tk.PhotoImage(file=str(ASSETS_PATH / "image_1.png")) + bg1 = tk.PhotoImage(file=str(ASSETS_PATH / "background_main.png")) self._bg_photos.append(bg1) self.canvas.create_image(640.0, 360.0, image=bg1) - bg2 = tk.PhotoImage(file=str(ASSETS_PATH / "image_2.png")) + bg2 = tk.PhotoImage(file=str(ASSETS_PATH / "outline_full.png")) self._bg_photos.append(bg2) self.canvas.create_image(639.333984375, 359.333984375, image=bg2) From 59aeb8c1db753d968aeee5a2a67a208769efbf1a Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 22:00:24 -0700 Subject: [PATCH 12/14] remove old files --- install.sh | 8 -------- test.py | 11 ----------- 2 files changed, 19 deletions(-) delete mode 100644 install.sh delete mode 100644 test.py diff --git a/install.sh b/install.sh deleted file mode 100644 index 1435e7e..0000000 --- a/install.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -echo "Attempting install..." -pip install tk -pip install pathlib -pip install requests -pip install datetime -pip install OAuth2 -echo "Installation complete" \ No newline at end of file diff --git a/test.py b/test.py deleted file mode 100644 index adadd57..0000000 --- a/test.py +++ /dev/null @@ -1,11 +0,0 @@ -from tkinter import Tk, Label - -root=Tk() - -def key_pressed(event): - print(event.char) - return - -root.bind("",key_pressed) - -root.mainloop() \ No newline at end of file From 3b6971ff198ce80c25bcf5ba2a55e179792103df Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 22:00:40 -0700 Subject: [PATCH 13/14] more refactoring --- src/api/traffic_light_api.py | 2 +- src/app_context.py | 2 +- src/controllers/navigation_controller.py | 2 +- ...ard_reader_controller.py => rfid_reader_controller.py} | 2 +- src/hardware/{reader.py => rfid_reader.py} | 0 src/hardware/{traffic.py => traffic_light.py} | 0 src/hardware/{render_ports.py => usb_ports.py} | 0 src/main.py | 8 ++++---- src/screens/{ => components}/dev_overlay.py | 0 9 files changed, 8 insertions(+), 8 deletions(-) rename src/controllers/{card_reader_controller.py => rfid_reader_controller.py} (99%) rename src/hardware/{reader.py => rfid_reader.py} (100%) rename src/hardware/{traffic.py => traffic_light.py} (100%) rename src/hardware/{render_ports.py => usb_ports.py} (100%) rename src/screens/{ => components}/dev_overlay.py (100%) diff --git a/src/api/traffic_light_api.py b/src/api/traffic_light_api.py index da94ce9..e571dd4 100644 --- a/src/api/traffic_light_api.py +++ b/src/api/traffic_light_api.py @@ -1,7 +1,7 @@ import threading from api.sheets import SheetManager -from hardware.traffic import TrafficLight +from hardware.traffic_light import TrafficLight class TrafficLightApi: diff --git a/src/app_context.py b/src/app_context.py index c8e6cfd..da66cb1 100644 --- a/src/app_context.py +++ b/src/app_context.py @@ -2,7 +2,7 @@ from api.sheets import SheetManager from api.traffic_light_api import TrafficLightApi -from hardware.traffic import TrafficLight +from hardware.traffic_light import TrafficLight class AppContext: diff --git a/src/controllers/navigation_controller.py b/src/controllers/navigation_controller.py index 1794f53..4c3b6f5 100644 --- a/src/controllers/navigation_controller.py +++ b/src/controllers/navigation_controller.py @@ -38,7 +38,7 @@ def __init__(self, window, ctx, dev_mode=False): self._frames[F] = F(window.canvas, self) if dev_mode: - from screens.dev_overlay import DevOverlay + from screens.components.dev_overlay import DevOverlay self._dev_overlay = DevOverlay(window.canvas, self) self.show_frame(CheckInRFID) diff --git a/src/controllers/card_reader_controller.py b/src/controllers/rfid_reader_controller.py similarity index 99% rename from src/controllers/card_reader_controller.py rename to src/controllers/rfid_reader_controller.py index 1d2c74a..4c562d9 100644 --- a/src/controllers/card_reader_controller.py +++ b/src/controllers/rfid_reader_controller.py @@ -6,7 +6,7 @@ from screens.create_account_manual import CreateAccountManual -class CardReaderController: +class RfidReaderController: def __init__(self, ctx): self.ctx = ctx self._no_wifi_shown = False diff --git a/src/hardware/reader.py b/src/hardware/rfid_reader.py similarity index 100% rename from src/hardware/reader.py rename to src/hardware/rfid_reader.py diff --git a/src/hardware/traffic.py b/src/hardware/traffic_light.py similarity index 100% rename from src/hardware/traffic.py rename to src/hardware/traffic_light.py diff --git a/src/hardware/render_ports.py b/src/hardware/usb_ports.py similarity index 100% rename from src/hardware/render_ports.py rename to src/hardware/usb_ports.py diff --git a/src/main.py b/src/main.py index cf15f5c..e91f1fd 100644 --- a/src/main.py +++ b/src/main.py @@ -3,11 +3,11 @@ from controllers.swipe_controller import SwipeController from controllers.check_in_controller import CheckInController from controllers.account_controller import AccountController -from controllers.card_reader_controller import CardReaderController -from hardware.reader import Reader +from controllers.rfid_reader_controller import RfidReaderController +from hardware.rfid_reader import Reader from screens.create_account_manual import CreateAccountManual from screens.check_in_manual import CheckInManual -from hardware.render_ports import get_usb_ids +from hardware.usb_ports import get_usb_ids from app_context import AppContext from api.sheets import check_api_health import logging @@ -51,7 +51,7 @@ def clear_and_return(ctx: AppContext): sw = SwipeController(ctx) reader = Reader(usb.reader) - card_reader = CardReaderController(ctx) + card_reader = RfidReaderController(ctx) card_reader.start(reader) window.bind("", lambda i: sw.keyboard_press(i)) diff --git a/src/screens/dev_overlay.py b/src/screens/components/dev_overlay.py similarity index 100% rename from src/screens/dev_overlay.py rename to src/screens/components/dev_overlay.py From 305faa45d26dd2048365ebfdf2732de00411d6c5 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 21 Mar 2026 23:21:20 -0700 Subject: [PATCH 14/14] refactor the api, re add barcode endpoint and simplify barcode account registration ui --- src/api/{sheets.py => client.py} | 22 +++--- src/api/get_info_from_pid.py | 70 ------------------ src/api/traffic_light_api.py | 4 +- src/app_context.py | 6 +- src/controllers/account_controller.py | 90 +++++------------------- src/controllers/navigation_controller.py | 1 + src/controllers/swipe_controller.py | 52 ++------------ src/main.py | 2 +- src/screens/components/dev_overlay.py | 5 +- src/screens/create_account_manual.py | 43 ++--------- 10 files changed, 46 insertions(+), 249 deletions(-) rename src/api/{sheets.py => client.py} (82%) delete mode 100644 src/api/get_info_from_pid.py diff --git a/src/api/sheets.py b/src/api/client.py similarity index 82% rename from src/api/sheets.py rename to src/api/client.py index 7bc3b67..dc81408 100644 --- a/src/api/sheets.py +++ b/src/api/client.py @@ -23,7 +23,7 @@ def check_api_health(retries=3, delay=3): sys.exit(1) -class SheetManager: +class ApiClient: def checkin_by_uuid(self, uuid): try: resp = _req("GET", f"{API_BASE_URL}/check-in/uuid/{uuid}", timeout=10) @@ -56,20 +56,14 @@ def get_traffic_light(self): logging.error(f"Error getting traffic light: {e}") return "off" - def create_account(self, first_name, last_name, email, pid, rfid): + def create_account(self, rfid, *, barcode=None, pid=None): try: - resp = _req( - "POST", - f"{API_BASE_URL}/accounts", - json={ - "first_name": first_name, - "last_name": last_name, - "email": email, - "pid": pid, - "rfid": rfid, - }, - timeout=30, - ) + payload = {"rfid": rfid} + if barcode: + payload["barcode"] = barcode + if pid: + payload["pid"] = pid + resp = _req("POST", f"{API_BASE_URL}/accounts", json=payload, timeout=30) resp.raise_for_status() return resp.json() except Exception as e: diff --git a/src/api/get_info_from_pid.py b/src/api/get_info_from_pid.py deleted file mode 100644 index af28c96..0000000 --- a/src/api/get_info_from_pid.py +++ /dev/null @@ -1,70 +0,0 @@ -import logging -from dataclasses import dataclass - -from config import API_BASE_URL -from api._client import _req - - -@dataclass -class StudentInfo: - first_name: str - last_name: str - emails: list - pid: str - first_enr_term: str - last_enr_term: str - - -class ContactClient: - def get_student_info(self, barcode): - try: - resp = _req("GET", f"{API_BASE_URL}/students/barcode/{barcode}", timeout=5) - if not resp.ok: - return False - d = resp.json() - return StudentInfo(d["first_name"], d["last_name"], d["emails"], d["pid"], d["first_enr_term"], d["last_enr_term"]) - except Exception as e: - logging.error(f"Error fetching student by barcode: {e}") - return False - - def get_student_info_pid(self, pid): - try: - resp = _req("GET", f"{API_BASE_URL}/students/pid/{pid}", timeout=5) - if not resp.ok: - return False - d = resp.json() - return StudentInfo(d["first_name"], d["last_name"], d["emails"], d["pid"], d["first_enr_term"], d["last_enr_term"]) - except Exception as e: - logging.error(f"Error fetching student by pid: {e}") - return False - - # TODO: I assume this was probably to add in support for employee checkin, when reimplemented - # TODO: it should be started with an implementation on the api side - # # not yet tested, still need to be authorized access to employeeData API. - # def get_staff_info(self, pid): - # if self.token["expires_at"] < time.time() + 60: - # self.token = self.oauth2_client.fetch_token( - # api_url + "token", grant_type="client_credentials" - # ) - # url = api_url + "employee_data/v1/employees/" + str(pid) - # token = self.token["access_token"] - # response = self.safe_get(url, token) - # if not response.ok: - # return False - # email = response.json()["officialEmail"] - # fname = response.json()["firstName"] - # lname = response.json()["lastName"] - # return [fname, lname, [email]] - # - # def safe_get(self, url, token, retries=2): - # for _ in range(retries): - # try: - # response = requests.get( - # url, headers={"Authorization": f"Bearer {token}"}, timeout=4 - # ) - # if response.ok: - # return response - # except (requests.exceptions.Timeout, requests.exceptions.ConnectionError): - # pass - # time.sleep(0.5) # small pause before retry - # return False diff --git a/src/api/traffic_light_api.py b/src/api/traffic_light_api.py index e571dd4..943bcc1 100644 --- a/src/api/traffic_light_api.py +++ b/src/api/traffic_light_api.py @@ -1,11 +1,11 @@ import threading -from api.sheets import SheetManager +from api.client import ApiClient from hardware.traffic_light import TrafficLight class TrafficLightApi: - def __init__(self, light: TrafficLight, sheets: SheetManager): + def __init__(self, light: TrafficLight, sheets: ApiClient): self._light = light self._sheets = sheets diff --git a/src/app_context.py b/src/app_context.py index da66cb1..904de3b 100644 --- a/src/app_context.py +++ b/src/app_context.py @@ -1,12 +1,12 @@ import threading -from api.sheets import SheetManager +from api.client import ApiClient from api.traffic_light_api import TrafficLightApi from hardware.traffic_light import TrafficLight class AppContext: - def __init__(self, sheets: SheetManager, traffic_light: TrafficLightApi): + def __init__(self, sheets: ApiClient, traffic_light: TrafficLightApi): self.sheets = sheets self.traffic_light = traffic_light self.window = None @@ -28,7 +28,7 @@ def rfid(self, value: str) -> None: @classmethod def create(cls, traffic_usb_id=None) -> "AppContext": - sheets = SheetManager() + sheets = ApiClient() light = TrafficLight(traffic_usb_id) traffic = TrafficLightApi(light, sheets) return cls(sheets, traffic) diff --git a/src/controllers/account_controller.py b/src/controllers/account_controller.py index e65b292..7fd9de2 100644 --- a/src/controllers/account_controller.py +++ b/src/controllers/account_controller.py @@ -1,4 +1,3 @@ -import time import tkinter import logging @@ -7,41 +6,14 @@ class AccountController: def __init__(self, ctx): self.ctx = ctx - def _email_check(self, email): - if "@" not in email or "." not in email: - return "Email is invalid" - return "good" + def create_account_from_barcode(self, barcode): + self._create(barcode=barcode) - def _name_check(self, fname, lname): - if len(fname) == 0 or len(lname) == 0: - return "Name was not entered" - return "good" - - def _id_check(self, user_id): - if len(user_id) <= 2 or len(user_id) > 12: - return "PID was not entered correctly" - return "good" - - def create_account(self, fname, lname, email, pid): - start = time.perf_counter() - idValid = self._id_check(pid) - emailValid = self._email_check(email) - nameValid = self._name_check(fname, lname) + def create_account_from_pid(self, pid): + self._create(pid=pid) + def _create(self, *, barcode=None, pid=None): canvas = self.ctx.window.canvas - - for validation in (idValid, emailValid, nameValid): - if validation != "good": - invalidID = tkinter.Label( - canvas, text=validation, bg="#153246", fg="white", font=("Arial", 20) - ) - invalidID.place(relx=0.5, rely=0.83, anchor="center") - invalidID.after(3000, lambda: invalidID.destroy()) - return - - end1 = time.perf_counter() - logging.debug(f"Time to validate info: {end1 - start}") - inProgress = tkinter.Label( canvas, text="Account creation in progress!", @@ -50,48 +22,18 @@ def create_account(self, fname, lname, email, pid): inProgress.place(relx=0.5, rely=0.87, anchor="center") self.ctx.window.update() - full_name = fname + " " + lname - logging.info(f"Creating user account for {full_name}") - - no_wifi = tkinter.Label( - canvas, - text="ERROR! Connection cannot be established, please let staff know.", - bg="#153246", fg="white", font=("Arial", 25), - ) - - end2 = time.perf_counter() - logging.debug(f"Time to structure row entries: {end2 - end1}") - - retries = 1 - while retries < 6: - try: - result = self.ctx.sheets.create_account(fname, lname, email, pid, self.ctx.rfid) - end3 = time.perf_counter() - logging.debug(f"Time to create account: {end3 - end2}") - - if result is None: - raise Exception("Account creation returned no result") - - break - except Exception as e: - logging.warning("Exception occurred while in account creation") - logging.exception("Exception occurred while in account creation") - no_wifi.place(relx=0.5, rely=0.91, anchor="center") - self.ctx.window.update() - time.sleep(retries) - retries += 1 - - no_wifi.destroy() + result = self.ctx.sheets.create_account(self.ctx.rfid, barcode=barcode, pid=pid) + inProgress.destroy() - if retries == 6: - self.ctx.nav.back_to_main() - inProgress.destroy() + if result is None: + error = tkinter.Label( + canvas, + text="ERROR! Could not create account, please try manually.", + bg="#153246", fg="white", font=("Arial", 20), + ) + error.place(relx=0.5, rely=0.87, anchor="center") + error.after(3000, lambda: error.destroy()) return - end4 = time.perf_counter() - logging.debug(f"Total time to send data: {end4 - end2}") - - inProgress.destroy() - # pop() runs the on_done continuation from the nav stack, - # which re-runs _run_check_in to handle waiver check and check-in. + logging.info("Account creation succeeded") self.ctx.nav.pop() diff --git a/src/controllers/navigation_controller.py b/src/controllers/navigation_controller.py index 4c3b6f5..669bd3b 100644 --- a/src/controllers/navigation_controller.py +++ b/src/controllers/navigation_controller.py @@ -96,6 +96,7 @@ def pop(self): def back_to_main(self): self._on_done_stack.clear() + self.ctx.rfid = "" self.ctx.traffic_light.request_off() self.show_frame(CheckInRFID) diff --git a/src/controllers/swipe_controller.py b/src/controllers/swipe_controller.py index 5d64be0..d3c26d6 100644 --- a/src/controllers/swipe_controller.py +++ b/src/controllers/swipe_controller.py @@ -1,9 +1,7 @@ import tkinter import logging -from screens.create_account_manual import CreateAccountManual from screens.create_account_barcode import CreateAccountBarcode -from screens.check_in_manual import CheckInManual -from api.get_info_from_pid import ContactClient +from screens.create_account_manual import CreateAccountManual class SwipeController: @@ -22,7 +20,7 @@ def _id_vet(self, id_check): def keyboard_press(self, key): curr_frame = self.ctx.nav.get_curr_frame() - if curr_frame not in (CreateAccountBarcode, CheckInManual): + if curr_frame not in (CreateAccountBarcode, CreateAccountManual): return self._id_string += key.char @@ -41,52 +39,10 @@ def keyboard_press(self, key): id_error.after(1500, lambda: self._destroy_swipe_error(id_error)) return - self._swipe_card(self._id_string) + logging.info(f"Card barcode read: {self._id_string.strip()!r}") + self.ctx.account.create_account_from_barcode(self._id_string.strip()) self._id_string = "" - def _pull_user(self, barcode, u_type): - logging.info(f"Card barcode read is: {barcode}. Trying to pull user...") - - contact = ContactClient() - try: - if u_type == "Staff": - u_info = contact.get_staff_info(barcode) - elif u_type == "Student": - u_info = contact.get_student_info(barcode) - except Exception: - logging.warning("An exception has ocurred with pulling user information", exc_info=True) - return None - - if not u_info: - logging.info("Student search returned False, returning...") - return None - - logging.info(f"Info pull succeeded:\n {u_info.first_name}, {u_info.last_name}, {u_info.pid}") - return u_info - - def _swipe_card(self, id_string): - u_data = self._pull_user(id_string.strip(), "Student") - if not u_data: - logging.info("Student search returned False, returning...") - return - - if self.ctx.nav.get_curr_frame() == CheckInManual: - self.ctx.nav.get_frame(CheckInManual).clear_entries() - self.ctx.nav.get_frame(CheckInManual).update_entries(u_data.pid) - return - - email_to_use = "" if len(u_data.emails) == 0 else u_data.emails[0] - for email in u_data.emails: - if email.endswith("@ucsd.edu"): - email_to_use = email - - manfill = self.ctx.nav.get_frame(CreateAccountManual) - manfill.clear_entries() - logging.info(f"Filling data with {u_data.first_name} {u_data.last_name} {email_to_use} {u_data.pid}") - manfill.update_entries(u_data.first_name, u_data.last_name, email_to_use, u_data.pid) - - self.ctx.nav.show_frame(CreateAccountManual) - def _destroy_swipe_error(self, id_error): id_error.destroy() self._swipe_error_shown = False diff --git a/src/main.py b/src/main.py index e91f1fd..70f0fc4 100644 --- a/src/main.py +++ b/src/main.py @@ -9,7 +9,7 @@ from screens.check_in_manual import CheckInManual from hardware.usb_ports import get_usb_ids from app_context import AppContext -from api.sheets import check_api_health +from api.client import check_api_health import logging import argparse from sys import stdout diff --git a/src/screens/components/dev_overlay.py b/src/screens/components/dev_overlay.py index 8254126..fbc827c 100644 --- a/src/screens/components/dev_overlay.py +++ b/src/screens/components/dev_overlay.py @@ -11,10 +11,12 @@ _DEV_NAME = "Dev User" _DEV_EMAIL = "devuser@ucsd.edu" _DEV_PID = "A12345678" +_DEV_RFID = "1a2b3c4d5e6f7g" _THANK_MSG = "Thank you for registering" def _sim_no_account_success(nav): + nav.ctx.rfid = _DEV_RFID def on_done(): nav.ctx.traffic_light.request_green() nav.get_frame(UserWelcome).display_name(_DEV_NAME, _THANK_MSG) @@ -22,13 +24,14 @@ def on_done(): def _sim_no_account_needs_waiver(nav): + nav.ctx.rfid = _DEV_RFID nav.go_to_create_account(on_done=nav.go_to_sign_waiver) def _sim_fill_and_go(nav): frm = nav.get_frame(CreateAccountManual) frm.clear_entries() - frm.update_entries("Dev", "User", _DEV_EMAIL, _DEV_PID) + frm.pid_entry.insert(0, _DEV_PID) nav.go_to_create_account_manual() diff --git a/src/screens/create_account_manual.py b/src/screens/create_account_manual.py index 7151cb7..7e5e45a 100644 --- a/src/screens/create_account_manual.py +++ b/src/screens/create_account_manual.py @@ -32,10 +32,7 @@ def _build(self, controller): self._image(1010.0, 77.0, image=icon_unchecked) field_img = self._photo(SHARED_PATH / "field.png") - self._image(640.0, 542.0, image=field_img) - self._image(640.0, 440.0, image=field_img) - self._image(640.0, 339.0, image=field_img) - self._image(640.0, 239.0, image=field_img) + self._image(640.0, 390.0, image=field_img) self._text( 250.0, 45.0, anchor="nw", @@ -46,19 +43,7 @@ def _build(self, controller): text="Waiver Status:", fill="#F5F0E6", font=("Montserrat", 40 * -1), ) self._text( - 640.0, 189.0, anchor="center", - text="First Name", fill="#F5F0E6", font=("Montserrat", 24 * -1), - ) - self._text( - 640.0, 290.0, anchor="center", - text="Last Name", fill="#F5F0E6", font=("Montserrat", 24 * -1), - ) - self._text( - 640.0, 391.0, anchor="center", - text="Email", fill="#F5F0E6", font=("Montserrat", 24 * -1), - ) - self._text( - 640.0, 492.0, anchor="center", + 640.0, 340.0, anchor="center", text="PID", fill="#F5F0E6", font=("Montserrat", 24 * -1), ) @@ -68,35 +53,21 @@ def _build(self, controller): borderwidth=0, highlightthickness=0, command=self._call_account_creation, relief="flat", ) - self._window(465.0, 598.0, btn, width=349, height=71) + self._window(465.0, 490.0, btn, width=349, height=71) - self.first_name_entry = self._canvas_entry(640.0, 239.0, w=800, h=44, font=("Montserrat", 20)) - self.last_name_entry = self._canvas_entry(640.0, 339.0, w=800, h=44, font=("Montserrat", 20)) - self.email_entry = self._canvas_entry(640.0, 440.0, w=800, h=44, font=("Montserrat", 20)) - self.pid_entry = self._canvas_entry(640.0, 542.0, w=800, h=44, font=("Montserrat", 20)) + self.pid_entry = self._canvas_entry(640.0, 390.0, w=800, h=44, font=("Montserrat", 20)) def hide(self): CanvasEntry.blur_all() super().hide() def clear_entries(self): - for entry in (self.first_name_entry, self.last_name_entry, - self.email_entry, self.pid_entry): - entry.delete(0, END) - - def update_entries(self, fname, lname, email, pid): - self.first_name_entry.insert(0, fname) - self.last_name_entry.insert(0, lname) - self.email_entry.insert(0, email) - self.pid_entry.insert(0, pid) + self.pid_entry.delete(0, END) def _call_account_creation(self): - first_name = self.first_name_entry.get() - last_name = self.last_name_entry.get() - email = self.email_entry.get() - pid = self.pid_entry.get() + pid = self.pid_entry.get() self.clear_entries() try: - self.controller.ctx.account.create_account(first_name, last_name, email, pid) + self.controller.ctx.account.create_account_from_pid(pid) except Exception: logging.warning("Error occurred trying to create a user account", exc_info=True)