From 7fe06cf565f05973d5a535e4fa0e9d30bb8a53b9 Mon Sep 17 00:00:00 2001 From: rex-bot Date: Mon, 27 Apr 2026 18:16:45 -0400 Subject: [PATCH] merge(dev): Re-apply changes after conflict resolution --- .../API/Controllers/CostAnalysisController.cs | 108 -- .../API/Controllers/PrintJobsController.cs | 86 -- .../API/DTOs/PrintJobs/CostPerPrintDtos.cs | 99 -- .../API/DTOs/PrintJobs/CostSummaryResponse.cs | 55 - backend/API/Jobs/FilamentUsageSyncJob.cs | 79 -- backend/Dockerfile | 3 - backend/Domain/Entities/FilamentUsage.cs | 73 -- backend/Domain/Entities/PrintJob.cs | 6 - backend/Domain/Entities/Printer.cs | 6 - backend/Domain/Entities/Spool.cs | 6 - .../Domain/Interfaces/ICostPerPrintService.cs | 76 -- .../Interfaces/IFilamentUsageSyncService.cs | 19 - backend/Domain/Interfaces/IMoonrakerClient.cs | 39 - .../Configuration/FilamentUsageSyncOptions.cs | 33 - .../FilamentUsageConfiguration.cs | 83 -- .../Infrastructure/Data/ExtrudexDbContext.cs | 1 - ..._AddFilamentUsageTrackingModel.Designer.cs | 1068 ----------------- ...426183433_AddFilamentUsageTrackingModel.cs | 533 -------- .../ExtrudexDbContextModelSnapshot.cs | 238 +--- .../Services/CostPerPrintService.cs | 158 --- .../Services/FilamentUsageSyncService.cs | 139 --- .../Services/MoonrakerClient.cs | 130 -- backend/Program.cs | 16 +- backend/appsettings.Development.json | 5 - backend/appsettings.json | 6 +- deploy.sh | 5 +- docker-compose.dev.yml | 40 +- frontend/.dockerignore | 11 - frontend/Dockerfile | 28 - frontend/nginx.conf | 42 - .../filament-filter.component.html | 76 -- .../filament-filter.component.scss | 134 --- .../filament-filter.component.ts | 158 --- .../filament-table.component.html | 20 +- .../filament-table.component.ts | 60 - 35 files changed, 78 insertions(+), 3561 deletions(-) delete mode 100644 backend/API/Controllers/CostAnalysisController.cs delete mode 100644 backend/API/DTOs/PrintJobs/CostPerPrintDtos.cs delete mode 100644 backend/API/DTOs/PrintJobs/CostSummaryResponse.cs delete mode 100644 backend/API/Jobs/FilamentUsageSyncJob.cs delete mode 100644 backend/Domain/Entities/FilamentUsage.cs delete mode 100644 backend/Domain/Interfaces/ICostPerPrintService.cs delete mode 100644 backend/Domain/Interfaces/IFilamentUsageSyncService.cs delete mode 100644 backend/Domain/Interfaces/IMoonrakerClient.cs delete mode 100644 backend/Infrastructure/Configuration/FilamentUsageSyncOptions.cs delete mode 100644 backend/Infrastructure/Data/Configurations/FilamentUsageConfiguration.cs delete mode 100644 backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.Designer.cs delete mode 100644 backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.cs delete mode 100644 backend/Infrastructure/Services/CostPerPrintService.cs delete mode 100644 backend/Infrastructure/Services/FilamentUsageSyncService.cs delete mode 100644 backend/Infrastructure/Services/MoonrakerClient.cs delete mode 100644 frontend/.dockerignore delete mode 100644 frontend/Dockerfile delete mode 100644 frontend/nginx.conf delete mode 100644 frontend/src/app/components/filament-filter/filament-filter.component.html delete mode 100644 frontend/src/app/components/filament-filter/filament-filter.component.scss delete mode 100644 frontend/src/app/components/filament-filter/filament-filter.component.ts diff --git a/backend/API/Controllers/CostAnalysisController.cs b/backend/API/Controllers/CostAnalysisController.cs deleted file mode 100644 index 87e94b0..0000000 --- a/backend/API/Controllers/CostAnalysisController.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Extrudex.API.DTOs.PrintJobs; -using Extrudex.Domain.Interfaces; -using Microsoft.AspNetCore.Mvc; - -namespace Extrudex.API.Controllers; - -/// -/// Controller for cost analysis endpoints. Provides spool-level -/// cost breakdowns and aggregated COGS reporting. -/// -[ApiController] -[Route("api/cost-analysis")] -public class CostAnalysisController : ControllerBase -{ - private readonly ICostPerPrintService _costService; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The cost-per-print calculation service. - /// The logger for diagnostic output. - public CostAnalysisController( - ICostPerPrintService costService, - ILogger logger) - { - _costService = costService; - _logger = logger; - } - - // ── POST /api/cost-analysis/spool ──────────────────────────── - - /// - /// Calculates cost breakdowns for all print jobs associated with a specific spool. - /// Returns per-job costs plus an aggregated total. Jobs with missing cost data - /// include warnings and null cost fields — the endpoint never throws for missing data. - /// - /// The request containing the spool identifier. - /// A spool-level cost summary with per-job breakdowns. - /// Returns the spool cost breakdown with per-job details. - /// If the spool has no print jobs. - [HttpPost("spool")] - [ProducesResponseType(typeof(SpoolCostResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> CalculateSpoolCost([FromBody] SpoolCostRequest request) - { - _logger.LogDebug("Calculating cost breakdown for spool {SpoolId}", request.SpoolId); - - var results = await _costService.CalculateBySpoolAsync(request.SpoolId); - - if (results.Count == 0) - { - return NotFound(new { error = $"No print jobs found for spool with ID '{request.SpoolId}'." }); - } - - // Build the spool-level summary - var firstResult = results[0]; - var jobResponses = results.Select(MapCostToResponse).ToList(); - - // Aggregate total cost and grams — only include jobs that have a valid cost - var calculableJobs = results.Where(r => r.CostPerPrint.HasValue).ToList(); - var totalCost = calculableJobs.Count == results.Count - ? Math.Round(calculableJobs.Sum(r => r.CostPerPrint!.Value), 4) - : (decimal?)null; - - var aggregateWarnings = new List(); - if (calculableJobs.Count < results.Count) - { - aggregateWarnings.Add( - $"{results.Count - calculableJobs.Count} of {results.Count} print jobs have missing cost data. " + - "Total cost reflects only jobs with complete data."); - } - - var response = new SpoolCostResponse - { - SpoolId = request.SpoolId, - SpoolSerial = firstResult.SpoolSerial, - PurchasePrice = firstResult.PurchasePrice, - WeightTotalGrams = firstResult.WeightTotalGrams, - CostPerGram = firstResult.CostPerGram, - TotalGramsConsumed = results.Sum(r => r.GramsDerived), - TotalCost = totalCost, - JobCount = results.Count, - Jobs = jobResponses, - Warnings = aggregateWarnings - }; - - return Ok(response); - } - - /// - /// Maps a domain CostPerPrintResult to an API CostPerPrintResponse DTO. - /// - private static CostPerPrintResponse MapCostToResponse(CostPerPrintResult r) => new() - { - PrintJobId = r.PrintJobId, - PrintName = r.PrintName, - SpoolId = r.SpoolId, - SpoolSerial = r.SpoolSerial, - MmExtruded = r.MmExtruded, - GramsDerived = r.GramsDerived, - PurchasePrice = r.PurchasePrice, - WeightTotalGrams = r.WeightTotalGrams, - CostPerGram = r.CostPerGram, - CostPerPrint = r.CostPerPrint, - Warnings = r.Warnings - }; -} \ No newline at end of file diff --git a/backend/API/Controllers/PrintJobsController.cs b/backend/API/Controllers/PrintJobsController.cs index 5cb802e..226430e 100644 --- a/backend/API/Controllers/PrintJobsController.cs +++ b/backend/API/Controllers/PrintJobsController.cs @@ -413,92 +413,6 @@ public class PrintJobsController : ControllerBase return NoContent(); } - // ── GET /api/printjobs/{id}/cost-summary ────────────────────────── - - /// - /// Gets the material cost summary for a specific print job. - /// Calculates total material cost from filament usage (grams derived) - /// and the spool's purchase price. Returns warnings instead of errors - /// when cost data is unavailable. - /// - /// The unique identifier of the print job. - /// A cost summary with breakdown and any warnings about missing data. - /// Returns the cost summary. Warnings field lists any missing data. - /// If the print job with the given ID is not found. - [HttpGet("{id:guid}/cost-summary")] - [ProducesResponseType(typeof(CostSummaryResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetCostSummary(Guid id) - { - _logger.LogDebug("Getting cost summary for print job {Id}", id); - - var job = await _dbContext.PrintJobs - .Include(j => j.Spool) - .ThenInclude(s => s!.MaterialBase) - .FirstOrDefaultAsync(j => j.Id == id); - - if (job is null) - { - _logger.LogWarning("Print job {Id} not found for cost summary", id); - return NotFound(new { error = $"Print job with ID '{id}' not found." }); - } - - var warnings = new List(); - var spool = job.Spool; - - // Build response with what we have - var response = new CostSummaryResponse - { - PrintJobId = job.Id, - PrintName = job.PrintName, - SpoolId = job.SpoolId, - SpoolSerial = spool?.SpoolSerial ?? string.Empty, - SpoolBrand = spool?.Brand ?? string.Empty, - SpoolColorName = spool?.ColorName ?? string.Empty, - MmExtruded = job.MmExtruded, - GramsDerived = job.GramsDerived, - SpoolPurchasePrice = spool?.PurchasePrice, - SpoolWeightTotalGrams = spool?.WeightTotalGrams, - StoredCostPerPrint = job.CostPerPrint - }; - - // Validate spool data availability - if (spool is null) - { - warnings.Add("Spool data is not available for this print job. Cost cannot be calculated."); - response.Warnings = warnings; - return Ok(response); - } - - // Check if we can calculate cost - if (!spool.PurchasePrice.HasValue) - { - warnings.Add("Spool purchase price is not set. Cost per gram and total material cost cannot be calculated."); - } - - if (spool.WeightTotalGrams <= 0) - { - warnings.Add("Spool total weight is zero or invalid. Cost per gram and total material cost cannot be calculated."); - } - - // If we have enough data, calculate the cost - if (spool.PurchasePrice.HasValue && spool.WeightTotalGrams > 0) - { - var pricePerGram = spool.PurchasePrice.Value / spool.WeightTotalGrams; - response.PricePerGram = Math.Round(pricePerGram, 4); - response.TotalMaterialCost = Math.Round(job.GramsDerived * pricePerGram, 4); - } - - // Warn if grams derived is zero but mm extruded is non-zero - if (job.GramsDerived == 0 && job.MmExtruded > 0) - { - warnings.Add("GramsDerived is zero despite MmExtruded being non-zero. Cost may be inaccurate. Consider re-deriving grams from filament parameters."); - } - - response.Warnings = warnings; - return Ok(response); - } - // ── Gram Derivation Formula ──────────────────────────────────── /// diff --git a/backend/API/DTOs/PrintJobs/CostPerPrintDtos.cs b/backend/API/DTOs/PrintJobs/CostPerPrintDtos.cs deleted file mode 100644 index e82e6b3..0000000 --- a/backend/API/DTOs/PrintJobs/CostPerPrintDtos.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Extrudex.API.DTOs.PrintJobs; - -/// -/// Response DTO for cost-per-print calculation. Contains the full cost -/// breakdown and any warnings about missing or incomplete data. -/// -public class CostPerPrintResponse -{ - /// The print job identifier this result belongs to. - public Guid PrintJobId { get; set; } - - /// Human-readable name of the print job. - public string PrintName { get; set; } = string.Empty; - - /// The spool identifier that provided filament. - public Guid SpoolId { get; set; } - - /// Serial number of the spool. - public string SpoolSerial { get; set; } = string.Empty; - - /// Total millimeters of filament extruded. - public decimal MmExtruded { get; set; } - - /// Derived grams consumed for this print. - public decimal GramsDerived { get; set; } - - /// The spool's purchase price. Null if not recorded. - public decimal? PurchasePrice { get; set; } - - /// The spool's total weight in grams when full. - public decimal? WeightTotalGrams { get; set; } - - /// Cost per gram of filament. Null if purchase price or total weight is missing. - public decimal? CostPerGram { get; set; } - - /// Calculated cost of this print job. Null if cost data is incomplete. - public decimal? CostPerPrint { get; set; } - - /// - /// Warnings about missing or incomplete data. Empty when all data is available - /// and the calculation succeeded. - /// - public List Warnings { get; set; } = new(); -} - -/// -/// Request DTO for batch cost calculation by spool. Returns cost breakdowns -/// for all print jobs associated with the specified spool. -/// -public class SpoolCostRequest -{ - /// The unique identifier of the spool to calculate costs for. - [Required(ErrorMessage = "SpoolId is required.")] - public Guid SpoolId { get; set; } -} - -/// -/// Response DTO for spool-level cost calculation. Contains cost breakdowns -/// for all print jobs on the spool, plus a total cost summary. -/// -public class SpoolCostResponse -{ - /// The spool identifier. - public Guid SpoolId { get; set; } - - /// Serial number of the spool. - public string SpoolSerial { get; set; } = string.Empty; - - /// The spool's purchase price. Null if not recorded. - public decimal? PurchasePrice { get; set; } - - /// The spool's total weight in grams when full. - public decimal? WeightTotalGrams { get; set; } - - /// Cost per gram of filament. Null if cost data is incomplete. - public decimal? CostPerGram { get; set; } - - /// Total grams consumed across all print jobs on this spool. - public decimal TotalGramsConsumed { get; set; } - - /// Total calculated cost across all print jobs. Null if any job has missing data. - public decimal? TotalCost { get; set; } - - /// Number of print jobs included in this calculation. - public int JobCount { get; set; } - - /// - /// Individual cost breakdowns per print job. Jobs with missing data - /// will have null cost fields and populated warnings. - /// - public List Jobs { get; set; } = new(); - - /// - /// Aggregate warnings about missing data across all jobs. - /// - public List Warnings { get; set; } = new(); -} \ No newline at end of file diff --git a/backend/API/DTOs/PrintJobs/CostSummaryResponse.cs b/backend/API/DTOs/PrintJobs/CostSummaryResponse.cs deleted file mode 100644 index 0c2f9ed..0000000 --- a/backend/API/DTOs/PrintJobs/CostSummaryResponse.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Extrudex.API.DTOs.PrintJobs; - -/// -/// Response DTO for the cost summary of a print job. -/// Provides a breakdown of material cost based on filament usage -/// and spool pricing data. If cost data is incomplete, warnings -/// are returned instead of throwing an error. -/// -public class CostSummaryResponse -{ - /// Unique identifier of the print job. - public Guid PrintJobId { get; set; } - - /// Human-readable name of the print job. - public string PrintName { get; set; } = string.Empty; - - /// Foreign key to the spool used for this print job. - public Guid SpoolId { get; set; } - - /// Serial number of the spool. - public string SpoolSerial { get; set; } = string.Empty; - - /// Brand of the spool. - public string SpoolBrand { get; set; } = string.Empty; - - /// Color name of the spool. - public string SpoolColorName { get; set; } = string.Empty; - - /// Total millimeters of filament extruded during this print. - public decimal MmExtruded { get; set; } - - /// Derived grams consumed for this print job. - public decimal GramsDerived { get; set; } - - /// Purchase price of the full spool, if available. - public decimal? SpoolPurchasePrice { get; set; } - - /// Total weight of the spool in grams when full. - public decimal? SpoolWeightTotalGrams { get; set; } - - /// Calculated price per gram (purchase price / total weight), if available. - public decimal? PricePerGram { get; set; } - - /// Calculated total material cost for this print job, if available. - public decimal? TotalMaterialCost { get; set; } - - /// The CostPerPrint stored on the print job entity, if set. - public decimal? StoredCostPerPrint { get; set; } - - /// - /// Warnings about missing data that prevent cost calculation. - /// Empty if all data is available and cost was calculated successfully. - /// - public List Warnings { get; set; } = new(); -} \ No newline at end of file diff --git a/backend/API/Jobs/FilamentUsageSyncJob.cs b/backend/API/Jobs/FilamentUsageSyncJob.cs deleted file mode 100644 index 19c991d..0000000 --- a/backend/API/Jobs/FilamentUsageSyncJob.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Extrudex.Domain.Interfaces; -using Extrudex.Infrastructure.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace Extrudex.API.Jobs; - -/// -/// Background job that periodically syncs filament usage data from -/// Moonraker printers. Runs as a hosted service and polls all active -/// Moonraker printers on a configurable interval to persist usage -/// data to the Extrudex database. -/// -/// Configuration is bound from the "FilamentUsageSync" section in -/// appsettings.json. Set Enabled=false to disable without removing -/// the service registration. -/// -public class FilamentUsageSyncJob : BackgroundService -{ - private readonly IFilamentUsageSyncService _syncService; - private readonly FilamentUsageSyncOptions _options; - private readonly ILogger _logger; - - /// - /// Creates a new FilamentUsageSyncJob. - /// - /// The service that performs the actual sync logic. - /// Configuration options for polling interval and timeouts. - /// Logger for diagnostic output. - public FilamentUsageSyncJob( - IFilamentUsageSyncService syncService, - IOptions options, - ILogger logger) - { - _syncService = syncService; - _options = options.Value; - _logger = logger; - } - - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - if (!_options.Enabled) - { - _logger.LogInformation("Filament usage sync job is disabled via configuration — exiting"); - return; - } - - _logger.LogInformation( - "Filament usage sync job starting — polling every {Interval}", - _options.PollingInterval); - - // Delay briefly on startup to allow the web host to fully initialize - await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); - - while (!stoppingToken.IsCancellationRequested) - { - try - { - var syncedCount = await _syncService.SyncAllAsync(stoppingToken); - - _logger.LogInformation( - "Filament usage sync completed — {SyncedCount} printer(s) synced. Next sync in {Interval}", - syncedCount, _options.PollingInterval); - } - catch (Exception ex) when (ex is not OperationCanceledException) - { - _logger.LogError(ex, - "Error during filament usage sync cycle — will retry in {Interval}", - _options.PollingInterval); - } - - await Task.Delay(_options.PollingInterval, stoppingToken); - } - - _logger.LogInformation("Filament usage sync job shutting down"); - } -} \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index 23aacef..b604978 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -17,9 +17,6 @@ RUN dotnet publish Extrudex.csproj \ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime WORKDIR /app -# Install curl for health check (not included in aspnet base image) -RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* - # Non-root user for security RUN adduser --disabled-password --gecos "" appuser USER appuser diff --git a/backend/Domain/Entities/FilamentUsage.cs b/backend/Domain/Entities/FilamentUsage.cs deleted file mode 100644 index 3237bc9..0000000 --- a/backend/Domain/Entities/FilamentUsage.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Extrudex.Domain.Base; - -namespace Extrudex.Domain.Entities; - -/// -/// Tracks filament consumption for a specific print job on a specific spool. -/// Each record captures the grams used, which printer consumed it, and when the -/// usage was recorded. This enables granular per-job usage analytics, COGS -/// reconciliation, and spool weight depletion tracking. -/// -/// A single PrintJob may have multiple FilamentUsage records if multiple spools -/// were consumed (e.g., multi-material prints via AMS). -/// -public class FilamentUsage : AuditableEntity -{ - /// - /// Foreign key to the print job that consumed this filament. - /// A usage record is always tied to a print job. - /// - public Guid PrintJobId { get; set; } - - /// - /// Navigation to the print job that consumed this filament. - /// - public PrintJob PrintJob { get; set; } = null!; - - /// - /// Foreign key to the spool (filament) that provided the material. - /// Links usage back to the specific physical spool for inventory tracking. - /// - public Guid SpoolId { get; set; } - - /// - /// Navigation to the spool that provided the material. - /// - public Spool Spool { get; set; } = null!; - - /// - /// Foreign key to the printer that executed the print job. - /// Denormalized from PrintJob for direct querying of per-printer usage. - /// - public Guid PrinterId { get; set; } - - /// - /// Navigation to the printer that executed the print job. - /// - public Printer Printer { get; set; } = null!; - - /// - /// Grams of filament consumed during this print job. - /// Derived from mm_extruded × cross_section_area × material_density, - /// or measured directly from AMS weight delta. - /// - public decimal GramsUsed { get; set; } - - /// - /// Millimeters of filament extruded for this usage record. - /// The primary physical measurement; grams_used is derived from this. - /// - public decimal MmExtruded { get; set; } - - /// - /// Timestamp when this usage record was created (UTC). - /// Represents when the usage was first logged, which may differ from - /// the print job's started_at or completed_at timestamps. - /// - public DateTime RecordedAt { get; set; } = DateTime.UtcNow; - - /// - /// Optional notes about this usage record (e.g., "AMS tray 3", "manual weight check"). - /// - public string? Notes { get; set; } -} \ No newline at end of file diff --git a/backend/Domain/Entities/PrintJob.cs b/backend/Domain/Entities/PrintJob.cs index caeb2d1..6483f72 100644 --- a/backend/Domain/Entities/PrintJob.cs +++ b/backend/Domain/Entities/PrintJob.cs @@ -97,10 +97,4 @@ public class PrintJob : AuditableEntity /// Optional notes about the print job (e.g., "First layer adhesion issues"). /// public string? Notes { get; set; } - - /// - /// Navigation collection of filament usage records for this print job. - /// Enables tracking granular per-spool consumption within a print. - /// - public ICollection FilamentUsages { get; set; } = new List(); } \ No newline at end of file diff --git a/backend/Domain/Entities/Printer.cs b/backend/Domain/Entities/Printer.cs index 4e48b19..b28419e 100644 --- a/backend/Domain/Entities/Printer.cs +++ b/backend/Domain/Entities/Printer.cs @@ -94,10 +94,4 @@ public class Printer : AuditableEntity /// Navigation collection of print jobs executed on this printer. /// public ICollection PrintJobs { get; set; } = new List(); - - /// - /// Navigation collection of filament usage records tracking consumption on this printer. - /// Enables querying per-printer filament usage and COGS. - /// - public ICollection FilamentUsages { get; set; } = new List(); } \ No newline at end of file diff --git a/backend/Domain/Entities/Spool.cs b/backend/Domain/Entities/Spool.cs index 5084a7b..0356002 100644 --- a/backend/Domain/Entities/Spool.cs +++ b/backend/Domain/Entities/Spool.cs @@ -102,10 +102,4 @@ public class Spool : AuditableEntity /// Navigation collection of print jobs that consumed filament from this spool. /// public ICollection PrintJobs { get; set; } = new List(); - - /// - /// Navigation collection of filament usage records tracking consumption from this spool. - /// Enables querying how much filament was consumed per print job. - /// - public ICollection FilamentUsages { get; set; } = new List(); } \ No newline at end of file diff --git a/backend/Domain/Interfaces/ICostPerPrintService.cs b/backend/Domain/Interfaces/ICostPerPrintService.cs deleted file mode 100644 index 041736a..0000000 --- a/backend/Domain/Interfaces/ICostPerPrintService.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Extrudex.Domain.Interfaces; - -/// -/// Service interface for calculating the cost of goods sold (COGS) per print job. -/// Uses the spool's purchase price and the print job's derived grams consumed -/// to produce a cost breakdown. Handles missing cost data gracefully by returning -/// warnings rather than throwing exceptions. -/// -public interface ICostPerPrintService -{ - /// - /// Calculates the cost per print for a specific print job. - /// - /// The unique identifier of the print job. - /// Optional cancellation token. - /// - /// A containing the cost breakdown, - /// or warnings if cost data is missing or incomplete. - /// - Task CalculateAsync(Guid printJobId, CancellationToken cancellationToken = default); - - /// - /// Calculates cost breakdowns for all print jobs associated with a specific spool. - /// Useful for spool-level COGS reporting. - /// - /// The unique identifier of the spool. - /// Optional cancellation token. - /// - /// A list of for each print job on the spool. - /// Jobs with missing cost data will include warnings. - /// - Task> CalculateBySpoolAsync(Guid spoolId, CancellationToken cancellationToken = default); -} - -/// -/// Result of a cost-per-print calculation. Contains the cost breakdown -/// and any warnings about missing or incomplete cost data. -/// -public class CostPerPrintResult -{ - /// The print job identifier this result belongs to. - public Guid PrintJobId { get; set; } - - /// Human-readable name of the print job. - public string PrintName { get; set; } = string.Empty; - - /// The spool identifier that provided filament. - public Guid SpoolId { get; set; } - - /// Serial number of the spool. - public string SpoolSerial { get; set; } = string.Empty; - - /// Total millimeters of filament extruded. - public decimal MmExtruded { get; set; } - - /// Derived grams consumed for this print. - public decimal GramsDerived { get; set; } - - /// The spool's purchase price. Null if not recorded. - public decimal? PurchasePrice { get; set; } - - /// The spool's total weight in grams when full. - public decimal? WeightTotalGrams { get; set; } - - /// Cost per gram of filament. Null if purchase price or total weight is missing. - public decimal? CostPerGram { get; set; } - - /// Calculated cost of this print job. Null if cost data is incomplete. - public decimal? CostPerPrint { get; set; } - - /// - /// Warnings about missing or incomplete data that prevented a full calculation. - /// Empty when all data is available and the calculation succeeded. - /// - public List Warnings { get; set; } = new(); -} \ No newline at end of file diff --git a/backend/Domain/Interfaces/IFilamentUsageSyncService.cs b/backend/Domain/Interfaces/IFilamentUsageSyncService.cs deleted file mode 100644 index 951f80d..0000000 --- a/backend/Domain/Interfaces/IFilamentUsageSyncService.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Extrudex.Domain.Interfaces; - -/// -/// Service interface for syncing filament usage data from printers -/// into the Extrudex database. Handles querying Moonraker printers, -/// computing derived usage metrics, and persisting updates to spools -/// and print job records. -/// -public interface IFilamentUsageSyncService -{ - /// - /// Performs a single sync cycle: queries all active Moonraker printers, - /// fetches their current filament usage data, and persists updates to - /// the database. - /// - /// Cancellation token for graceful shutdown. - /// The number of printers successfully synced. - Task SyncAllAsync(CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/backend/Domain/Interfaces/IMoonrakerClient.cs b/backend/Domain/Interfaces/IMoonrakerClient.cs deleted file mode 100644 index d4599f2..0000000 --- a/backend/Domain/Interfaces/IMoonrakerClient.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Extrudex.Domain.Interfaces; - -/// -/// Client interface for communicating with Moonraker REST API endpoints -/// on Klipper-based printers (e.g., Elegoo Centauri Carbon). -/// Used to retrieve filament usage data, print job status, and -/// remaining spool weight from the printer. -/// -public interface IMoonrakerClient -{ - /// - /// Fetches the current filament usage data from the Moonraker server. - /// Returns a dictionary of usage metrics reported by the printer. - /// - /// The printer's hostname or IP address. - /// The Moonraker API port (default: 7125). - /// Optional API key for authentication. - /// Cancellation token for the HTTP request. - /// A dictionary of usage metric names to their decimal values. - Task> GetFilamentUsageAsync( - string hostnameOrIp, - int port, - string? apiKey, - CancellationToken cancellationToken = default); - - /// - /// Checks whether the Moonraker server is reachable and responding. - /// - /// The printer's hostname or IP address. - /// The Moonraker API port (default: 7125). - /// Optional API key for authentication. - /// Cancellation token for the HTTP request. - /// true if the server responded successfully; otherwise false. - Task IsReachableAsync( - string hostnameOrIp, - int port, - string? apiKey, - CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/backend/Infrastructure/Configuration/FilamentUsageSyncOptions.cs b/backend/Infrastructure/Configuration/FilamentUsageSyncOptions.cs deleted file mode 100644 index 29f95b2..0000000 --- a/backend/Infrastructure/Configuration/FilamentUsageSyncOptions.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Extrudex.Infrastructure.Configuration; - -/// -/// Configuration options for the FilamentUsageSync background job. -/// Bound from appsettings.json under the "FilamentUsageSync" section. -/// Controls polling interval and per-printer timeout settings. -/// -public class FilamentUsageSyncOptions -{ - /// - /// The section name in appsettings.json where these options are bound. - /// - public const string SectionName = "FilamentUsageSync"; - - /// - /// How often the background job polls printers for usage data. - /// Default: 5 minutes. Minimum recommended: 1 minute. - /// - public TimeSpan PollingInterval { get; set; } = TimeSpan.FromMinutes(5); - - /// - /// Timeout for individual HTTP requests to a Moonraker printer. - /// Default: 30 seconds. - /// - public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30); - - /// - /// Whether the sync job is enabled. Set to false to disable - /// the background job without removing its registration. - /// Default: true. - /// - public bool Enabled { get; set; } = true; -} \ No newline at end of file diff --git a/backend/Infrastructure/Data/Configurations/FilamentUsageConfiguration.cs b/backend/Infrastructure/Data/Configurations/FilamentUsageConfiguration.cs deleted file mode 100644 index 8672735..0000000 --- a/backend/Infrastructure/Data/Configurations/FilamentUsageConfiguration.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Extrudex.Domain.Entities; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Extrudex.Infrastructure.Data.Configurations; - -public class FilamentUsageConfiguration : BaseEntityConfiguration -{ - public override void Configure(EntityTypeBuilder builder) - { - base.Configure(builder); - - builder.Property(e => e.PrintJobId) - .HasColumnName("print_job_id") - .IsRequired(); - - builder.Property(e => e.SpoolId) - .HasColumnName("spool_id") - .IsRequired(); - - builder.Property(e => e.PrinterId) - .HasColumnName("printer_id") - .IsRequired(); - - builder.Property(e => e.GramsUsed) - .HasColumnName("grams_used") - .HasPrecision(10, 2) - .IsRequired(); - - builder.Property(e => e.MmExtruded) - .HasColumnName("mm_extruded") - .HasPrecision(12, 2) - .IsRequired(); - - builder.Property(e => e.RecordedAt) - .HasColumnName("recorded_at") - .HasDefaultValueSql("now() at time zone 'utc'") - .IsRequired(); - - builder.Property(e => e.Notes) - .HasColumnName("notes") - .HasMaxLength(2000); - - // Index on print_job_id for querying usage by print job - builder.HasIndex(e => e.PrintJobId) - .HasDatabaseName("ix_filament_usages_print_job_id"); - - // Index on spool_id for querying usage by spool (filament) - builder.HasIndex(e => e.SpoolId) - .HasDatabaseName("ix_filament_usages_spool_id"); - - // Index on printer_id for querying usage by printer - builder.HasIndex(e => e.PrinterId) - .HasDatabaseName("ix_filament_usages_printer_id"); - - // Index on recorded_at for time-range queries - builder.HasIndex(e => e.RecordedAt) - .HasDatabaseName("ix_filament_usages_recorded_at"); - - // Composite index for querying usage by spool within a date range - builder.HasIndex(e => new { e.SpoolId, e.RecordedAt }) - .HasDatabaseName("ix_filament_usages_spool_id_recorded_at"); - - // Relationships - builder.HasOne(e => e.PrintJob) - .WithMany(e => e.FilamentUsages) - .HasForeignKey(e => e.PrintJobId) - .HasConstraintName("fk_filament_usages_print_job") - .OnDelete(DeleteBehavior.Cascade); - - builder.HasOne(e => e.Spool) - .WithMany(e => e.FilamentUsages) - .HasForeignKey(e => e.SpoolId) - .HasConstraintName("fk_filament_usages_spool") - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(e => e.Printer) - .WithMany(e => e.FilamentUsages) - .HasForeignKey(e => e.PrinterId) - .HasConstraintName("fk_filament_usages_printer") - .OnDelete(DeleteBehavior.Restrict); - } -} \ No newline at end of file diff --git a/backend/Infrastructure/Data/ExtrudexDbContext.cs b/backend/Infrastructure/Data/ExtrudexDbContext.cs index 270718c..f6ec61a 100644 --- a/backend/Infrastructure/Data/ExtrudexDbContext.cs +++ b/backend/Infrastructure/Data/ExtrudexDbContext.cs @@ -23,7 +23,6 @@ public class ExtrudexDbContext : DbContext public DbSet AmsUnits => Set(); public DbSet AmsSlots => Set(); public DbSet PrintJobs => Set(); - public DbSet FilamentUsages => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.Designer.cs b/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.Designer.cs deleted file mode 100644 index 393abb9..0000000 --- a/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.Designer.cs +++ /dev/null @@ -1,1068 +0,0 @@ -// -using System; -using Extrudex.Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Extrudex.Infrastructure.Data.Migrations -{ - [DbContext(typeof(ExtrudexDbContext))] - [Migration("20260426183433_AddFilamentUsageTrackingModel")] - partial class AddFilamentUsageTrackingModel - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AmsUnitId") - .HasColumnType("uuid") - .HasColumnName("ams_unit_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("RemainingWeightG") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("remaining_weight_g"); - - b.Property("SpoolId") - .HasColumnType("uuid") - .HasColumnName("spool_id"); - - b.Property("TrayIndex") - .HasColumnType("integer") - .HasColumnName("tray_index"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("SpoolId") - .HasDatabaseName("ix_ams_slots_spool_id"); - - b.HasIndex("AmsUnitId", "TrayIndex") - .IsUnique() - .HasDatabaseName("ix_ams_slots_ams_unit_id_tray_index"); - - b.ToTable("ams_slots", (string)null); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PrinterId") - .HasColumnType("uuid") - .HasColumnName("printer_id"); - - b.Property("UnitIndex") - .HasColumnType("integer") - .HasColumnName("unit_index"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("PrinterId", "UnitIndex") - .IsUnique() - .HasDatabaseName("ix_ams_units_printer_id_unit_index"); - - b.ToTable("ams_units", (string)null); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("GramsUsed") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("grams_used"); - - b.Property("MmExtruded") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("mm_extruded"); - - b.Property("Notes") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("notes"); - - b.Property("PrintJobId") - .HasColumnType("uuid") - .HasColumnName("print_job_id"); - - b.Property("PrinterId") - .HasColumnType("uuid") - .HasColumnName("printer_id"); - - b.Property("RecordedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("recorded_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("SpoolId") - .HasColumnType("uuid") - .HasColumnName("spool_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("PrintJobId") - .HasDatabaseName("ix_filament_usages_print_job_id"); - - b.HasIndex("PrinterId") - .HasDatabaseName("ix_filament_usages_printer_id"); - - b.HasIndex("RecordedAt") - .HasDatabaseName("ix_filament_usages_recorded_at"); - - b.HasIndex("SpoolId") - .HasDatabaseName("ix_filament_usages_spool_id"); - - b.HasIndex("SpoolId", "RecordedAt") - .HasDatabaseName("ix_filament_usages_spool_id_recorded_at"); - - b.ToTable("filament_usages", (string)null); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("DensityGperCm3") - .HasPrecision(10, 4) - .HasColumnType("numeric(10,4)") - .HasColumnName("density_g_per_cm3"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique() - .HasDatabaseName("ix_material_bases_name"); - - b.ToTable("material_bases", (string)null); - - b.HasData( - new - { - Id = new Guid("10000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388), - DensityGperCm3 = 1.24m, - Name = "PLA", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388) - }, - new - { - Id = new Guid("10000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871), - DensityGperCm3 = 1.27m, - Name = "PETG", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871) - }, - new - { - Id = new Guid("10000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881), - DensityGperCm3 = 1.04m, - Name = "ABS", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881) - }, - new - { - Id = new Guid("10000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888), - DensityGperCm3 = 1.07m, - Name = "ASA", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888) - }, - new - { - Id = new Guid("10000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895), - DensityGperCm3 = 1.21m, - Name = "TPU", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895) - }, - new - { - Id = new Guid("10000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9901), - DensityGperCm3 = 1.14m, - Name = "Nylon", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9902) - }); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("MaterialBaseId") - .HasColumnType("uuid") - .HasColumnName("material_base_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("MaterialBaseId", "Name") - .IsUnique() - .HasDatabaseName("ix_material_finishes_material_base_id_name"); - - b.ToTable("material_finishes", (string)null); - - b.HasData( - new - { - Id = new Guid("20000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Silk", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Glitter", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Marble", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Sparkle", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000007"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), - Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000008"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), - Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000009"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(297), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), - Name = "Silk", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(298) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000010"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), - Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000011"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), - Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000012"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(316), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), - Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(317) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000013"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), - Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000014"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000005"), - Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329) - }, - new - { - Id = new Guid("20000000-0000-0000-0000-000000000015"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), - Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336) - }); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("MaterialBaseId") - .HasColumnType("uuid") - .HasColumnName("material_base_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("MaterialBaseId", "Name") - .IsUnique() - .HasDatabaseName("ix_material_modifiers_material_base_id_name"); - - b.ToTable("material_modifiers", (string)null); - - b.HasData( - new - { - Id = new Guid("30000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(805), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(806) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Wood Fill", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), - Name = "Glow-in-the-Dark", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), - Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), - Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000007"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), - Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000008"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), - Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000009"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), - Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000010"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(859), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), - Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(860) - }, - new - { - Id = new Guid("30000000-0000-0000-0000-000000000011"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866), - MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), - Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866) - }); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CostPerPrint") - .HasPrecision(10, 4) - .HasColumnType("numeric(10,4)") - .HasColumnName("cost_per_print"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("DataSource") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("data_source"); - - b.Property("FilamentDiameterAtPrintMm") - .HasPrecision(6, 3) - .HasColumnType("numeric(6,3)") - .HasColumnName("filament_diameter_at_print_mm"); - - b.Property("GcodeFilePath") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("gcode_file_path"); - - b.Property("GramsDerived") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("grams_derived"); - - b.Property("MaterialDensityAtPrint") - .HasPrecision(10, 4) - .HasColumnType("numeric(10,4)") - .HasColumnName("material_density_at_print"); - - b.Property("MmExtruded") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("mm_extruded"); - - b.Property("Notes") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("notes"); - - b.Property("PrintName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("print_name"); - - b.Property("PrinterId") - .HasColumnType("uuid") - .HasColumnName("printer_id"); - - b.Property("SpoolId") - .HasColumnType("uuid") - .HasColumnName("spool_id"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasDefaultValue("Queued") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("DataSource") - .HasDatabaseName("ix_print_jobs_data_source"); - - b.HasIndex("PrinterId") - .HasDatabaseName("ix_print_jobs_printer_id"); - - b.HasIndex("SpoolId") - .HasDatabaseName("ix_print_jobs_spool_id"); - - b.HasIndex("Status") - .HasDatabaseName("ix_print_jobs_status"); - - b.ToTable("print_jobs", (string)null); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ApiKey") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("api_key"); - - b.Property("ConnectionType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("connection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("HostnameOrIp") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("hostname_or_ip"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("Manufacturer") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("manufacturer"); - - b.Property("Model") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("model"); - - b.Property("MqttPassword") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("mqtt_password"); - - b.Property("MqttUseTls") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(false) - .HasColumnName("mqtt_use_tls"); - - b.Property("MqttUsername") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mqtt_username"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Port") - .HasColumnType("integer") - .HasColumnName("port"); - - b.Property("PrinterType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("printer_type"); - - b.Property("Status") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasDefaultValue("Offline") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("ConnectionType") - .HasDatabaseName("ix_printers_connection_type"); - - b.HasIndex("IsActive") - .HasDatabaseName("ix_printers_is_active"); - - b.HasIndex("PrinterType") - .HasDatabaseName("ix_printers_printer_type"); - - b.HasIndex("Status") - .HasDatabaseName("ix_printers_status"); - - b.ToTable("printers", (string)null); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Brand") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand"); - - b.Property("ColorHex") - .IsRequired() - .HasMaxLength(7) - .HasColumnType("character varying(7)") - .HasColumnName("color_hex"); - - b.Property("ColorName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("color_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("FilamentDiameterMm") - .HasPrecision(6, 3) - .HasColumnType("numeric(6,3)") - .HasColumnName("filament_diameter_mm"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("MaterialBaseId") - .HasColumnType("uuid") - .HasColumnName("material_base_id"); - - b.Property("MaterialFinishId") - .HasColumnType("uuid") - .HasColumnName("material_finish_id"); - - b.Property("MaterialModifierId") - .HasColumnType("uuid") - .HasColumnName("material_modifier_id"); - - b.Property("PurchaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("purchase_date"); - - b.Property("PurchasePrice") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("purchase_price"); - - b.Property("SpoolSerial") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("spool_serial"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("WeightRemainingGrams") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("weight_remaining_grams"); - - b.Property("WeightTotalGrams") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("weight_total_grams"); - - b.HasKey("Id"); - - b.HasIndex("IsActive") - .HasDatabaseName("ix_spools_is_active"); - - b.HasIndex("MaterialBaseId") - .HasDatabaseName("ix_spools_material_base_id"); - - b.HasIndex("MaterialFinishId") - .HasDatabaseName("ix_spools_material_finish_id"); - - b.HasIndex("MaterialModifierId") - .HasDatabaseName("ix_spools_material_modifier_id"); - - b.HasIndex("SpoolSerial") - .IsUnique() - .HasDatabaseName("ix_spools_spool_serial"); - - b.ToTable("spools", (string)null); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => - { - b.HasOne("Extrudex.Domain.Entities.AmsUnit", "AmsUnit") - .WithMany("Slots") - .HasForeignKey("AmsUnitId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ams_slots_ams_unit"); - - b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") - .WithMany("AmsSlots") - .HasForeignKey("SpoolId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_ams_slots_spool"); - - b.Navigation("AmsUnit"); - - b.Navigation("Spool"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => - { - b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") - .WithMany("AmsUnits") - .HasForeignKey("PrinterId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ams_units_printer"); - - b.Navigation("Printer"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => - { - b.HasOne("Extrudex.Domain.Entities.PrintJob", "PrintJob") - .WithMany("FilamentUsages") - .HasForeignKey("PrintJobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_filament_usages_print_job"); - - b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") - .WithMany("FilamentUsages") - .HasForeignKey("PrinterId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_filament_usages_printer"); - - b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") - .WithMany("FilamentUsages") - .HasForeignKey("SpoolId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_filament_usages_spool"); - - b.Navigation("PrintJob"); - - b.Navigation("Printer"); - - b.Navigation("Spool"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => - { - b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") - .WithMany("Finishes") - .HasForeignKey("MaterialBaseId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_material_finishes_material_base"); - - b.Navigation("MaterialBase"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => - { - b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") - .WithMany("Modifiers") - .HasForeignKey("MaterialBaseId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_material_modifiers_material_base"); - - b.Navigation("MaterialBase"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => - { - b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") - .WithMany("PrintJobs") - .HasForeignKey("PrinterId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_print_jobs_printer"); - - b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") - .WithMany("PrintJobs") - .HasForeignKey("SpoolId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_print_jobs_spool"); - - b.Navigation("Printer"); - - b.Navigation("Spool"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => - { - b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") - .WithMany("Spools") - .HasForeignKey("MaterialBaseId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_spools_material_base"); - - b.HasOne("Extrudex.Domain.Entities.MaterialFinish", "MaterialFinish") - .WithMany("Spools") - .HasForeignKey("MaterialFinishId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_spools_material_finish"); - - b.HasOne("Extrudex.Domain.Entities.MaterialModifier", "MaterialModifier") - .WithMany("Spools") - .HasForeignKey("MaterialModifierId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_spools_material_modifier"); - - b.Navigation("MaterialBase"); - - b.Navigation("MaterialFinish"); - - b.Navigation("MaterialModifier"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => - { - b.Navigation("Slots"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => - { - b.Navigation("Finishes"); - - b.Navigation("Modifiers"); - - b.Navigation("Spools"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => - { - b.Navigation("Spools"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => - { - b.Navigation("Spools"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => - { - b.Navigation("FilamentUsages"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => - { - b.Navigation("AmsUnits"); - - b.Navigation("FilamentUsages"); - - b.Navigation("PrintJobs"); - }); - - modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => - { - b.Navigation("AmsSlots"); - - b.Navigation("FilamentUsages"); - - b.Navigation("PrintJobs"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.cs b/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.cs deleted file mode 100644 index 0adfc7b..0000000 --- a/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.cs +++ /dev/null @@ -1,533 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Extrudex.Infrastructure.Data.Migrations -{ - /// - public partial class AddFilamentUsageTrackingModel : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "filament_usages", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - print_job_id = table.Column(type: "uuid", nullable: false), - spool_id = table.Column(type: "uuid", nullable: false), - printer_id = table.Column(type: "uuid", nullable: false), - grams_used = table.Column(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false), - mm_extruded = table.Column(type: "numeric(12,2)", precision: 12, scale: 2, nullable: false), - recorded_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), - notes = table.Column(type: "character varying(2000)", maxLength: 2000, nullable: true), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") - }, - constraints: table => - { - table.PrimaryKey("PK_filament_usages", x => x.id); - table.ForeignKey( - name: "fk_filament_usages_print_job", - column: x => x.print_job_id, - principalTable: "print_jobs", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_filament_usages_printer", - column: x => x.printer_id, - principalTable: "printers", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_filament_usages_spool", - column: x => x.spool_id, - principalTable: "spools", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000001"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000002"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000003"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000004"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000005"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000006"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9901), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9902) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000001"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000002"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000003"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000004"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000005"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000006"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000007"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000008"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000009"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(297), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(298) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000010"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000011"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000012"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(316), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(317) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000013"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000014"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000015"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000001"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000002"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(805), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(806) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000003"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000004"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000005"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000006"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000007"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000008"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000009"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000010"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(859), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(860) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000011"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866) }); - - migrationBuilder.CreateIndex( - name: "ix_filament_usages_print_job_id", - table: "filament_usages", - column: "print_job_id"); - - migrationBuilder.CreateIndex( - name: "ix_filament_usages_printer_id", - table: "filament_usages", - column: "printer_id"); - - migrationBuilder.CreateIndex( - name: "ix_filament_usages_recorded_at", - table: "filament_usages", - column: "recorded_at"); - - migrationBuilder.CreateIndex( - name: "ix_filament_usages_spool_id", - table: "filament_usages", - column: "spool_id"); - - migrationBuilder.CreateIndex( - name: "ix_filament_usages_spool_id_recorded_at", - table: "filament_usages", - columns: new[] { "spool_id", "recorded_at" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "filament_usages"); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000001"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000002"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000003"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000004"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000005"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645) }); - - migrationBuilder.UpdateData( - table: "material_bases", - keyColumn: "id", - keyValue: new Guid("10000000-0000-0000-0000-000000000006"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1651), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1652) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000001"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000002"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000003"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000004"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2055), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2056) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000005"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000006"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000007"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000008"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000009"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000010"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000011"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000012"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000013"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000014"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2132), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2133) }); - - migrationBuilder.UpdateData( - table: "material_finishes", - keyColumn: "id", - keyValue: new Guid("20000000-0000-0000-0000-000000000015"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000001"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000002"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000003"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000004"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2477), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2478) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000005"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000006"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2490), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2491) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000007"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000008"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000009"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000010"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516) }); - - migrationBuilder.UpdateData( - table: "material_modifiers", - keyColumn: "id", - keyValue: new Guid("30000000-0000-0000-0000-000000000011"), - columns: new[] { "created_at", "updated_at" }, - values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2522), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2523) }); - } - } -} diff --git a/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs b/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs index f214506..64647c5 100644 --- a/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs +++ b/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs @@ -104,77 +104,6 @@ namespace Extrudex.Infrastructure.Data.Migrations b.ToTable("ams_units", (string)null); }); - modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("GramsUsed") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("grams_used"); - - b.Property("MmExtruded") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("mm_extruded"); - - b.Property("Notes") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("notes"); - - b.Property("PrintJobId") - .HasColumnType("uuid") - .HasColumnName("print_job_id"); - - b.Property("PrinterId") - .HasColumnType("uuid") - .HasColumnName("printer_id"); - - b.Property("RecordedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("recorded_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("SpoolId") - .HasColumnType("uuid") - .HasColumnName("spool_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.HasKey("Id"); - - b.HasIndex("PrintJobId") - .HasDatabaseName("ix_filament_usages_print_job_id"); - - b.HasIndex("PrinterId") - .HasDatabaseName("ix_filament_usages_printer_id"); - - b.HasIndex("RecordedAt") - .HasDatabaseName("ix_filament_usages_recorded_at"); - - b.HasIndex("SpoolId") - .HasDatabaseName("ix_filament_usages_spool_id"); - - b.HasIndex("SpoolId", "RecordedAt") - .HasDatabaseName("ix_filament_usages_spool_id_recorded_at"); - - b.ToTable("filament_usages", (string)null); - }); - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => { b.Property("Id") @@ -216,50 +145,50 @@ namespace Extrudex.Infrastructure.Data.Migrations new { Id = new Guid("10000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096), DensityGperCm3 = 1.24m, Name = "PLA", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096) }, new { Id = new Guid("10000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620), DensityGperCm3 = 1.27m, Name = "PETG", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620) }, new { Id = new Guid("10000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630), DensityGperCm3 = 1.04m, Name = "ABS", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630) }, new { Id = new Guid("10000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638), DensityGperCm3 = 1.07m, Name = "ASA", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638) }, new { Id = new Guid("10000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645), DensityGperCm3 = 1.21m, Name = "TPU", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645) }, new { Id = new Guid("10000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9901), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1651), DensityGperCm3 = 1.14m, Name = "Nylon", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9902) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1652) }); }); @@ -303,122 +232,122 @@ namespace Extrudex.Infrastructure.Data.Migrations new { Id = new Guid("20000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850) }, new { Id = new Guid("20000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041) }, new { Id = new Guid("20000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Silk", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049) }, new { Id = new Guid("20000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2055), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Glitter", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2056) }, new { Id = new Guid("20000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Marble", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062) }, new { Id = new Guid("20000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Sparkle", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068) }, new { Id = new Guid("20000000-0000-0000-0000-000000000007"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075) }, new { Id = new Guid("20000000-0000-0000-0000-000000000008"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081) }, new { Id = new Guid("20000000-0000-0000-0000-000000000009"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(297), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Silk", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(298) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100) }, new { Id = new Guid("20000000-0000-0000-0000-000000000010"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107) }, new { Id = new Guid("20000000-0000-0000-0000-000000000011"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113) }, new { Id = new Guid("20000000-0000-0000-0000-000000000012"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(316), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(317) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120) }, new { Id = new Guid("20000000-0000-0000-0000-000000000013"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126) }, new { Id = new Guid("20000000-0000-0000-0000-000000000014"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2132), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000005"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2133) }, new { Id = new Guid("20000000-0000-0000-0000-000000000015"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139) }); }); @@ -462,90 +391,90 @@ namespace Extrudex.Infrastructure.Data.Migrations new { Id = new Guid("30000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304) }, new { Id = new Guid("30000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(805), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(806) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463) }, new { Id = new Guid("30000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Wood Fill", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471) }, new { Id = new Guid("30000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2477), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Glow-in-the-Dark", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2478) }, new { Id = new Guid("30000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484) }, new { Id = new Guid("30000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2490), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2491) }, new { Id = new Guid("30000000-0000-0000-0000-000000000007"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497) }, new { Id = new Guid("30000000-0000-0000-0000-000000000008"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503) }, new { Id = new Guid("30000000-0000-0000-0000-000000000009"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510) }, new { Id = new Guid("30000000-0000-0000-0000-000000000010"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(859), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(860) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516) }, new { Id = new Guid("30000000-0000-0000-0000-000000000011"), - CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2522), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866) + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2523) }); }); @@ -909,36 +838,6 @@ namespace Extrudex.Infrastructure.Data.Migrations b.Navigation("Printer"); }); - modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => - { - b.HasOne("Extrudex.Domain.Entities.PrintJob", "PrintJob") - .WithMany("FilamentUsages") - .HasForeignKey("PrintJobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_filament_usages_print_job"); - - b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") - .WithMany("FilamentUsages") - .HasForeignKey("PrinterId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_filament_usages_printer"); - - b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") - .WithMany("FilamentUsages") - .HasForeignKey("SpoolId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_filament_usages_spool"); - - b.Navigation("PrintJob"); - - b.Navigation("Printer"); - - b.Navigation("Spool"); - }); - modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => { b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") @@ -1037,17 +936,10 @@ namespace Extrudex.Infrastructure.Data.Migrations b.Navigation("Spools"); }); - modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => - { - b.Navigation("FilamentUsages"); - }); - modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => { b.Navigation("AmsUnits"); - b.Navigation("FilamentUsages"); - b.Navigation("PrintJobs"); }); @@ -1055,8 +947,6 @@ namespace Extrudex.Infrastructure.Data.Migrations { b.Navigation("AmsSlots"); - b.Navigation("FilamentUsages"); - b.Navigation("PrintJobs"); }); #pragma warning restore 612, 618 diff --git a/backend/Infrastructure/Services/CostPerPrintService.cs b/backend/Infrastructure/Services/CostPerPrintService.cs deleted file mode 100644 index e4b2593..0000000 --- a/backend/Infrastructure/Services/CostPerPrintService.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Extrudex.Domain.Interfaces; -using Extrudex.Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace Extrudex.Infrastructure.Services; - -/// -/// Calculates the cost of goods sold (COGS) per print job using the spool's -/// purchase price and the print job's derived grams consumed. -/// -/// Formula: -/// cost_per_gram = purchase_price / weight_total_grams -/// cost_per_print = grams_derived × cost_per_gram -/// -/// Handles missing data gracefully — if the spool has no purchase price or -/// weight recorded, the result includes warnings and null cost fields -/// instead of throwing exceptions. -/// -public class CostPerPrintService : ICostPerPrintService -{ - private readonly ExtrudexDbContext _dbContext; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The database context for data access. - /// The logger for diagnostic output. - public CostPerPrintService(ExtrudexDbContext dbContext, ILogger logger) - { - _dbContext = dbContext; - _logger = logger; - } - - /// - public async Task CalculateAsync(Guid printJobId, CancellationToken cancellationToken = default) - { - _logger.LogDebug("Calculating cost per print for job {PrintJobId}", printJobId); - - var job = await _dbContext.PrintJobs - .Include(j => j.Spool) - .ThenInclude(s => s!.MaterialBase) - .FirstOrDefaultAsync(j => j.Id == printJobId, cancellationToken); - - if (job is null) - { - _logger.LogWarning("Print job {PrintJobId} not found for cost calculation", printJobId); - return new CostPerPrintResult - { - PrintJobId = printJobId, - Warnings = new List { $"Print job with ID '{printJobId}' not found." } - }; - } - - return BuildResult(job); - } - - /// - public async Task> CalculateBySpoolAsync( - Guid spoolId, CancellationToken cancellationToken = default) - { - _logger.LogDebug("Calculating cost per print for all jobs on spool {SpoolId}", spoolId); - - var jobs = await _dbContext.PrintJobs - .Include(j => j.Spool) - .ThenInclude(s => s!.MaterialBase) - .Where(j => j.SpoolId == spoolId) - .OrderByDescending(j => j.CreatedAt) - .ToListAsync(cancellationToken); - - if (jobs.Count == 0) - { - _logger.LogDebug("No print jobs found for spool {SpoolId}", spoolId); - return Array.Empty(); - } - - return jobs.Select(BuildResult).ToList(); - } - - /// - /// Builds a from a print job entity. - /// Computes cost_per_gram and cost_per_print when all required data is available. - /// Populates warnings when data is missing or incomplete. - /// - /// The print job entity with Spool navigation loaded. - /// A cost calculation result with breakdown and any warnings. - private CostPerPrintResult BuildResult(Domain.Entities.PrintJob job) - { - var warnings = new List(); - var spool = job.Spool; - - // Map what we always have - var result = new CostPerPrintResult - { - PrintJobId = job.Id, - PrintName = job.PrintName, - SpoolId = job.SpoolId, - SpoolSerial = spool?.SpoolSerial ?? string.Empty, - MmExtruded = job.MmExtruded, - GramsDerived = job.GramsDerived, - }; - - // Guard: spool must be loaded - if (spool is null) - { - warnings.Add("Spool data is not available for this print job."); - result.Warnings = warnings; - return result; - } - - // Capture purchase price - result.PurchasePrice = spool.PurchasePrice; - result.WeightTotalGrams = spool.WeightTotalGrams; - - // Check for missing purchase price - if (!spool.PurchasePrice.HasValue) - { - warnings.Add( - "Spool purchase price is not recorded. Cost calculation requires a purchase price on the spool."); - } - - // Check for zero or negative weight — prevents division by zero - if (spool.WeightTotalGrams <= 0) - { - warnings.Add( - "Spool total weight is zero or not recorded. Cost calculation requires a positive weight_total_grams on the spool."); - } - - // Check for zero grams derived - if (job.GramsDerived <= 0) - { - warnings.Add( - "Derived grams consumed is zero. Ensure mm_extruded, filament diameter, and material density are recorded for this print job."); - } - - // If all data is present and valid, compute the cost - if (spool.PurchasePrice.HasValue && spool.WeightTotalGrams > 0 && job.GramsDerived > 0) - { - var costPerGram = spool.PurchasePrice.Value / spool.WeightTotalGrams; - result.CostPerGram = Math.Round(costPerGram, 6); - result.CostPerPrint = Math.Round(job.GramsDerived * costPerGram, 4); - - _logger.LogDebug( - "Cost calculated for job {PrintJobId}: {GramsDerived}g × {CostPerGram:C}/g = {CostPerPrint:C}", - job.Id, job.GramsDerived, result.CostPerGram, result.CostPerPrint); - } - else - { - _logger.LogDebug( - "Cost calculation incomplete for job {PrintJobId}: missing data (warnings: {WarningCount})", - job.Id, warnings.Count); - } - - result.Warnings = warnings; - return result; - } -} \ No newline at end of file diff --git a/backend/Infrastructure/Services/FilamentUsageSyncService.cs b/backend/Infrastructure/Services/FilamentUsageSyncService.cs deleted file mode 100644 index c2e305b..0000000 --- a/backend/Infrastructure/Services/FilamentUsageSyncService.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Extrudex.Domain.Enums; -using Extrudex.Domain.Interfaces; -using Extrudex.Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace Extrudex.Infrastructure.Configuration; - -/// -/// Service that syncs filament usage data from Moonraker printers into the -/// Extrudex database. Queries all active Moonraker printers, fetches their -/// current filament usage metrics, and updates spool remaining weights and -/// print job records. -/// -public class FilamentUsageSyncService : IFilamentUsageSyncService -{ - private readonly ExtrudexDbContext _dbContext; - private readonly IMoonrakerClient _moonrakerClient; - private readonly ILogger _logger; - - /// - /// Creates a new FilamentUsageSyncService. - /// - /// The EF Core database context for persisting updates. - /// The Moonraker HTTP client for fetching printer data. - /// Logger for diagnostic output. - public FilamentUsageSyncService( - ExtrudexDbContext dbContext, - IMoonrakerClient moonrakerClient, - ILogger logger) - { - _dbContext = dbContext; - _moonrakerClient = moonrakerClient; - _logger = logger; - } - - /// - public async Task SyncAllAsync(CancellationToken cancellationToken = default) - { - _logger.LogInformation("Starting filament usage sync cycle"); - - var printers = await _dbContext.Printers - .Where(p => p.IsActive && p.ConnectionType == ConnectionType.Moonraker) - .Include(p => p.AmsUnits) - .ThenInclude(u => u.Slots) - .ThenInclude(s => s.Spool) - .ToListAsync(cancellationToken); - - if (printers.Count == 0) - { - _logger.LogInformation("No active Moonraker printers found — skipping sync"); - return 0; - } - - _logger.LogInformation("Found {PrinterCount} active Moonraker printer(s) to sync", printers.Count); - - var syncedCount = 0; - - foreach (var printer in printers) - { - try - { - var usageData = await _moonrakerClient.GetFilamentUsageAsync( - printer.HostnameOrIp, - printer.Port, - printer.ApiKey, - cancellationToken); - - if (usageData.Count == 0) - { - _logger.LogWarning( - "No usage data returned from printer {PrinterName} ({Host}:{Port})", - printer.Name, printer.HostnameOrIp, printer.Port); - continue; - } - - // Update spool remaining weights from AMS data - UpdateSpoolWeights(printer, usageData); - - // Mark printer as seen and idle (reachable = idle, not printing) - printer.LastSeenAt = DateTime.UtcNow; - printer.Status = PrinterStatus.Idle; - - syncedCount++; - _logger.LogInformation( - "Successfully synced filament usage from printer {PrinterName}", - printer.Name); - } - catch (Exception ex) - { - _logger.LogError(ex, - "Error syncing filament usage from printer {PrinterName} ({Host}:{Port})", - printer.Name, printer.HostnameOrIp, printer.Port); - } - } - - await _dbContext.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Filament usage sync cycle complete — {SyncedCount}/{TotalCount} printers synced", - syncedCount, printers.Count); - - return syncedCount; - } - - /// - /// Updates spool remaining weights based on usage data received from Moonraker. - /// For printers with AMS units, updates the remaining weight on each slot's spool. - /// - private void UpdateSpoolWeights( - Domain.Entities.Printer printer, - Dictionary usageData) - { - // Update AMS slot remaining weights if available - foreach (var amsUnit in printer.AmsUnits) - { - foreach (var slot in amsUnit.Slots) - { - if (slot.Spool != null && slot.RemainingWeightG.HasValue) - { - // Sync the AMS-reported remaining weight to the spool - slot.Spool.WeightRemainingGrams = slot.RemainingWeightG.Value; - - _logger.LogDebug( - "Updated spool {SpoolSerial} remaining weight to {Weight}g", - slot.Spool.SpoolSerial, slot.RemainingWeightG.Value); - } - } - } - - // If usage data contains extruded mm, log it for observability - if (usageData.TryGetValue("mm_extruded", out var mmExtruded) && mmExtruded > 0) - { - _logger.LogInformation( - "Printer {PrinterName} reports {MmExtruded}mm filament extruded in latest job", - printer.Name, mmExtruded); - } - } -} \ No newline at end of file diff --git a/backend/Infrastructure/Services/MoonrakerClient.cs b/backend/Infrastructure/Services/MoonrakerClient.cs deleted file mode 100644 index 1666dcf..0000000 --- a/backend/Infrastructure/Services/MoonrakerClient.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Net.Http.Json; -using System.Text.Json; -using Extrudex.Domain.Interfaces; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace Extrudex.Infrastructure.Configuration; - -/// -/// HTTP client for communicating with Moonraker REST API endpoints -/// on Klipper-based printers (e.g., Elegoo Centauri Carbon). -/// Retrieves filament usage data and printer status information. -/// -public class MoonrakerClient : IMoonrakerClient -{ - private readonly HttpClient _httpClient; - private readonly ILogger _logger; - - /// - /// Creates a new MoonrakerClient with the configured HTTP client and logger. - /// - /// The HTTP client for making requests to Moonraker endpoints. - /// Logger for diagnostic output. - public MoonrakerClient(HttpClient httpClient, ILogger logger) - { - _httpClient = httpClient; - _logger = logger; - } - - /// - public async Task> GetFilamentUsageAsync( - string hostnameOrIp, - int port, - string? apiKey, - CancellationToken cancellationToken = default) - { - var baseUrl = BuildBaseUrl(hostnameOrIp, port); - var result = new Dictionary(); - - try - { - // Query Moonraker server info endpoint for filament usage data - using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/server/history/items?limit=1"); - if (!string.IsNullOrEmpty(apiKey)) - { - request.Headers.Add("X-Api-Key", apiKey); - } - - using var response = await _httpClient.SendAsync(request, cancellationToken); - response.EnsureSuccessStatusCode(); - - var json = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); - - // Extract filament usage from the response - // Moonraker returns job history with filament_used and similar fields - if (json.TryGetProperty("result", out var resultElement)) - { - if (resultElement.TryGetProperty("items", out var items) && items.GetArrayLength() > 0) - { - var job = items[0]; - - // Moonraker tracks filament_used in millimeters - if (job.TryGetProperty("filament_used", out var filamentUsed)) - { - result["mm_extruded"] = filamentUsed.GetDecimal(); - } - - // Total duration in seconds - if (job.TryGetProperty("print_duration", out var duration)) - { - result["print_duration_seconds"] = duration.GetDecimal(); - } - } - } - - _logger.LogDebug( - "Retrieved filament usage from Moonraker at {Host}:{Port}: {MetricCount} metrics", - hostnameOrIp, port, result.Count); - } - catch (HttpRequestException ex) - { - _logger.LogWarning(ex, - "Failed to retrieve filament usage from Moonraker at {Host}:{Port}", - hostnameOrIp, port); - } - catch (JsonException ex) - { - _logger.LogWarning(ex, - "Failed to parse Moonraker response from {Host}:{Port}", - hostnameOrIp, port); - } - - return result; - } - - /// - public async Task IsReachableAsync( - string hostnameOrIp, - int port, - string? apiKey, - CancellationToken cancellationToken = default) - { - var baseUrl = BuildBaseUrl(hostnameOrIp, port); - - try - { - using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/server/info"); - if (!string.IsNullOrEmpty(apiKey)) - { - request.Headers.Add("X-Api-Key", apiKey); - } - - using var response = await _httpClient.SendAsync(request, cancellationToken); - return response.IsSuccessStatusCode; - } - catch (HttpRequestException) - { - _logger.LogDebug("Moonraker at {Host}:{Port} is not reachable", hostnameOrIp, port); - return false; - } - } - - /// - /// Builds the base URL for Moonraker API calls from hostname and port. - /// - private static string BuildBaseUrl(string hostnameOrIp, int port) - { - return $"http://{hostnameOrIp}:{port}"; - } -} \ No newline at end of file diff --git a/backend/Program.cs b/backend/Program.cs index c2bdcc9..46780df 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -1,9 +1,7 @@ using System.Reflection; using Extrudex.API.Filters; using Extrudex.API.Hubs; -using Extrudex.API.Jobs; using Extrudex.Domain.Interfaces; -using Extrudex.Infrastructure.Configuration; using Extrudex.Infrastructure.Data; using Extrudex.Infrastructure.Services; using FluentValidation; @@ -52,8 +50,8 @@ builder.Services.AddSwaggerGen(c => // ── QR Code Generation ────────────────────────────────────── builder.Services.AddSingleton(); -// ── Cost Per Print Calculation ───────────────────────────── -builder.Services.AddScoped(); +// ── Low Stock Detection ──────────────────────────────────── +builder.Services.AddSingleton(); // ── FluentValidation ────────────────────────────────────── // Registers all validators from the API assembly into DI. @@ -82,16 +80,6 @@ builder.Services.AddCors(options => // ── SignalR (real-time printer updates) ──────────────────── builder.Services.AddSignalR(); -// ── Filament Usage Sync (Background Job) ────────────────── -builder.Services.Configure( - builder.Configuration.GetSection(FilamentUsageSyncOptions.SectionName)); -builder.Services.AddHttpClient(client => -{ - client.DefaultRequestHeaders.Add("User-Agent", "Extrudex/1.0"); -}); -builder.Services.AddScoped(); -builder.Services.AddHostedService(); - // ── Health Checks ─────────────────────────────────────────── builder.Services.AddHealthChecks() .AddNpgSql(connectionString); diff --git a/backend/appsettings.Development.json b/backend/appsettings.Development.json index 06130e3..8edfdd9 100644 --- a/backend/appsettings.Development.json +++ b/backend/appsettings.Development.json @@ -8,10 +8,5 @@ }, "ConnectionStrings": { "ExtrudexDb": "Host=localhost;Port=5432;Database=extrudex_dev;Username=extrudex;Password=changeme" - }, - "FilamentUsageSync": { - "PollingInterval": "00:01:00", - "RequestTimeout": "00:00:30", - "Enabled": true } } \ No newline at end of file diff --git a/backend/appsettings.json b/backend/appsettings.json index e5c747f..6790af0 100644 --- a/backend/appsettings.json +++ b/backend/appsettings.json @@ -10,9 +10,7 @@ "ConnectionStrings": { "ExtrudexDb": "Host=localhost;Port=5432;Database=extrudex;Username=extrudex;Password=changeme" }, - "FilamentUsageSync": { - "PollingInterval": "00:05:00", - "RequestTimeout": "00:00:30", - "Enabled": true + "FilamentAlerts": { + "LowStockThresholdPercent": 20 } } \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index d00c1e6..d17960c 100755 --- a/deploy.sh +++ b/deploy.sh @@ -18,14 +18,13 @@ echo "📦 Building and starting services..." $COMPOSE_CMD -f docker-compose.dev.yml up -d --build echo "⏳ Waiting for services to become healthy..." -sleep 15 +sleep 10 echo "✅ Deployment complete!" echo "" echo "Services running:" -echo " • PostgreSQL: localhost:5433" echo " • Extrudex API: http://localhost:5080" -echo " • Extrudex Web: http://localhost:5081" +echo " • Control Center Web: http://localhost:5081" echo "" echo "To view logs:" echo " $COMPOSE_CMD -f docker-compose.dev.yml logs -f" diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index a0a3d49..2859dff 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,25 +1,6 @@ -services: - extrudex-db: - image: postgres:16-alpine - container_name: extrudex-db - environment: - POSTGRES_USER: extrudex - POSTGRES_PASSWORD: changeme - POSTGRES_DB: extrudex - ports: - - "5433:5432" - volumes: - - extrudex-db-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U extrudex"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - restart: unless-stopped - networks: - - extrudex-network +version: '3.8' +services: extrudex-api: build: context: ./backend @@ -30,14 +11,6 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://+:8080 - - EXTRUDEX_DB_HOST=extrudex-db - - EXTRUDEX_DB_PORT=5432 - - EXTRUDEX_DB_NAME=extrudex - - EXTRUDEX_DB_USER=extrudex - - EXTRUDEX_DB_PASSWORD=changeme - depends_on: - extrudex-db: - condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] @@ -48,11 +21,11 @@ services: networks: - extrudex-network - extrudex-web: + control-center-web: build: - context: ./frontend + context: ../Control-Center/frontend dockerfile: Dockerfile - container_name: extrudex-web + container_name: control-center-web ports: - "5081:80" depends_on: @@ -62,9 +35,6 @@ services: networks: - extrudex-network -volumes: - extrudex-db-data: - networks: extrudex-network: driver: bridge \ No newline at end of file diff --git a/frontend/.dockerignore b/frontend/.dockerignore deleted file mode 100644 index 6da98e8..0000000 --- a/frontend/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -node_modules -dist -.git -.gitignore -.angular -.vscode -*.md -.editorconfig -.prettierrc -src/test.ts -**/*.spec.ts \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile deleted file mode 100644 index 4aa493c..0000000 --- a/frontend/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -# Stage 1: Build the Angular application -FROM node:22-alpine AS build - -WORKDIR /app - -# Copy package files first for better layer caching -COPY package.json package-lock.json ./ -RUN npm ci - -# Copy source and build -COPY . . -RUN npx ng build --configuration production - -# Stage 2: Serve static files with nginx -FROM nginx:alpine - -# Remove default nginx config -RUN rm /etc/nginx/conf.d/default.conf - -# Copy custom nginx config -COPY nginx.conf /etc/nginx/conf.d/default.conf - -# Copy built Angular artifacts from build stage -COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html - -EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf deleted file mode 100644 index 64b7fa0..0000000 --- a/frontend/nginx.conf +++ /dev/null @@ -1,42 +0,0 @@ -server { - listen 80; - server_name _; - root /usr/share/nginx/html; - index index.html; - - # Gzip compression - gzip on; - gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; - gzip_min_length 256; - - # Angular SPA — fallback to index.html for client-side routing - location / { - try_files $uri $uri/ /index.html; - } - - # Cache static assets aggressively - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Proxy API requests to backend - # Uses resolver so nginx doesn't crash if backend isn't available at startup - resolver 127.0.0.11 valid=30s ipv6=off; - set $backend "extrudex-api:8080"; - - location /api/ { - proxy_pass http://$backend; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Health check endpoint - location /health { - access_log off; - return 200 "ok"; - add_header Content-Type text/plain; - } -} \ No newline at end of file diff --git a/frontend/src/app/components/filament-filter/filament-filter.component.html b/frontend/src/app/components/filament-filter/filament-filter.component.html deleted file mode 100644 index fd93087..0000000 --- a/frontend/src/app/components/filament-filter/filament-filter.component.html +++ /dev/null @@ -1,76 +0,0 @@ - - \ No newline at end of file diff --git a/frontend/src/app/components/filament-filter/filament-filter.component.scss b/frontend/src/app/components/filament-filter/filament-filter.component.scss deleted file mode 100644 index 8ebbaaf..0000000 --- a/frontend/src/app/components/filament-filter/filament-filter.component.scss +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Filament Filter Bar Styles - * Responsive filter layout for kiosk and mobile - */ - -$spacing-unit: 8px; - -.filament-filter-bar { - display: flex; - align-items: center; - gap: $spacing-unit * 2; - flex-wrap: wrap; - padding: $spacing-unit * 2 0; - margin-bottom: $spacing-unit * 2; -} - -// Form field sizing -.filter-field { - flex: 0 1 auto; - min-width: 160px; - - &.material-filter { - min-width: 200px; - } - - &.color-filter { - min-width: 180px; - } - - // Reduce vertical spacing inside filter fields - .mat-mdc-form-field-subscript-wrapper { - display: none; // No hint/error text needed for filters - } -} - -// Selected material chips -.selected-chips { - flex-wrap: wrap; - gap: 4px; -} - -.filter-chip { - font-size: 12px !important; - min-height: 24px !important; - - mat-icon { - font-size: 14px !important; - width: 14px !important; - height: 14px !important; - } -} - -// Active filter icon -.filter-active-icon { - color: var(--mat-sys-primary); - font-size: 18px !important; - width: 18px !important; - height: 18px !important; -} - -// Checkbox styling -.filter-checkbox { - display: flex; - align-items: center; - gap: 4px; - white-space: nowrap; - user-select: none; - touch-action: manipulation; // Prevent zoom on double-tap - - .checkbox-icon { - font-size: 18px !important; - width: 18px !important; - height: 18px !important; - color: var(--mat-sys-on-surface-variant); - transition: color 0.2s ease; - - &.active { - color: var(--mat-sys-primary); - } - } -} - -// Clear filters button -.clear-filters-btn { - display: flex; - align-items: center; - gap: 4px; - font-size: 13px; - - mat-icon { - font-size: 18px !important; - width: 18px !important; - height: 18px !important; - } -} - -// Responsive: stack filters vertically on small screens -@media (max-width: 768px) { - .filament-filter-bar { - flex-direction: column; - align-items: stretch; - gap: $spacing-unit; - } - - .filter-field { - width: 100%; - min-width: unset; - - &.material-filter, - &.color-filter { - min-width: unset; - } - } - - .filter-checkbox { - padding: $spacing-unit 0; - } - - .clear-filters-btn { - align-self: flex-start; - } -} - -// Extra-small screens (phone portrait) -@media (max-width: 480px) { - .filament-filter-bar { - padding: $spacing-unit 0; - margin-bottom: $spacing-unit; - } - - .filter-checkbox { - font-size: 13px; - } -} \ No newline at end of file diff --git a/frontend/src/app/components/filament-filter/filament-filter.component.ts b/frontend/src/app/components/filament-filter/filament-filter.component.ts deleted file mode 100644 index 7559afc..0000000 --- a/frontend/src/app/components/filament-filter/filament-filter.component.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - Output, - computed, - signal, -} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatIconModule } from '@angular/material/icon'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatButtonModule } from '@angular/material/button'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { - Filament, - StockLevel, - classifyStockLevel, -} from '../../models/filament.model'; - -/** Filter state emitted by the filament filter component */ -export interface FilamentFilterState { - /** Selected material base names — empty means all */ - materialBaseNames: string[]; - - /** Color search text — empty string means all */ - colorSearch: string; - - /** Whether to show only low/critical stock */ - lowStockOnly: boolean; - - /** Whether to show only active spools */ - activeOnly: boolean; -} - -/** - * FilamentFilterComponent — Filter bar for the filament inventory list. - * - * Provides: - * - Material type multi-select filter - * - Color name text search - * - Low stock toggle (shows only critical/low spools) - * - Active-only toggle - * - Clear all filters action - */ -@Component({ - selector: 'app-filament-filter', - standalone: true, - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - MatCheckboxModule, - MatIconModule, - MatChipsModule, - MatButtonModule, - MatTooltipModule, - ], - templateUrl: './filament-filter.component.html', - styleUrl: './filament-filter.component.scss', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class FilamentFilterComponent { - /** Filament data input — used to derive material options */ - @Input() set filaments(value: Filament[]) { - this._filaments.set(value); - const materials = [...new Set(value.map((f) => f.materialBaseName))].sort(); - this.materialOptions.set(materials); - } - get filaments(): Filament[] { - return this._filaments(); - } - private readonly _filaments = signal([]); - - /** Available material base names derived from filament data */ - readonly materialOptions = signal([]); - - /** Selected material base names */ - readonly selectedMaterials = signal([]); - - /** Color search text */ - readonly colorSearch = signal(''); - - /** Low stock only toggle */ - readonly lowStockOnly = signal(false); - - /** Active only toggle */ - readonly activeOnly = signal(false); - - /** Computed: whether any filters are active */ - readonly hasActiveFilters = computed( - () => - this.selectedMaterials().length > 0 || - this.colorSearch().trim().length > 0 || - this.lowStockOnly() || - this.activeOnly() - ); - - /** Emits the current filter state whenever filters change */ - @Output() readonly filterChange = new EventEmitter(); - - /** Handle material selection change */ - onMaterialChange(selected: string[]): void { - this.selectedMaterials.set(selected); - this.emitFilterState(); - } - - /** Handle color search input */ - onColorSearchChange(value: string): void { - this.colorSearch.set(value); - this.emitFilterState(); - } - - /** Handle low stock toggle */ - onLowStockToggle(checked: boolean): void { - this.lowStockOnly.set(checked); - this.emitFilterState(); - } - - /** Handle active-only toggle */ - onActiveOnlyToggle(checked: boolean): void { - this.activeOnly.set(checked); - this.emitFilterState(); - } - - /** Remove a single material chip */ - removeMaterial(material: string): void { - const updated = this.selectedMaterials().filter((m) => m !== material); - this.selectedMaterials.set(updated); - this.emitFilterState(); - } - - /** Clear all filters */ - clearAll(): void { - this.selectedMaterials.set([]); - this.colorSearch.set(''); - this.lowStockOnly.set(false); - this.activeOnly.set(false); - this.emitFilterState(); - } - - /** Emit the current filter state */ - private emitFilterState(): void { - this.filterChange.emit({ - materialBaseNames: this.selectedMaterials(), - colorSearch: this.colorSearch().trim().toLowerCase(), - lowStockOnly: this.lowStockOnly(), - activeOnly: this.activeOnly(), - }); - } -} \ No newline at end of file diff --git a/frontend/src/app/components/filament-table/filament-table.component.html b/frontend/src/app/components/filament-table/filament-table.component.html index 5beccd4..75d798f 100644 --- a/frontend/src/app/components/filament-table/filament-table.component.html +++ b/frontend/src/app/components/filament-table/filament-table.component.html @@ -1,12 +1,6 @@ - +
- - - @if (criticalCount() > 0) {