Skip to content
Open
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
28 changes: 22 additions & 6 deletions src/routes/dashboard/admin/print/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@ export async function load({ locals }) {
throw error(403, { message: 'oi get out' });
}

const projects = await getProjects(['t1_approved'], [], []);

const allowedStatuses: (typeof project.status._.data)[] = ['printing', 't1_approved'];
const projects = await getProjects(allowedStatuses, [], []);

const allProjects = await db
.select({
id: project.id,
name: project.name
name: project.name,
status: project.status,
userId: project.userId
})
.from(project)
.where(and(eq(project.deleted, false)));
.where(and(
eq(project.deleted, false),
inArray(project.status, ['printing', 't1_approved', 'printed']),
sql`(${project.status} != 'printed' OR ${project.userId} = ${locals.user.id})`
));

const users = await db
.select({
Expand All @@ -37,7 +45,8 @@ export async function load({ locals }) {
allProjects,
projects,
users,
currentlyPrinting
currentlyPrinting,
currentUserId: locals.user.id
};
}

Expand All @@ -51,20 +60,27 @@ export const actions = {
}

const data = await request.formData();
const statusFilter = data.getAll('status') as (typeof project.status._.data)[];
const allowedStatuses: (typeof project.status._.data)[] = ['printing', 't1_approved', 'printed'];
const statusFilter = data.getAll('status')
.map((s) => s.toString())
.filter((s): s is typeof project.status._.data => allowedStatuses.includes(s as typeof project.status._.data)) as (typeof project.status._.data)[];

const projectFilter = data.getAll('project').map((projectId) => {
const parsedInt = parseInt(projectId.toString());
if (!parsedInt) throw error(400, { message: 'malformed project filter' });
return parseInt(projectId.toString());
});

const userFilter = data.getAll('user').map((userId) => {
let userFilter = data.getAll('user').map((userId) => {
const parsedInt = parseInt(userId.toString());
if (!parsedInt) throw error(400, { message: 'malformed user filter' });
return parseInt(userId.toString());
});

if (statusFilter.length === 1 && statusFilter[0] === 'printed') {
userFilter = [locals.user.id];
}

const projects = await getProjects(statusFilter, projectFilter, userFilter);

return {
Expand Down
20 changes: 14 additions & 6 deletions src/routes/dashboard/admin/print/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@

let { data, form } = $props();

type AllProject = { id: number; name: string | null; status: string; userId: number };
const allProjects: AllProject[] = data.allProjects;
const currentUserId: number = data.currentUserId;

let projectSearch = $state('');
let userSearch = $state('');

let projects = $derived(form?.projects ?? data.projects);

let filteredProjects = $derived(
data.allProjects.filter((project) =>
project.name?.toLowerCase().includes(projectSearch.toLowerCase())
)
allProjects
.filter((project) => {
if (project.status === 'printing') return true;
if (project.status === 'printed' && project.userId === currentUserId) return true;
return false;
})
.filter((project) => project.name?.toLowerCase().includes(projectSearch.toLowerCase()))
);
let filteredUsers = $derived(
data.users.filter((user) => user.name.toLowerCase().includes(userSearch.toLowerCase()))
Expand Down Expand Up @@ -64,9 +72,9 @@
value={form?.fields.status ?? ['t1_approved']}
multiple
>
{#each Object.entries(projectStatuses) as [status, longStatus]}
<option value={status} class="truncate">{longStatus}</option>
{/each}
<option value="t1_approved" class="truncate">{projectStatuses['t1_approved'] ?? 'On Print Queue'}</option>
<option value="printing" class="truncate">{projectStatuses['printing'] ?? 'Being printed'}</option>
<option value="printed" class="truncate">{projectStatuses['printed'] ?? 'Printed'}</option>
</select>
</label>

Expand Down
18 changes: 8 additions & 10 deletions src/routes/dashboard/admin/print/[id]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { eq, and, asc, sql } from 'drizzle-orm';
import type { Actions } from './$types';
import { sendSlackDM } from '$lib/server/slack.js';
import { getReviewHistory } from '../../getReviewHistory.server';
import { getCurrentlyPrinting } from '../utils.server';
import { getCurrentlyPrinting, calculateFilamentUsage } from '../utils.server';

export async function load({ locals, params }) {
if (!locals.user) {
Expand Down Expand Up @@ -224,26 +224,24 @@ export const actions = {
}

const data = await request.formData();
const filamentUsed = data.get('filament');
const gcodeFile = data.get('gcode');
const notes = data.get('notes')?.toString();
const feedback = data.get('feedback')?.toString();

if (notes === null || feedback === null) {
if (!gcodeFile || typeof gcodeFile === 'string' || notes === null || feedback === null) {
return error(400);
}

if (
!filamentUsed ||
isNaN(parseFloat(filamentUsed.toString())) ||
parseFloat(filamentUsed.toString()) < 0
) {
return error(400, { message: 'invalid filament used' });
const gcodeText = await gcodeFile.text();
const filamentUsed = calculateFilamentUsage(gcodeText);
if (typeof filamentUsed !== 'number' || isNaN(filamentUsed) || filamentUsed <= 0) {
return error(400, { message: 'Could not calculate valid filament usage from G-code file' });
}

await db.insert(legionReview).values({
projectId: id,
userId: locals.user.id,
filamentUsed: parseFloat(filamentUsed.toString()),
filamentUsed: filamentUsed,
notes,
feedback,
action: 'print'
Expand Down
12 changes: 6 additions & 6 deletions src/routes/dashboard/admin/print/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
<form
method="POST"
action="?/print"
enctype="multipart/form-data"
class="flex flex-col gap-3"
use:enhance={() => {
printFormPending = true;
Expand All @@ -175,15 +176,14 @@
return confirm('really submit?');
}}
>

<label class="flex flex-col gap-1">
<span class="font-medium">Filament used <span class="opacity-50">(grams)</span></span>
<span class="font-medium">G-code file <span class="opacity-50">(from slicer, required)</span></span>
<input
name="filament"
type="number"
name="gcode"
type="file"
class="themed-input-on-box"
placeholder="50"
step="0.1"
min="0"
accept=".gcode"
required
/>
</label>
Expand Down
29 changes: 29 additions & 0 deletions src/routes/dashboard/admin/print/utils.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,32 @@ export async function getCurrentlyPrinting(user: { id: number | SQLWrapper }) {

return currentlyPrinting;
}

export function calculateFilamentUsage(gcodeText: string): number {
const FILAMENT_DIAMETER = 1.75;
const FILAMENT_DENSITY = 1.24;
const lines = gcodeText.split('\n');
let totalExtruded = 0;
let currentE = 0;
for (const line of lines) {
const trimmed = line.trim().toUpperCase();
if (trimmed.startsWith('G92')) {
const eMatch = trimmed.match(/E([0-9.-]+)/);
if (eMatch) {
currentE = parseFloat(eMatch[1]);
}
} else if (trimmed.startsWith('G1')) {
const eMatch = trimmed.match(/E([0-9.-]+)/);
if (eMatch) {
const newE = parseFloat(eMatch[1]);
if (newE > currentE) {
totalExtruded += newE - currentE;
}
currentE = newE;
}
}
}
const area = Math.PI * (FILAMENT_DIAMETER / 2) ** 2;
const volumeCm3 = (totalExtruded * area) / 1000;
return volumeCm3 * FILAMENT_DENSITY;
}