nightly - 2025-09-27
This commit is contained in:
@@ -21,11 +21,19 @@ On the main menu press "Alt (or Option) + a" to toggle admin mode. Then click Pr
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
Ticket Form: **functional**
|
Ticket Form: **functional**
|
||||||
|
|
||||||
Basket Form: **functional**
|
Basket Form: **functional**
|
||||||
|
|
||||||
Drawing Form: **functional**
|
Drawing Form: **functional**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
By Name Report: **functional**
|
By Name Report: **functional**
|
||||||
|
|
||||||
Basket ID Report: **functional**
|
Basket ID Report: **functional**
|
||||||
|
|
||||||
Ticket Count Report: **not started**
|
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.
|
Deployment System: **not started** | Likely will Dockerize it first, then will move to possible Electron or Tauri option for desktop application.
|
||||||
@@ -8,6 +8,7 @@ from routers.tickets import ticket_router
|
|||||||
from routers.baskets import basket_router
|
from routers.baskets import basket_router
|
||||||
from routers.combined import combined_router
|
from routers.combined import combined_router
|
||||||
from routers.reports import report_router
|
from routers.reports import report_router
|
||||||
|
from routers.backuprestore import backup_router
|
||||||
|
|
||||||
if argv[1] == "run":
|
if argv[1] == "run":
|
||||||
app = FastAPI(title="TAM3 API Server", docs_url=None, redoc_url=None)
|
app = FastAPI(title="TAM3 API Server", docs_url=None, redoc_url=None)
|
||||||
@@ -18,4 +19,5 @@ app.include_router(prefix_router)
|
|||||||
app.include_router(ticket_router)
|
app.include_router(ticket_router)
|
||||||
app.include_router(basket_router)
|
app.include_router(basket_router)
|
||||||
app.include_router(combined_router)
|
app.include_router(combined_router)
|
||||||
app.include_router(report_router)
|
app.include_router(report_router)
|
||||||
|
app.include_router(backup_router)
|
||||||
@@ -26,12 +26,16 @@ class PrefixRepo(Repo):
|
|||||||
return Prefix(*result)
|
return Prefix(*result)
|
||||||
|
|
||||||
def add_one(self, prefix: Prefix) -> str:
|
def add_one(self, prefix: Prefix) -> str:
|
||||||
self.cur.execute(
|
self.cur.execute("REPLACE INTO prefixes VALUES (%s, %s, %s)", (prefix.name, prefix.color, prefix.weight))
|
||||||
"REPLACE INTO prefixes VALUES (%s, %s, %s)",
|
|
||||||
(prefix.name, prefix.color, prefix.weight),
|
|
||||||
)
|
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
return "Prefix inserted successfully."
|
return "Prefix inserted successfully."
|
||||||
|
|
||||||
|
def add_list(self, prefix_list: list[Prefix]):
|
||||||
|
for prefix in prefix_list:
|
||||||
|
self.cur.execute("REPLACE INTO prefixes VALUES (%s, %s, %s)", (prefix.name, prefix.color, prefix.weight))
|
||||||
|
self.conn.commit()
|
||||||
|
return "Prefixes inserted successfully."
|
||||||
|
|
||||||
|
|
||||||
def del_one(self, prefix_name: str) -> str:
|
def del_one(self, prefix_name: str) -> str:
|
||||||
self.cur.execute("DELETE FROM prefixes WHERE name = %s", (prefix_name,))
|
self.cur.execute("DELETE FROM prefixes WHERE name = %s", (prefix_name,))
|
||||||
|
|||||||
40
api/routers/backuprestore.py
Normal file
40
api/routers/backuprestore.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from fastapi import APIRouter, Body
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List
|
||||||
|
from exceptions import bad_key
|
||||||
|
|
||||||
|
from repos.api_keys import ApiKeyRepo
|
||||||
|
from repos.prefixes import Prefix, PrefixRepo
|
||||||
|
from repos.tickets import Ticket, TicketRepo
|
||||||
|
from repos.baskets import Basket, BasketRepo
|
||||||
|
from repos.template import Repo
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BackupFile:
|
||||||
|
prefixes: List[Prefix] = field(default_factory=list)
|
||||||
|
tickets: List[Ticket] = field(default_factory=list)
|
||||||
|
baskets: List[Basket] = field(default_factory=list)
|
||||||
|
|
||||||
|
class BackupFileRepo:
|
||||||
|
def get_file(self):
|
||||||
|
new_file = BackupFile(prefixes=PrefixRepo().get_all(), tickets=TicketRepo().get_all(), baskets=BasketRepo().get_all())
|
||||||
|
return new_file
|
||||||
|
def post_file(self, uploaded_file: BackupFile) -> str:
|
||||||
|
PrefixRepo().add_list(uploaded_file.prefixes)
|
||||||
|
TicketRepo().post_list(uploaded_file.tickets)
|
||||||
|
BasketRepo().post_list(uploaded_file.baskets)
|
||||||
|
return "File posted successfully."
|
||||||
|
|
||||||
|
backup_router = APIRouter(prefix="/api/backuprestore")
|
||||||
|
|
||||||
|
@backup_router.get("/")
|
||||||
|
def get_backup_file(api_key: str):
|
||||||
|
if not ApiKeyRepo().check_api(api_key):
|
||||||
|
raise bad_key
|
||||||
|
return BackupFileRepo().get_file()
|
||||||
|
|
||||||
|
@backup_router.post("/")
|
||||||
|
def post_backup_file(api_key: str, uploaded_file: BackupFile):
|
||||||
|
if not ApiKeyRepo().check_api(api_key):
|
||||||
|
raise bad_key
|
||||||
|
return {"detail": BackupFileRepo().post_file(uploaded_file)}
|
||||||
45
webapp/src/routes/api/backuprestore/+server.js
Normal file
45
webapp/src/routes/api/backuprestore/+server.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
import { db } from "$lib/server/db";
|
||||||
|
import { prefixes, tickets, baskets } from "$lib/server/db/schema";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
if (env.TAM3_REMOTE) {
|
||||||
|
const res = await fetch(`${env.TAM3_REMOTE}/api/backuprestore/?api_key=${env.TAM3_REMOTE_KEY}`);
|
||||||
|
if (!res.ok) {
|
||||||
|
return new Response(JSON.stringify({detail: "Unable to get backup file"}), {status: res.status, statusText: res.statusText})
|
||||||
|
};
|
||||||
|
const data = await res.json();
|
||||||
|
return new Response(JSON.stringify(data), {status: 200, statusText: "Fetched backup file successfully."})
|
||||||
|
} else {
|
||||||
|
const p_data = await db.select().from(prefixes);
|
||||||
|
const t_data = await db.select().from(tickets);
|
||||||
|
const b_data = await db.select().from(baskets);
|
||||||
|
return new Response(JSON.stringify({prefixes: p_data, tickets: t_data, baskets: b_data}), {status: 200, statusText: "Loaded backup file successfully."})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST({ request }) {
|
||||||
|
const req = await request.json();
|
||||||
|
for (let prefix of req.prefixes) {
|
||||||
|
await db.insert(prefixes).values({name: prefix.name, color: prefix.color, weight: prefix.weight}).onConflictDoUpdate({target: prefixes.name, set: {color: prefix.color, weight: prefix.weight}});
|
||||||
|
};
|
||||||
|
for (let ticket of req.tickets) {
|
||||||
|
await db.insert(tickets).values({prefix: ticket.prefix, t_id: ticket.t_id, first_name: ticket.first_name, last_name: ticket.last_name, phone_number: ticket.phone_number, preference: ticket.preference})
|
||||||
|
.onConflictDoUpdate({target: [tickets.prefix, tickets.t_id], set: {first_name: ticket.first_name, last_name: ticket.last_name, phone_number: ticket.phone_number, preference: ticket.preference}});
|
||||||
|
};
|
||||||
|
for (let basket of req.baskets) {
|
||||||
|
await db.insert(baskets).values({prefix: basket.prefix, b_id: basket.b_id, description: basket.description, donors: basket.donors, winning_ticket: basket.winning_ticket})
|
||||||
|
.onConflictDoUpdate({target: [baskets.prefix, baskets.b_id], set: {description: basket.description, donors: basket.donors, winning_ticket: basket.winning_ticket}})
|
||||||
|
};
|
||||||
|
if (env.TAM3_REMOTE) {
|
||||||
|
const res = await fetch(`${env.TAM3_REMOTE}/api/backuprestore/?api_key=${env.TAM3_REMOTE_KEY}`, {
|
||||||
|
body: JSON.stringify(req),
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'}
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
return new Response(JSON.stringify({detail: "Error posting file."}), {status: res.status, statusText: "Error posting file."})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Response(JSON.stringify({detail: "File uploaded successfully"}), {status: 200, statusText: "File uploaded successfully."})
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@
|
|||||||
|
|
||||||
const functions = {
|
const functions = {
|
||||||
refreshPage: async () => {
|
refreshPage: async () => {
|
||||||
|
if (current_drawings.length > 0) {
|
||||||
|
functions.saveAll()
|
||||||
|
}
|
||||||
const res = await fetch(`/api/combined/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
const res = await fetch(`/api/combined/${prefix.name}/${pagerForm.id_from}/${pagerForm.id_to}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
document.title = `${prefix.name} Ticket Entry`
|
document.title = `${prefix.name} Ticket Entry`;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user