summaryrefslogtreecommitdiff
path: root/app/src/lib/server/assignments.ts
diff options
context:
space:
mode:
authorLinnnus <[email protected]>2025-02-19 18:37:33 +0100
committerLinnnus <[email protected]>2025-02-19 18:37:51 +0100
commitf7a6244cc1a8f39ad44186a4da6b743ab6821d51 (patch)
tree54e63d1509f56caaac542a4548325f9716d42069 /app/src/lib/server/assignments.ts
parent80a432fbda4ca2f35286197c636ad2a733ca4b5a (diff)
Add assignment submission
Diffstat (limited to 'app/src/lib/server/assignments.ts')
-rw-r--r--app/src/lib/server/assignments.ts106
1 files changed, 83 insertions, 23 deletions
diff --git a/app/src/lib/server/assignments.ts b/app/src/lib/server/assignments.ts
index 7622408..787865c 100644
--- a/app/src/lib/server/assignments.ts
+++ b/app/src/lib/server/assignments.ts
@@ -1,20 +1,6 @@
-import type pg from "pg";
-
-/** A row from the `cemetary_plot` table. */
-export interface CemetaryPlot {
- id: number;
- address: string;
- ownerId: number;
- //assignmentInterval: Date;
-}
-
-/** A row from the `assignments` table. */
-export interface Assignment {
- id: number;
- gardenerId: number;
- cemetaryPlotId: number;
- date: Date;
-}
+import { PutObjectCommand, type S3Client } from "@aws-sdk/client-s3"; /*=;* /
+import type { ClientBase } from "pg";
+import type { CemetaryPlot, Assignment, AssignmentState } from "../common/assignments";
/**
* Retrieves all assignments for the given user.
@@ -22,7 +8,7 @@ export interface Assignment {
* @param dbConn Connection to database.
* @param userId ID used to identify user.
*/
-export async function getAssignments(dbConn: pg.ClientBase, userId: number): Promise<Assignment[]> {
+export async function getAssignments(dbConn: ClientBase, userId: number): Promise<Assignment[]> {
const result = await dbConn.query(
"SELECT * FROM assignments WHERE gardener_id = $1 ORDER BY date",
[userId],
@@ -34,6 +20,7 @@ export async function getAssignments(dbConn: pg.ClientBase, userId: number): Pro
gardenerId: r.gardener_id,
cemetaryPlotId: r.cemetary_plot_id,
date: r.date,
+ state: r.state,
}) satisfies Assignment,
);
}
@@ -46,7 +33,7 @@ type GetAssignmentResult =
* Retrieves a specfic assignment, along with relevant cemetary plot.
*/
export async function getAssignmentAndCemetaryById(
- dbConn: pg.ClientBase,
+ dbConn: ClientBase,
assignmentId: number,
): Promise<GetAssignmentResult> {
const queryText = `SELECT
@@ -54,6 +41,7 @@ export async function getAssignmentAndCemetaryById(
a.gardener_id,
a.cemetary_plot_id,
a.date,
+ a.state,
c.id,
c.address,
c.owner_id,
@@ -73,12 +61,84 @@ export async function getAssignmentAndCemetaryById(
gardenerId: result.rows[0][1],
cemetaryPlotId: result.rows[0][2],
date: result.rows[0][3],
+ state: result.rows[0][4],
};
const cemetaryPlot: CemetaryPlot = {
- id: result.rows[0][4],
- address: result.rows[0][5],
- ownerId: result.rows[0][6],
- //assignmentInterval: result.rows[0][7],
+ id: result.rows[0][5],
+ address: result.rows[0][6],
+ ownerId: result.rows[0][7],
+ //assignmentInterval: result.rows[0][8],
};
return { assignment, cemetaryPlot };
}
+
+export interface FinishAssignmentArgs {
+ images: { bytes: Uint8Array; name: string }[];
+ note?: string;
+ assignmentId: number;
+}
+
+// TODO: Error recovery.
+export async function finishAssignment(
+ dbConn: ClientBase,
+ s3Client: S3Client,
+ { images, note, assignmentId }: FinishAssignmentArgs,
+): Promise<void> {
+ // Upload to S3, returning path
+ // FIXME: Should be factored out?
+ const uploadPromises = images.map(async (image) => {
+ const key = generateImageKey();
+
+ const cmd = new PutObjectCommand({
+ Bucket: "images",
+ Key: key,
+ Body: image.bytes,
+ IfNoneMatch: "*", // Error, in case of key collision.
+ });
+
+ await s3Client.send(cmd);
+ return { ...image, key };
+ });
+ const uploadedImages = await Promise.all(uploadPromises);
+
+ // TODO: Add beanstalkd job
+
+ await dbConn.query("BEGIN");
+
+ try {
+ // Create 'images' row for each image.
+ // FIXME: Apparently node-pg doesn't have an equivalent to Python's `insert_many`??
+ for (const image of uploadedImages) {
+ dbConn.query({
+ name: "insert-assignment-image",
+ text: "INSERT INTO images(s3_path, original_filename, assignment_id) VALUES ($1, $2, $3)",
+ values: [image.key, image.name, assignmentId],
+ });
+ }
+
+ // Update the assingment's state.
+ dbConn.query(
+ `UPDATE
+ assignments
+ SET
+ state = 'AWAITING_WATERMARKING' :: assignment_state,
+ note = $1
+ WHERE
+ id = $2`,
+ [note, assignmentId],
+ );
+
+ dbConn.query("COMMIT");
+ } catch (err) {
+ // We should probably try to delete S3 objects.
+
+ // We should probably try to delete the job.
+
+ await dbConn.query("ROLLBACK");
+ throw err;
+ }
+}
+
+function generateImageKey(): string {
+ return crypto.randomUUID();
+}