158 lines
4.2 KiB
Go
158 lines
4.2 KiB
Go
|
|
package repositories
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/CubeCraft-Creations/Extrudex/backend/internal/models"
|
||
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
||
|
|
)
|
||
|
|
|
||
|
|
// PrintJobRepository handles database queries for print_jobs.
|
||
|
|
type PrintJobRepository struct {
|
||
|
|
pool *pgxpool.Pool
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewPrintJobRepository creates a PrintJobRepository backed by the given pool.
|
||
|
|
func NewPrintJobRepository(pool *pgxpool.Pool) *PrintJobRepository {
|
||
|
|
return &PrintJobRepository{pool: pool}
|
||
|
|
}
|
||
|
|
|
||
|
|
// PrintJobFilter holds query parameters for listing print jobs.
|
||
|
|
type PrintJobFilter struct {
|
||
|
|
Status string // filter by job_status name (case-insensitive)
|
||
|
|
PrinterID *int // filter by printer_id
|
||
|
|
Limit int
|
||
|
|
Offset int
|
||
|
|
}
|
||
|
|
|
||
|
|
// scanPrintJobWithJoins scans a print_job row with JOINed tables.
|
||
|
|
func (r *PrintJobRepository) scanPrintJobWithJoins(row interface{ Scan(...interface{}) error }) (models.PrintJob, error) {
|
||
|
|
var pj models.PrintJob
|
||
|
|
var js models.JobStatus
|
||
|
|
|
||
|
|
err := row.Scan(
|
||
|
|
&pj.ID, &pj.PrinterID, &pj.FilamentSpoolID,
|
||
|
|
&pj.JobName, &pj.FileName,
|
||
|
|
&pj.JobStatusID,
|
||
|
|
&pj.StartedAt, &pj.CompletedAt,
|
||
|
|
&pj.DurationSeconds, &pj.EstimatedDurationSeconds,
|
||
|
|
&pj.TotalMMExtruded, &pj.TotalGramsUsed, &pj.TotalCostUSD,
|
||
|
|
&pj.Notes,
|
||
|
|
&pj.DeletedAt, &pj.CreatedAt, &pj.UpdatedAt,
|
||
|
|
&js.ID, &js.Name,
|
||
|
|
&js.CreatedAt, &js.UpdatedAt,
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return pj, err
|
||
|
|
}
|
||
|
|
|
||
|
|
pj.JobStatus = &js
|
||
|
|
return pj, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetAll returns print jobs matching the given filters, with pagination.
|
||
|
|
func (r *PrintJobRepository) GetAll(ctx context.Context, filter PrintJobFilter) ([]models.PrintJob, int, error) {
|
||
|
|
conditions := []string{"pj.deleted_at IS NULL"}
|
||
|
|
args := []interface{}{}
|
||
|
|
argIdx := 1
|
||
|
|
|
||
|
|
if filter.Status != "" {
|
||
|
|
conditions = append(conditions, fmt.Sprintf("LOWER(js.name) = LOWER($%d)", argIdx))
|
||
|
|
args = append(args, filter.Status)
|
||
|
|
argIdx++
|
||
|
|
}
|
||
|
|
if filter.PrinterID != nil {
|
||
|
|
conditions = append(conditions, fmt.Sprintf("pj.printer_id = $%d", argIdx))
|
||
|
|
args = append(args, *filter.PrinterID)
|
||
|
|
argIdx++
|
||
|
|
}
|
||
|
|
|
||
|
|
whereClause := ""
|
||
|
|
if len(conditions) > 0 {
|
||
|
|
whereClause = "WHERE " + strings.Join(conditions, " AND ")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Count.
|
||
|
|
var total int
|
||
|
|
countQuery := `SELECT COUNT(*)
|
||
|
|
FROM print_jobs pj
|
||
|
|
LEFT JOIN job_statuses js ON pj.job_status_id = js.id
|
||
|
|
` + " " + whereClause
|
||
|
|
if err := r.pool.QueryRow(ctx, countQuery, args...).Scan(&total); err != nil {
|
||
|
|
return nil, 0, err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Query with pagination.
|
||
|
|
dataQuery := `SELECT
|
||
|
|
pj.id, pj.printer_id, pj.filament_spool_id,
|
||
|
|
pj.job_name, pj.file_name,
|
||
|
|
pj.job_status_id,
|
||
|
|
pj.started_at, pj.completed_at,
|
||
|
|
pj.duration_seconds, pj.estimated_duration_seconds,
|
||
|
|
pj.total_mm_extruded, pj.total_grams_used, pj.total_cost_usd,
|
||
|
|
pj.notes,
|
||
|
|
pj.deleted_at, pj.created_at, pj.updated_at,
|
||
|
|
js.id, js.name,
|
||
|
|
js.created_at, js.updated_at
|
||
|
|
FROM print_jobs pj
|
||
|
|
LEFT JOIN job_statuses js ON pj.job_status_id = js.id
|
||
|
|
` + whereClause +
|
||
|
|
" ORDER BY pj.created_at DESC" +
|
||
|
|
fmt.Sprintf(" LIMIT $%d OFFSET $%d", argIdx, argIdx+1)
|
||
|
|
|
||
|
|
dataArgs := make([]interface{}, len(args))
|
||
|
|
copy(dataArgs, args)
|
||
|
|
dataArgs = append(dataArgs, filter.Limit, filter.Offset)
|
||
|
|
|
||
|
|
rows, err := r.pool.Query(ctx, dataQuery, dataArgs...)
|
||
|
|
if err != nil {
|
||
|
|
return nil, 0, err
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
var jobs []models.PrintJob
|
||
|
|
for rows.Next() {
|
||
|
|
pj, err := r.scanPrintJobWithJoins(rows)
|
||
|
|
if err != nil {
|
||
|
|
return nil, 0, err
|
||
|
|
}
|
||
|
|
jobs = append(jobs, pj)
|
||
|
|
}
|
||
|
|
if err := rows.Err(); err != nil {
|
||
|
|
return nil, 0, err
|
||
|
|
}
|
||
|
|
if jobs == nil {
|
||
|
|
jobs = []models.PrintJob{}
|
||
|
|
}
|
||
|
|
|
||
|
|
return jobs, total, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetByID returns a single print job by ID with JOINed job_status.
|
||
|
|
func (r *PrintJobRepository) GetByID(ctx context.Context, id int) (*models.PrintJob, error) {
|
||
|
|
row := r.pool.QueryRow(ctx, `
|
||
|
|
SELECT
|
||
|
|
pj.id, pj.printer_id, pj.filament_spool_id,
|
||
|
|
pj.job_name, pj.file_name,
|
||
|
|
pj.job_status_id,
|
||
|
|
pj.started_at, pj.completed_at,
|
||
|
|
pj.duration_seconds, pj.estimated_duration_seconds,
|
||
|
|
pj.total_mm_extruded, pj.total_grams_used, pj.total_cost_usd,
|
||
|
|
pj.notes,
|
||
|
|
pj.deleted_at, pj.created_at, pj.updated_at,
|
||
|
|
js.id, js.name,
|
||
|
|
js.created_at, js.updated_at
|
||
|
|
FROM print_jobs pj
|
||
|
|
LEFT JOIN job_statuses js ON pj.job_status_id = js.id
|
||
|
|
WHERE pj.id = $1 AND pj.deleted_at IS NULL
|
||
|
|
`, id)
|
||
|
|
|
||
|
|
pj, err := r.scanPrintJobWithJoins(row)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return &pj, nil
|
||
|
|
}
|