Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions install.sh

This file was deleted.

20 changes: 20 additions & 0 deletions run_dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

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"

date "+%Y-%m-%d %H:%M:%S" >> "$output_file"

python src/main.py "$@" 2>&1 | tee -a log.txt
Empty file added src/api/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions src/api/_client.py
Original file line number Diff line number Diff line change
@@ -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
34 changes: 10 additions & 24 deletions src/sheets.py → src/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -31,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)
Expand All @@ -58,26 +50,20 @@ 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}")
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:
Expand Down
44 changes: 44 additions & 0 deletions src/api/traffic_light_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import threading

from api.client import ApiClient
from hardware.traffic_light import TrafficLight


class TrafficLightApi:
def __init__(self, light: TrafficLight, sheets: ApiClient):
self._light = light
self._sheets = sheets

@property
def connected(self) -> bool:
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(
target=self._sheets.set_traffic_light,
args=(color,),
daemon=True,
).start()

def request_red(self) -> None:
self._post("red")

def request_green(self) -> None:
self._post("green")

def request_yellow(self) -> None:
self._post("yellow")

def request_off(self) -> None:
self._post("off")
34 changes: 34 additions & 0 deletions src/app_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import threading

from api.client import ApiClient
from api.traffic_light_api import TrafficLightApi
from hardware.traffic_light import TrafficLight


class AppContext:
def __init__(self, sheets: ApiClient, traffic_light: TrafficLightApi):
self.sheets = sheets
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 = ""

@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 = ApiClient()
light = TrafficLight(traffic_usb_id)
traffic = TrafficLightApi(light, sheets)
return cls(sheets, traffic)
Binary file removed src/assets/check_in_no_id_assets/image_2.png
Binary file not shown.
Binary file removed src/assets/manual_fill_assets/image_4.png
Binary file not shown.
Binary file removed src/assets/manual_fill_assets/image_5.png
Binary file not shown.
Binary file removed src/assets/manual_fill_assets/image_6.png
Binary file not shown.
Binary file removed src/assets/manual_fill_assets/image_7.png
Binary file not shown.
Binary file removed src/assets/manual_fill_assets/image_8.png
Binary file not shown.
Binary file removed src/assets/manual_fill_assets/image_9.png
Binary file not shown.
Binary file removed src/assets/no_acc_no_waiver_swipe_assets/image_4.png
Binary file not shown.
Binary file removed src/assets/no_acc_no_waiver_swipe_assets/image_5.png
Binary file not shown.
Binary file removed src/assets/qr_codes_assets/image_3.png
Binary file not shown.
File renamed without changes
File renamed without changes
Binary file removed src/assets/waiver_no_acc_swipe_assets/button_1.png
Diff not rendered.
Binary file removed src/assets/waiver_no_acc_swipe_assets/image_2.png
Diff not rendered.
Binary file removed src/assets/waiver_no_acc_swipe_assets/image_3.png
Diff not rendered.
Binary file removed src/assets/waiver_no_acc_swipe_assets/image_4.png
Diff not rendered.
Binary file removed src/assets/waiver_no_acc_swipe_assets/image_5.png
Diff not rendered.
Empty file added src/controllers/__init__.py
Empty file.
39 changes: 39 additions & 0 deletions src/controllers/account_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import tkinter
import logging


class AccountController:
def __init__(self, ctx):
self.ctx = ctx

def create_account_from_barcode(self, barcode):
self._create(barcode=barcode)

def create_account_from_pid(self, pid):
self._create(pid=pid)

def _create(self, *, barcode=None, pid=None):
canvas = self.ctx.window.canvas
inProgress = tkinter.Label(
canvas,
text="Account creation in progress!",
bg="#153246", fg="white", font=("Arial", 25),
)
inProgress.place(relx=0.5, rely=0.87, anchor="center")
self.ctx.window.update()

result = self.ctx.sheets.create_account(self.ctx.rfid, barcode=barcode, pid=pid)
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

logging.info("Account creation succeeded")
self.ctx.nav.pop()
54 changes: 54 additions & 0 deletions src/controllers/check_in_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import logging
from tkinter import Label

from screens.user_welcome import UserWelcome


class CheckInController:
def __init__(self, ctx):
self.ctx = ctx

def handle_by_uuid(self, tag):
# 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):
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(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"No waiver for {identifier}")
self.ctx.traffic_light.request_yellow()
self.ctx.nav.go_to_sign_waiver()
return

logging.info(f"Check-in successful: {result['name']}")
self.ctx.traffic_light.request_green()
self.ctx.nav.get_frame(UserWelcome).display_name(result["name"], welcome_message)
Loading