Fix: ViewReceipt parse button now uses parse queue instead of direct parsing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -156,28 +156,20 @@
|
|||||||
<strong>Parse Receipt</strong>
|
<strong>Parse Receipt</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@if (Model.AvailableParsers.Any())
|
<form method="post" asp-page-handler="Parse" asp-route-id="@Model.Receipt.Id">
|
||||||
{
|
<p class="text-muted small mb-2">
|
||||||
<form method="post" asp-page-handler="Parse" asp-route-id="@Model.Receipt.Id">
|
Using: <strong>@Model.SelectedModel</strong>
|
||||||
<input type="hidden" name="parser" value="@Model.AvailableParsers.First().FullName" />
|
<a href="/Settings" class="ms-2 small">Change</a>
|
||||||
<p class="text-muted small mb-2">
|
</p>
|
||||||
Using: <strong>@Model.SelectedModel</strong>
|
<div class="mb-2">
|
||||||
<a href="/Settings" class="ms-2 small">Change</a>
|
<label for="ParsingNotes" class="form-label small text-muted mb-1">Notes for AI</label>
|
||||||
</p>
|
<textarea asp-for="ParsingNotes" class="form-control form-control-sm" rows="3"
|
||||||
<div class="mb-2">
|
placeholder="Optional hints for parsing (e.g., 'This is a restaurant receipt', 'Ignore the voided items')"></textarea>
|
||||||
<label for="ParsingNotes" class="form-label small text-muted mb-1">Notes for AI</label>
|
</div>
|
||||||
<textarea asp-for="ParsingNotes" class="form-control form-control-sm" rows="3"
|
<button type="submit" class="btn btn-primary btn-sm w-100">
|
||||||
placeholder="Optional hints for parsing (e.g., 'This is a restaurant receipt', 'Ignore the voided items')"></textarea>
|
Parse Receipt
|
||||||
</div>
|
</button>
|
||||||
<button type="submit" class="btn btn-primary btn-sm w-100">
|
</form>
|
||||||
Parse Receipt
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<p class="text-muted small mb-0">No parsers available</p>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,25 +11,24 @@ namespace MoneyMap.Pages
|
|||||||
{
|
{
|
||||||
private readonly MoneyMapContext _db;
|
private readonly MoneyMapContext _db;
|
||||||
private readonly IReceiptManager _receiptManager;
|
private readonly IReceiptManager _receiptManager;
|
||||||
private readonly IEnumerable<IReceiptParser> _parsers;
|
private readonly IReceiptParseQueue _parseQueue;
|
||||||
private readonly IConfiguration _config;
|
private readonly IConfiguration _config;
|
||||||
|
|
||||||
public ViewReceiptModel(
|
public ViewReceiptModel(
|
||||||
MoneyMapContext db,
|
MoneyMapContext db,
|
||||||
IReceiptManager receiptManager,
|
IReceiptManager receiptManager,
|
||||||
IEnumerable<IReceiptParser> parsers,
|
IReceiptParseQueue parseQueue,
|
||||||
IConfiguration config)
|
IConfiguration config)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_receiptManager = receiptManager;
|
_receiptManager = receiptManager;
|
||||||
_parsers = parsers;
|
_parseQueue = parseQueue;
|
||||||
_config = config;
|
_config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Receipt? Receipt { get; set; }
|
public Receipt? Receipt { get; set; }
|
||||||
public List<ReceiptLineItem> LineItems { get; set; } = new();
|
public List<ReceiptLineItem> LineItems { get; set; } = new();
|
||||||
public List<ReceiptParseLog> ParseLogs { get; set; } = new();
|
public List<ReceiptParseLog> ParseLogs { get; set; } = new();
|
||||||
public List<ParserOption> AvailableParsers { get; set; } = new();
|
|
||||||
public string ReceiptUrl { get; set; } = "";
|
public string ReceiptUrl { get; set; } = "";
|
||||||
public string SelectedModel => _config["AI:ReceiptParsingModel"] ?? "gpt-4o-mini";
|
public string SelectedModel => _config["AI:ReceiptParsingModel"] ?? "gpt-4o-mini";
|
||||||
|
|
||||||
@@ -62,13 +61,6 @@ namespace MoneyMap.Pages
|
|||||||
// Get receipt URL for display - use handler parameter
|
// Get receipt URL for display - use handler parameter
|
||||||
ReceiptUrl = $"/ViewReceipt/{id}?handler=file";
|
ReceiptUrl = $"/ViewReceipt/{id}?handler=file";
|
||||||
|
|
||||||
// Get available parsers
|
|
||||||
AvailableParsers = _parsers.Select(p => new ParserOption
|
|
||||||
{
|
|
||||||
Name = p.GetType().Name.Replace("ReceiptParser", ""),
|
|
||||||
FullName = p.GetType().Name
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,35 +86,26 @@ namespace MoneyMap.Pages
|
|||||||
return File(fileBytes, receipt.ContentType);
|
return File(fileBytes, receipt.ContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> OnPostParseAsync(long id, string parser)
|
public async Task<IActionResult> OnPostParseAsync(long id)
|
||||||
{
|
{
|
||||||
var selectedParser = _parsers.FirstOrDefault(p => p.GetType().Name == parser);
|
var receipt = await _db.Receipts.FindAsync(id);
|
||||||
|
|
||||||
if (selectedParser == null)
|
if (receipt == null)
|
||||||
{
|
{
|
||||||
ErrorMessage = "Parser not found.";
|
ErrorMessage = "Receipt not found.";
|
||||||
return RedirectToPage(new { id });
|
return RedirectToPage(new { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the configured model from settings, pass user notes
|
// Save parsing notes to the receipt entity so the worker can use them
|
||||||
var result = await selectedParser.ParseReceiptAsync(id, SelectedModel, ParsingNotes);
|
receipt.ParsingNotes = ParsingNotes;
|
||||||
|
receipt.ParseStatus = ReceiptParseStatus.Queued;
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
if (result.IsSuccess)
|
// Enqueue the receipt for parsing
|
||||||
{
|
await _parseQueue.EnqueueAsync(id);
|
||||||
SuccessMessage = result.Message;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ErrorMessage = result.Message;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
SuccessMessage = "Receipt queued for parsing.";
|
||||||
return RedirectToPage(new { id });
|
return RedirectToPage(new { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ParserOption
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string FullName { get; set; } = "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,6 +60,9 @@ namespace MoneyMap.Services
|
|||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
return ReceiptParseResult.Failure("Receipt file not found on disk.");
|
return ReceiptParseResult.Failure("Receipt file not found on disk.");
|
||||||
|
|
||||||
|
// Fall back to receipt.ParsingNotes if notes parameter is null
|
||||||
|
var effectiveNotes = notes ?? receipt.ParsingNotes;
|
||||||
|
|
||||||
var selectedModel = model ?? _configuration["AI:ReceiptParsingModel"] ?? "gpt-4o-mini";
|
var selectedModel = model ?? _configuration["AI:ReceiptParsingModel"] ?? "gpt-4o-mini";
|
||||||
var (client, provider) = _clientResolver.Resolve(selectedModel);
|
var (client, provider) = _clientResolver.Resolve(selectedModel);
|
||||||
|
|
||||||
@@ -79,7 +82,7 @@ namespace MoneyMap.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (base64Data, mediaType) = await PrepareImageDataAsync(receipt, filePath);
|
var (base64Data, mediaType) = await PrepareImageDataAsync(receipt, filePath);
|
||||||
var promptText = await BuildPromptAsync(receipt, notes, client);
|
var promptText = await BuildPromptAsync(receipt, effectiveNotes, client);
|
||||||
var visionResult = await CallVisionClientAsync(client, base64Data, mediaType, promptText, selectedModel);
|
var visionResult = await CallVisionClientAsync(client, base64Data, mediaType, promptText, selectedModel);
|
||||||
|
|
||||||
if (!visionResult.IsSuccess)
|
if (!visionResult.IsSuccess)
|
||||||
@@ -89,7 +92,7 @@ namespace MoneyMap.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
var parseData = ParseResponse(visionResult.Content);
|
var parseData = ParseResponse(visionResult.Content);
|
||||||
await ApplyParseResultAsync(receipt, receiptId, parseData, notes);
|
await ApplyParseResultAsync(receipt, receiptId, parseData, effectiveNotes);
|
||||||
|
|
||||||
parseLog.Success = true;
|
parseLog.Success = true;
|
||||||
parseLog.Confidence = parseData.Confidence;
|
parseLog.Confidence = parseData.Confidence;
|
||||||
|
|||||||
Reference in New Issue
Block a user