diff --git a/README.md b/README.md
index f970168..851b6d3 100644
--- a/README.md
+++ b/README.md
@@ -16,4 +16,16 @@ After cloning, cd'ing into webapp, and running `npm install` followed by `npm ru
That is making prefixes.
-On the main menu press "Alt (or Option) + a" to toggle admin mode. Then click Prefix editor to open the form to edit prefixes.
\ No newline at end of file
+On the main menu press "Alt (or Option) + a" to toggle admin mode. Then click Prefix editor to open the form to edit prefixes.
+
+## Status
+
+Ticket Form: **functional**
+Basket Form: **functional**
+Drawing Form: **functional**
+
+By Name Report: **functional**
+Basket ID Report: **functional**
+Ticket Count Report: **not started**
+
+Deployment System: **not started** | Likely will Dockerize it first, then will move to possible Electron or Tauri option for desktop application.
\ No newline at end of file
diff --git a/api/main.py b/api/main.py
index 646f243..fee52f1 100644
--- a/api/main.py
+++ b/api/main.py
@@ -7,6 +7,7 @@ from routers.prefixes import prefix_router
from routers.tickets import ticket_router
from routers.baskets import basket_router
from routers.combined import combined_router
+from routers.reports import report_router
if argv[1] == "run":
app = FastAPI(title="TAM3 API Server", docs_url=None, redoc_url=None)
@@ -16,4 +17,5 @@ else:
app.include_router(prefix_router)
app.include_router(ticket_router)
app.include_router(basket_router)
-app.include_router(combined_router)
\ No newline at end of file
+app.include_router(combined_router)
+app.include_router(report_router)
\ No newline at end of file
diff --git a/api/repos/reports.py b/api/repos/reports.py
new file mode 100644
index 0000000..2786a8b
--- /dev/null
+++ b/api/repos/reports.py
@@ -0,0 +1,22 @@
+from dataclasses import dataclass
+from .template import Repo
+
+@dataclass
+class ReportItem:
+ prefix: str
+ winner_name: str
+ phone_number: str | None
+ preference: str
+ b_id: int
+ winning_ticket: int
+ description: str
+
+class ReportRepo(Repo):
+ def get_byname(self, prefix: str) -> list[ReportItem]:
+ self.cur.execute("SELECT * FROM report WHERE prefix = %s", (prefix,))
+ results = self.cur.fetchall()
+ return [ReportItem(*r) for r in results]
+ def get_bybasket(self, prefix: str) -> list[ReportItem]:
+ self.cur.execute("SELECT * FROM report WHERE prefix = %s ORDER BY b_id ASC", (prefix,))
+ results = self.cur.fetchall()
+ return [ReportItem(*r) for r in results]
\ No newline at end of file
diff --git a/api/routers/reports.py b/api/routers/reports.py
new file mode 100644
index 0000000..98e20d4
--- /dev/null
+++ b/api/routers/reports.py
@@ -0,0 +1,18 @@
+from fastapi import APIRouter
+from repos.reports import ReportItem, ReportRepo
+from repos.api_keys import ApiKeyRepo
+from exceptions import bad_key
+
+report_router = APIRouter(prefix="/api/reports")
+
+@report_router.get("/byname/{prefix}/")
+def get_report_byname(api_key: str, prefix: str):
+ if not ApiKeyRepo().check_api(api_key):
+ raise bad_key
+ return ReportRepo().get_byname(prefix)
+
+@report_router.get("/bybasket/{prefix}/")
+def get_report_bybasket(api_key: str, prefix: str):
+ if not ApiKeyRepo().check_api(api_key):
+ raise bad_key
+ return ReportRepo().get_bybasket(prefix)
\ No newline at end of file
diff --git a/db/schema.sql b/db/schema.sql
index 79ba527..a6bb6d3 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -12,19 +12,19 @@ CREATE TABLE IF NOT EXISTS prefixes (
CREATE TABLE IF NOT EXISTS tickets (
`prefix` VARCHAR(255),
`t_id` INT,
- `first_name` VARCHAR(255),
- `last_name` VARCHAR(255),
- `phone_number` VARCHAR(255),
- `preference` VARCHAR(20),
+ `first_name` VARCHAR(255) DEFAULT "",
+ `last_name` VARCHAR(255) DEFAULT "",
+ `phone_number` VARCHAR(255) DEFAULT "",
+ `preference` VARCHAR(20) DEFAULT "CALL",
PRIMARY KEY (`prefix`, `t_id`)
);
CREATE TABLE IF NOT EXISTS baskets (
`prefix` VARCHAR(255),
`b_id` INT,
- `description` VARCHAR(255),
- `donors` VARCHAR(255),
- `winning_ticket` INT,
+ `description` VARCHAR(255) DEFAULT "",
+ `donors` VARCHAR(255) DEFAULT "",
+ `winning_ticket` INT DEFAULT 0,
PRIMARY KEY (`prefix`, `b_id`)
);
diff --git a/testdata/tam3_name.py b/testdata/tam3_name.py
new file mode 100644
index 0000000..bd795a1
--- /dev/null
+++ b/testdata/tam3_name.py
@@ -0,0 +1,27 @@
+from sys import argv
+from names import get_full_name
+import random as r
+
+def gen_phone_number():
+ def d0():
+ return r.randint(0, 1)
+ def d():
+ return r.randint(0, 9)
+ return f"{d0()}{d()}{d()}-{d()}{d()}{d()}-{d()}{d()}{d()}{d()}"
+
+class Person:
+ def __init__(self):
+ self.name = get_full_name()
+ self.phone_number = gen_phone_number()
+ self.preference = r.choice(("TEXT", "TEXT", "TEXT", "CALL"))
+ def __repr__(self):
+ return f"{self.name} {self.phone_number} {self.preference}"
+
+how_many = input("Insert how many random people to generate: ")
+try:
+ how_many = int(how_many)
+except:
+ print("Try entering an integer next time.")
+
+for i in range(0, int(how_many)):
+ print(Person())
\ No newline at end of file
diff --git a/testdata/tam3_ticket_num.py b/testdata/tam3_ticket_num.py
new file mode 100644
index 0000000..f651815
--- /dev/null
+++ b/testdata/tam3_ticket_num.py
@@ -0,0 +1,20 @@
+import random as r
+
+how_many = input("Enter how many numbers you want to generate: ")
+try:
+ how_many = int(how_many)
+except:
+ print("Enter an integer next time.")
+ quit(1)
+
+ticket_range = input("Enter the start and end numbers of the random range you want to use, separated by a hyphen: ")
+
+ticket_range = ticket_range.split("-")
+try:
+ ticket_range = [int(i) for i in ticket_range]
+except:
+ print("Invalid range entered. Needs to be something like \"1-20\"")
+ quit(1)
+
+for i in range(0, how_many):
+ print(r.randint(ticket_range[0], ticket_range[1]))
\ No newline at end of file
diff --git a/webapp/src/lib/server/db/schema.js b/webapp/src/lib/server/db/schema.js
index 1d8b8cb..38de75c 100644
--- a/webapp/src/lib/server/db/schema.js
+++ b/webapp/src/lib/server/db/schema.js
@@ -10,10 +10,10 @@ export const prefixes = sqliteTable('prefixes', {
export const tickets = sqliteTable('tickets', {
prefix: text('prefix'),
t_id: integer('t_id'),
- first_name: text('first_name'),
- last_name: text('last_name'),
- phone_number: text('phone_number'),
- preference: text('preference')
+ first_name: text('first_name').default(''),
+ last_name: text('last_name').default(''),
+ phone_number: text('phone_number').default(''),
+ preference: text('preference').default('CALL')
}, (table) => [
primaryKey({columns: [table.prefix, table.t_id]})
]);
@@ -21,8 +21,8 @@ export const tickets = sqliteTable('tickets', {
export const baskets = sqliteTable('baskets', {
prefix: text('prefix'),
b_id: integer('b_id'),
- description: text('description'),
- donors: text('donors'),
+ description: text('description').default(''),
+ donors: text('donors').default(''),
winning_ticket: integer('winning_ticket').default(0)
}, (table) => [
primaryKey({columns: [table.prefix, table.b_id]})
diff --git a/webapp/src/routes/+page.js b/webapp/src/routes/+page.js
index 44022f6..05a53d5 100644
--- a/webapp/src/routes/+page.js
+++ b/webapp/src/routes/+page.js
@@ -1,5 +1,5 @@
export async function load({ fetch }) {
- const res = await fetch('/api/prefixes/');
+ const res = await fetch('/api/prefixes');
if (res.ok) {
const data = await res.json();
return { prefixes: data, status: "Prefixes fetched successfully." }
diff --git a/webapp/src/routes/+page.svelte b/webapp/src/routes/+page.svelte
index d9708e0..e629a2e 100644
--- a/webapp/src/routes/+page.svelte
+++ b/webapp/src/routes/+page.svelte
@@ -47,8 +47,8 @@
Reports:
{#if admin_mode}
Admin Mode:
diff --git a/webapp/src/routes/api/baskets/[prefix]/[b_from]/[b_to]/+server.js b/webapp/src/routes/api/baskets/[prefix]/[b_from]/[b_to]/+server.js
index 4e85fe3..d10a07b 100644
--- a/webapp/src/routes/api/baskets/[prefix]/[b_from]/[b_to]/+server.js
+++ b/webapp/src/routes/api/baskets/[prefix]/[b_from]/[b_to]/+server.js
@@ -6,7 +6,7 @@ import { eq, and } from "drizzle-orm";
export async function GET({ params }) {
let n_b_from = parseInt(params.b_from), n_b_to = parseInt(params.b_to);
if (env.TAM3_REMOTE) {
- const res = await fetch(`${env.TAM3_REMOTE}/api/baskets/${n_b_from}/${n_b_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
+ const res = await fetch(`${env.TAM3_REMOTE}/api/baskets/${params.prefix}/${n_b_from}/${n_b_to}/?api_key=${env.TAM3_REMOTE_KEY}`);
if (!res.ok) {
return new Response(JSON.stringify({detail: "Unable to fetch baskets"}), {status: res.status, statusText: res.statusText});
};
diff --git a/webapp/src/routes/api/prefixes/[name]/+server.js b/webapp/src/routes/api/prefixes/[name]/+server.js
index 8eebe5f..eb1b7f9 100644
--- a/webapp/src/routes/api/prefixes/[name]/+server.js
+++ b/webapp/src/routes/api/prefixes/[name]/+server.js
@@ -6,7 +6,7 @@ import { env } from "$env/dynamic/private";
export async function GET({ params }) {
let { name } = params;
if (env.TAM3_REMOTE) {
- const res = await fetch(`${env.TAM3_REMOTE}/prefixes/${name}/?api_key=${env.TAM3_REMOTE_KEY}`);
+ const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/${name}/?api_key=${env.TAM3_REMOTE_KEY}`);
if (!res.ok) {
return new Response(JSON.stringify({status: "Issue getting prefix."}), {status: res.status, statusText: res.statusText});
}
@@ -26,7 +26,7 @@ export async function DELETE({ params }) {
let { name } = params;
await db.delete(prefixes).where(eq(prefixes.name, name))
if (env.TAM3_REMOTE) {
- const res = await fetch(`${env.TAM3_REMOTE}/prefixes/?api_key=${env.TAM3_REMOTE_KEY}&prefix_name=${name}`);
+ const res = await fetch(`${env.TAM3_REMOTE}/api/prefixes/?api_key=${env.TAM3_REMOTE_KEY}&prefix_name=${name}`);
if (!res.ok) {
return new Response(JSON.stringify({status: "Issue deleting prefix."}), {status: res.status, statusText: res.statusText});
} else {
diff --git a/webapp/src/routes/api/reports/bybasket/[prefix]/+server.js b/webapp/src/routes/api/reports/bybasket/[prefix]/+server.js
new file mode 100644
index 0000000..d545faf
--- /dev/null
+++ b/webapp/src/routes/api/reports/bybasket/[prefix]/+server.js
@@ -0,0 +1,18 @@
+import { env } from "process";
+import { db } from "$lib/server/db";
+import { report } from "$lib/server/db/schema";
+import { eq } from "drizzle-orm";
+
+export async function GET({ params }) {
+ if (env.TAM3_REMOTE) {
+ const res = await fetch(`${env.TAM3_REMOTE}/api/reports/bybasket/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
+ if (!res.ok) {
+ return new Response(JSON.stringify({detail: "Unable to fetch report."}), {status: res.status, statusText: res.statusText})
+ }
+ const data = await res.json();
+ return new Response(JSON.stringify(data), {status: 200, statusText: "Fetched report successfully."})
+ } else {
+ const data = await db.select().from(report).where(eq(report.prefix, params.prefix)).orderBy(report.b_id);
+ return new Response(JSON.stringify(data), {status: 200, statusText: "Loaded report successfully."})
+ }
+}
\ No newline at end of file
diff --git a/webapp/src/routes/api/reports/byname/[prefix]/+server.js b/webapp/src/routes/api/reports/byname/[prefix]/+server.js
new file mode 100644
index 0000000..5157995
--- /dev/null
+++ b/webapp/src/routes/api/reports/byname/[prefix]/+server.js
@@ -0,0 +1,18 @@
+import { env } from "process";
+import { db } from "$lib/server/db";
+import { report } from "$lib/server/db/schema";
+import { eq } from "drizzle-orm";
+
+export async function GET({ params }) {
+ if (env.TAM3_REMOTE) {
+ const res = await fetch(`${env.TAM3_REMOTE}/api/reports/byname/${params.prefix}/?api_key=${env.TAM3_REMOTE_KEY}`);
+ if (!res.ok) {
+ return new Response(JSON.stringify({detail: "Unable to fetch report."}), {status: res.status, statusText: res.statusText})
+ }
+ const data = await res.json();
+ return new Response(JSON.stringify(data), {status: 200, statusText: "Fetched report successfully."})
+ } else {
+ const data = await db.select().from(report).where(eq(report.prefix, params.prefix));
+ return new Response(JSON.stringify(data), {status: 200, statusText: "Loaded report successfully."})
+ }
+}
\ No newline at end of file
diff --git a/webapp/src/routes/api/tickets/[prefix]/[t_id]/+server.js b/webapp/src/routes/api/tickets/[prefix]/[t_id]/+server.js
index 05bf0ea..218345a 100644
--- a/webapp/src/routes/api/tickets/[prefix]/[t_id]/+server.js
+++ b/webapp/src/routes/api/tickets/[prefix]/[t_id]/+server.js
@@ -5,7 +5,7 @@ import { eq, and } from "drizzle-orm";
export async function GET({ params }) {
if (env.TAM3_REMOTE) {
- const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/${params.prefix}/${params.t_id}/?api_key=${env.TAM3_REMOTE}`);
+ const res = await fetch(`${env.TAM3_REMOTE}/api/tickets/${params.prefix}/${params.t_id}/?api_key=${env.TAM3_REMOTE_KEY}`);
if (!res.ok) {
return new Response(JSON.stringify({detail: "Unable to fetch ticket."}), {status: res.status, statusText: res.statusText})
}
diff --git a/webapp/src/routes/baskets/[prefix]/+page.svelte b/webapp/src/routes/baskets/[prefix]/+page.svelte
index 5450ae8..d839aa0 100644
--- a/webapp/src/routes/baskets/[prefix]/+page.svelte
+++ b/webapp/src/routes/baskets/[prefix]/+page.svelte
@@ -44,7 +44,7 @@
duplicateDown: () => {
const next_idx = current_idx + 1;
if (current_baskets[next_idx]) {
- current_baskets[next_idx] = {...current_baskets[current_idx], b_id: current_baskets[next_idx].b_id, changed: true};
+ current_baskets[next_idx] = {...current_baskets[current_idx], b_id: current_baskets[next_idx].b_id, winning_ticket: current_baskets[next_idx].winning_ticket, changed: true};
changeFocus(next_idx);
} else {
changeFocus(next_idx);
@@ -53,7 +53,7 @@
duplicateUp: () => {
const prev_idx = current_idx - 1;
if (prev_idx >= 0) {
- current_baskets[prev_idx] = {...current_baskets[current_idx], b_id: current_baskets[prev_idx].b_id, changed: true};
+ current_baskets[prev_idx] = {...current_baskets[current_idx], b_id: current_baskets[prev_idx].b_id, winning_ticket: current_baskets[prev_idx].winning_ticket, changed: true};
changeFocus(prev_idx);
} else {
changeFocus(prev_idx);
diff --git a/webapp/src/routes/reports/bybasket/[prefix]/+page.js b/webapp/src/routes/reports/bybasket/[prefix]/+page.js
new file mode 100644
index 0000000..40b5754
--- /dev/null
+++ b/webapp/src/routes/reports/bybasket/[prefix]/+page.js
@@ -0,0 +1,7 @@
+export async function load({ fetch, params }) {
+ let res = await fetch(`/api/prefixes/${params.prefix}`);
+ const prefix_data = await res.json();
+ res = await fetch(`/api/reports/bybasket/${params.prefix}`);
+ const report_data = await res.json()
+ return {prefix: prefix_data, report: report_data}
+}
\ No newline at end of file
diff --git a/webapp/src/routes/reports/bybasket/[prefix]/+page.svelte b/webapp/src/routes/reports/bybasket/[prefix]/+page.svelte
new file mode 100644
index 0000000..f215417
--- /dev/null
+++ b/webapp/src/routes/reports/bybasket/[prefix]/+page.svelte
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+ {prefix.name} - Report - {report_subject} |
+
+
+ | Basket ID |
+ Description |
+ Ticket # |
+ Winner Name |
+ Phone Number |
+
+
+
+ {#each show_data as report_entry}
+
+ | {report_entry.b_id} |
+ {report_entry.description} |
+ {report_entry.winning_ticket} |
+ {report_entry.winner_name} |
+ {report_entry.phone_number} |
+
+ {/each}
+
+
+
+ | {env.PUBLIC_TAM3_VENUE || ""} |
+ TAM3 by Dilan Gilluly |
+
+
+
+
+
\ No newline at end of file
diff --git a/webapp/src/routes/reports/byname/[prefix]/+page.js b/webapp/src/routes/reports/byname/[prefix]/+page.js
new file mode 100644
index 0000000..8bfff47
--- /dev/null
+++ b/webapp/src/routes/reports/byname/[prefix]/+page.js
@@ -0,0 +1,7 @@
+export async function load({ fetch, params }) {
+ let res = await fetch(`/api/prefixes/${params.prefix}`);
+ const prefix_data = await res.json();
+ res = await fetch(`/api/reports/byname/${params.prefix}`);
+ const report_data = await res.json()
+ return {prefix: prefix_data, report: report_data}
+}
\ No newline at end of file
diff --git a/webapp/src/routes/reports/byname/[prefix]/+page.svelte b/webapp/src/routes/reports/byname/[prefix]/+page.svelte
new file mode 100644
index 0000000..8dda445
--- /dev/null
+++ b/webapp/src/routes/reports/byname/[prefix]/+page.svelte
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+ {prefix.name} - Report - {report_subject} |
+
+
+ | Winner Name |
+ Phone Number |
+ Basket ID |
+ Ticket # |
+ Description |
+
+
+
+ {#each show_data as report_entry}
+
+ | {report_entry.winner_name} |
+ {report_entry.phone_number} |
+ {report_entry.b_id} |
+ {report_entry.winning_ticket} |
+ {report_entry.description} |
+
+ {/each}
+
+
+
+ | {env.PUBLIC_TAM3_VENUE || ""} |
+ TAM3 by Dilan Gilluly |
+
+
+
+
+
\ No newline at end of file