Files
PepApi.Core/PepApi.Core/Controllers/DrawingsController.cs
T
ajandClaude Sonnet 4.6 5e0856b5d1 fix: resolve API route name collisions
- DrawingsController: rename 'name/{name}' to 'by-name/{name}' to
  eliminate ambiguous match with '{name}/dxf' on path /drawings/name/dxf
- NestsController: add regex constraint to '{nestName}/download' and
  '{nestName}/plates' to prevent collision with year-parameterized routes
  when nestName is a 4-digit integer
- PepMcp/PepTools.cs: update GetDrawingByName URL to match renamed route

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 07:20:53 -04:00

269 lines
9.2 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using PepApi.Core.Configuration;
using PepApi.Core.Models;
using PepLib.Data;
using PepLib.IO;
namespace PepApi.Core.Controllers;
[ApiController]
[Route("drawings")]
public class DrawingsController : ControllerBase
{
private readonly PepDB _db;
private readonly PepSettings _settings;
public DrawingsController(PepDB db, IOptions<PepSettings> settings)
{
_db = db;
_settings = settings.Value;
}
/// <summary>
/// Get a list of drawings with optional filtering.
/// </summary>
[HttpGet]
public async Task<ActionResult<List<DrawingSummary>>> GetDrawings([FromQuery] DrawingFilterData? filter)
{
var drawings = await _db.Drawings
.Select(d => new DrawingSummary
{
ID = d.ID,
Name = d.Name,
Customer = d.Customer,
CustID = d.CustID,
Revision = d.Revision,
Description = d.Description,
Material = d.Material,
MaterialGrade = d.MatGrade,
Status = d.Status,
Type = d.Type,
Application = d.Application,
Programmer = d.Programmer,
CreatedBy = d.CreatedBy,
Width = d.Width,
Length = d.Length,
TrueArea = d.TrueArea,
CutLength = d.CutLength,
CreationDate = d.CreationDate,
LastEditDate = d.LastEditDate,
ModifiedDate = d.ModifiedDate,
HasBevel = d.HasBevel != 0,
HasLeadIn = d.HasLeadIn != 0,
HasTab = d.HasTab != 0
})
.ToListAsync();
if (filter != null)
{
var filtered = filter.Apply(drawings.AsQueryable()).ToList();
var paginated = filtered.Skip(filter.Offset).Take(filter.Limit).ToList();
return Ok(paginated);
}
return Ok(drawings.Take(100).ToList());
}
/// <summary>
/// Get a drawing by ID.
/// </summary>
[HttpGet("{id:int}")]
public async Task<ActionResult<DrawingDetails>> GetDrawingById(int id)
{
var drawing = await _db.Drawings
.Where(d => d.ID == id)
.FirstOrDefaultAsync();
if (drawing == null)
return NotFound(new { message = "Drawing not found" });
return Ok(ConvertToDetails(drawing));
}
/// <summary>
/// Get a drawing by exact name.
/// </summary>
[HttpGet("by-name/{name}")]
public async Task<ActionResult<DrawingDetails>> GetDrawingByName(string name)
{
var drawing = await _db.Drawings
.Where(d => d.Name.ToUpper() == name.ToUpper())
.FirstOrDefaultAsync();
if (drawing == null)
return NotFound(new { message = "Drawing not found" });
return Ok(ConvertToDetails(drawing));
}
/// <summary>
/// Search drawings by partial name.
/// </summary>
[HttpGet("search")]
public async Task<ActionResult<DrawingSearchResponse>> SearchDrawings(
[FromQuery] string search,
[FromQuery] int limit = 100)
{
if (string.IsNullOrWhiteSpace(search))
return BadRequest(new { message = "Search term is required" });
var searchUpper = search.Trim().ToUpper();
var allMatches = await _db.Drawings
.Where(d => d.Name.ToUpper().Contains(searchUpper))
.Select(d => new DrawingSummary
{
ID = d.ID,
Name = d.Name,
Customer = d.Customer,
CustID = d.CustID,
Revision = d.Revision,
Description = d.Description,
Material = d.Material,
MaterialGrade = d.MatGrade,
Status = d.Status,
Type = d.Type,
Application = d.Application,
Programmer = d.Programmer,
CreatedBy = d.CreatedBy,
Width = d.Width,
Length = d.Length,
TrueArea = d.TrueArea,
CutLength = d.CutLength,
CreationDate = d.CreationDate,
LastEditDate = d.LastEditDate,
ModifiedDate = d.ModifiedDate,
HasBevel = d.HasBevel != 0,
HasLeadIn = d.HasLeadIn != 0,
HasTab = d.HasTab != 0
})
.ToListAsync();
var limitedResults = limit > 0 ? allMatches.Take(limit).ToList() : allMatches;
return Ok(new DrawingSearchResponse
{
SearchTerm = search,
TotalMatches = allMatches.Count,
ResultsReturned = limitedResults.Count,
Results = limitedResults
});
}
/// <summary>
/// Export a drawing as a DXF file.
/// </summary>
[HttpGet("{name}/dxf")]
public async Task<IActionResult> GetDrawingDxf(string name)
{
var drawing = await _db.Drawings
.Where(d => d.Name.ToUpper() == name.ToUpper())
.FirstOrDefaultAsync();
if (drawing == null)
return NotFound(new { message = "Drawing not found in database" });
if (string.IsNullOrEmpty(_settings.DrawingsDirectory))
return StatusCode(503, new { message = "DrawingsDirectory is not configured" });
var filePath = Path.Combine(_settings.DrawingsDirectory, drawing.File);
if (!System.IO.File.Exists(filePath))
return NotFound(new { message = $"Drawing file not found: {drawing.File}" });
PepLib.Models.Drawing pep;
try
{
pep = PepLib.Models.Drawing.Load(filePath);
}
catch (Exception ex)
{
return StatusCode(500, new { message = $"Failed to load drawing: {ex.Message}" });
}
var stream = DrawingDxfExporter.Export(pep);
return File(stream, "application/dxf", $"{name}.dxf");
}
private static DrawingDetails ConvertToDetails(Drawing drawing)
{
return new DrawingDetails
{
ID = drawing.ID,
Name = drawing.Name,
CustID = drawing.CustID,
Revision = drawing.Revision,
Path = drawing.Path,
File = drawing.File,
InUseBy = drawing.InUseBy,
InUseDate = drawing.InUseDate,
Status = drawing.Status,
StatusModifiedBy = drawing.StatusModifiedBy,
StatusModifiedDate = drawing.StatusModifiedDate,
CreationDate = drawing.CreationDate,
LastEditDate = drawing.LastEditDate,
LastRefDate = drawing.LastRefDate,
Description = drawing.Description,
Customer = drawing.Customer,
Comment = drawing.Comment,
Notes = drawing.Notes,
Grain = drawing.Grain,
GrainAngle = drawing.GrainAngle,
Material = drawing.Material,
MaterialGrade = drawing.MatGrade,
Programmer = drawing.Programmer,
CreatedBy = drawing.CreatedBy,
Type = drawing.Type,
CommonCut = drawing.CommonCut,
CombineCut = drawing.CombineCut,
Errors = drawing.Errors,
Hardness = drawing.Hardness,
Specification = drawing.Specification,
NestInCutOuts = drawing.NestInCutOuts,
UserDefined1 = drawing.UserDefined1,
UserDefined2 = drawing.UserDefined2,
UserDefined3 = drawing.UserDefined3,
UserDefined4 = drawing.UserDefined4,
UserDefined5 = drawing.UserDefined5,
UserDefined6 = drawing.UserDefined6,
Machine = drawing.Machine,
Application = drawing.Application,
PartCount = drawing.PartCount,
Color = drawing.Color,
CombineMethod = drawing.CombineMethod,
SeqCutouts = drawing.SeqCutouts,
AllowMirror = drawing.AllowMirror,
SourceFile = drawing.SourceFile,
SourceDate = drawing.SourceDate,
SourceSize = drawing.SourceSize,
CadScaled = drawing.CadScaled,
CadDimVerified = drawing.CadDimVerified,
CadDimCount = drawing.CadDimCount,
Width = drawing.Width,
Length = drawing.Length,
RectArea = drawing.RectArea,
ExtArea = drawing.ExtArea,
TrueArea = drawing.TrueArea,
ExtUtil = drawing.ExtUtil,
TrueUtil = drawing.TrueUtil,
SmallestAreaAng = drawing.SmallestAreaAng,
SmallestAreaLen = drawing.SmallestAreaLen,
SmallestAreaWid = drawing.SmallestAreaWid,
SmallestYAng = drawing.SmallestYAng,
SmallestYLen = drawing.SmallestYLen,
SmallestYWid = drawing.SmallestYWid,
CutLength = drawing.CutLength,
ScribeLength = drawing.ScribeLength,
Checked = drawing.Checked,
PepBendStatus = drawing.PepBendStatus,
HasBevel = drawing.HasBevel != 0,
HasLeadIn = drawing.HasLeadIn != 0,
HasTab = drawing.HasTab != 0,
ModifiedDate = drawing.ModifiedDate,
ModifiedBy = drawing.ModifiedBy
};
}
}