fixes
This commit is contained in:
parent
343fb818c7
commit
f3ac82acb2
|
|
@ -13,6 +13,7 @@ using Nop.Web.Areas.Admin.Controllers;
|
|||
using Nop.Web.Framework;
|
||||
using Nop.Web.Framework.Mvc.Filters;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||
|
|
@ -231,7 +232,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
}
|
||||
|
||||
var filesList = new List<Files>();
|
||||
|
||||
|
||||
|
||||
//iteratation 1: iterate documents to determine their type by AI
|
||||
|
||||
|
|
@ -258,7 +259,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
//if (file.Length > 0 && file.ContentType == "application/pdf")
|
||||
if (file.Length > 0)
|
||||
{
|
||||
if (file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)){
|
||||
if (file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open the PDF from the IFormFile's stream directly in memory
|
||||
|
|
@ -336,7 +338,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
_logger.Detail(pdfText);
|
||||
}
|
||||
|
||||
|
||||
string analysisPrompt = "Extract the document identification number from this document, determine the type of the " +
|
||||
"document IN ENGLISH from the available list, and return them as JSON: documentNumber, documentType. " +
|
||||
$"Available filetypes: {nameof(DocumentType.Invoice)}, {nameof(DocumentType.ShippingDocument)} , {nameof(DocumentType.OrderConfirmation)}, {nameof(DocumentType.Unknown)}" +
|
||||
|
|
@ -377,65 +378,206 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
// - IF WE DON'T HAVE PARTNERID ALREADY: read partner information
|
||||
// (check if all 3 refers to the same partner)
|
||||
// save partner information to partners table { Id, Name, TaxId, CertificationNumber, PostalCode, Country, State, County, City, Street }
|
||||
if (partnerId != null)
|
||||
if (partnerId == null)
|
||||
{
|
||||
string partnerAnalysisPrompt = "Extract the partner information from this document, and return them as JSON: name, taxId, certificationNumber, postalCode, country, state, county, city, street. " +
|
||||
"If you can't find information of any of these, return null value for that field.";
|
||||
|
||||
//here I can start preparing the file entity
|
||||
var partnerAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), partnerAnalysisPrompt);
|
||||
var extractedPartnerData = ParsePartnerDataAIResponse(partnerAnalyzis);
|
||||
//find partner in DB... there is a serious chance that the partner Name and Taxid determines the partner
|
||||
|
||||
if (extractedPartnerData.Name != null)
|
||||
var partners = await _dbContext.Partners.GetAll().ToListAsync();
|
||||
foreach (var partner in partners)
|
||||
{
|
||||
_logger.Detail("AI Analysis Partner Result:");
|
||||
_logger.Detail(extractedPartnerData.Name);
|
||||
if (pdfText.Contains(partner.Name) || (partner.TaxId != null && pdfText.Contains(partner.TaxId)))
|
||||
{
|
||||
partnerId = partner.Id;
|
||||
_logger.Detail($"Found existing partner in DB: {partner.Name} (ID: {partner.Id})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (extractedPartnerData.TaxId != null)
|
||||
if (partnerId == null)
|
||||
{
|
||||
_logger.Detail(extractedPartnerData.TaxId);
|
||||
}
|
||||
_logger.Detail("No existing partner found in DB, proceeding to extract partner info via AI.");
|
||||
|
||||
if (extractedPartnerData.Country != null)
|
||||
{
|
||||
string partnerAnalysisPrompt = "You are an agent of Fruitbank, helping to analyze pdf content. Determine which partner of Fruitbank sent this document, extract their data and return them as JSON: name, taxId, certificationNumber, postalCode, country, state, county, city, street. " +
|
||||
"If you can't find information of any of these, return null value for that field.";
|
||||
|
||||
_logger.Detail(extractedPartnerData.Country);
|
||||
}
|
||||
//here I can start preparing the file entity
|
||||
var partnerAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), partnerAnalysisPrompt);
|
||||
var extractedPartnerData = ParsePartnerDataAIResponse(partnerAnalyzis);
|
||||
|
||||
if (extractedPartnerData.Name != null)
|
||||
{
|
||||
_logger.Detail("AI Analysis Partner Result:");
|
||||
_logger.Detail(extractedPartnerData.Name);
|
||||
}
|
||||
|
||||
if (extractedPartnerData.TaxId != null)
|
||||
{
|
||||
_logger.Detail(extractedPartnerData.TaxId);
|
||||
}
|
||||
|
||||
if (extractedPartnerData.Country != null)
|
||||
{
|
||||
|
||||
_logger.Detail(extractedPartnerData.Country);
|
||||
}
|
||||
|
||||
if (extractedPartnerData.State != null)
|
||||
{
|
||||
_logger.Detail(extractedPartnerData.State);
|
||||
}
|
||||
|
||||
if (extractedPartnerData.State != null)
|
||||
{
|
||||
_logger.Detail(extractedPartnerData.State);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// - save the documents to file system - wwwroot/uploads/orders/order-{orderId}/fileId-documentId.pdf
|
||||
// where documentId is the number or id IN the document
|
||||
|
||||
//shortcut
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// Create upload directory if it doesn't exist
|
||||
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "orders", "order" + shippingDocumentId.ToString(), "documents");
|
||||
Directory.CreateDirectory(uploadsPath);
|
||||
|
||||
// Generate unique filename
|
||||
fileName = $"{Guid.NewGuid()}_{file.FileName}";
|
||||
var filePath = Path.Combine(uploadsPath, fileName);
|
||||
|
||||
// Save file
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
var partnerPrompt = @"Extract the partner/company information from the following text and return ONLY a valid JSON object with these exact fields:
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
""Name"": ""company name"",
|
||||
""TaxId"": ""tax identification number"",
|
||||
""CertificationNumber"": ""certification number if exists, otherwise empty string"",
|
||||
""PostalCode"": ""postal code"",
|
||||
""Country"": ""country name"",
|
||||
""State"": ""state if exists, otherwise empty string"",
|
||||
""County"": ""county if exists, otherwise empty string"",
|
||||
""City"": ""city name"",
|
||||
""Street"": ""street address""
|
||||
}
|
||||
|
||||
If a field is not found in the text, use an empty string. Return ONLY the JSON object, no additional text or explanation.
|
||||
|
||||
Text to analyze:
|
||||
" + pdfText;
|
||||
|
||||
var partnerResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
||||
pdfText,
|
||||
partnerPrompt
|
||||
);
|
||||
|
||||
var partner = JsonSerializer.Deserialize<Partner>(CleanJsonResponse(partnerResponse),
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
// Step 2: Extract Shipping Document Information
|
||||
var shippingDocPrompt = @"Extract the shipping document information from the following text and return ONLY a valid JSON object with these exact fields:
|
||||
{
|
||||
""DocumentIdNumber"": ""document ID or reference number if exists, otherwise empty string"",
|
||||
""ShippingDate"": ""shipping date in ISO format (yyyy-MM-dd)"",
|
||||
""Country"": ""country of origin for pickup"",
|
||||
""TotalPallets"": number_of_total_pallets
|
||||
}
|
||||
|
||||
If a field is not found, use empty string for text fields, current date for ShippingDate, and 0 for TotalPallets. Return ONLY the JSON object, no additional text or explanation.
|
||||
|
||||
Text to analyze:
|
||||
" + pdfText;
|
||||
|
||||
var shippingDocResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
||||
pdfText,
|
||||
shippingDocPrompt
|
||||
);
|
||||
|
||||
var shippingDocData = JsonSerializer.Deserialize<ShippingDocumentDto>(CleanJsonResponse(shippingDocResponse),
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
// Step 3: Extract Shipping Items
|
||||
var itemsPrompt = @"Extract all shipping items (fruits, vegetables, or products) from the following text and return ONLY a valid JSON array with objects having these exact fields:
|
||||
[
|
||||
{
|
||||
""Name"": ""product name"",
|
||||
""PalletsOnDocument"": number_of_pallets,
|
||||
""QuantityOnDocument"": quantity_count,
|
||||
""NetWeightOnDocument"": net_weight_in_kg,
|
||||
""GrossWeightOnDocument"": gross_weight_in_kg
|
||||
}
|
||||
]
|
||||
|
||||
If a numeric field is not found, use 0. Return ONLY the JSON array, no additional text or explanation.
|
||||
|
||||
Text to analyze:
|
||||
" + pdfText;
|
||||
|
||||
var itemsResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
||||
pdfText,
|
||||
itemsPrompt
|
||||
);
|
||||
|
||||
var items = JsonSerializer.Deserialize<List<ShippingItem>>(CleanJsonResponse(itemsResponse),
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? new List<ShippingItem>();
|
||||
|
||||
// Prepare result
|
||||
var result = new ShippingDocumentAnalysisResult
|
||||
{
|
||||
Partner = partner,
|
||||
ShippingDocument = new ShippingDocument
|
||||
{
|
||||
DocumentIdNumber = shippingDocData.DocumentIdNumber,
|
||||
ShippingDate = shippingDocData.ShippingDate,
|
||||
Country = shippingDocData.Country,
|
||||
TotalPallets = shippingDocData.TotalPallets
|
||||
},
|
||||
ShippingItems = items
|
||||
};
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
return BadRequest($"Failed to parse AI response: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"Error saving file: {ex.Message}", ex);
|
||||
//return Json(new { success = false, errorMessage = ex.Message });
|
||||
return BadRequest("No files were uploaded.");
|
||||
return StatusCode(500, $"An error occurred: {ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// - save the documents to file system - wwwroot/uploads/orders/order-{orderId}/fileId-documentId.pdf
|
||||
// where documentId is the number or id IN the document
|
||||
//try
|
||||
//{
|
||||
// // Create upload directory if it doesn't exist
|
||||
// var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "orders", "order" + shippingDocumentId.ToString(), "documents");
|
||||
// Directory.CreateDirectory(uploadsPath);
|
||||
|
||||
// // Generate unique filename
|
||||
// fileName = $"{Guid.NewGuid()}_{file.FileName}";
|
||||
// var filePath = Path.Combine(uploadsPath, fileName);
|
||||
|
||||
// // Save file
|
||||
// using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
// {
|
||||
// await file.CopyToAsync(stream);
|
||||
// }
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// _logger.Error($"Error saving file: {ex.Message}", ex);
|
||||
// //return Json(new { success = false, errorMessage = ex.Message });
|
||||
// return BadRequest("No files were uploaded.");
|
||||
//}
|
||||
|
||||
|
||||
// - create a list of documents to read information and to save the document's information to DB
|
||||
// - INSIDE ITERATION:
|
||||
// - IF SHIPPINGDOCUMENT: read shipping information
|
||||
|
|
@ -446,68 +588,70 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
// save document information into DB {financial db table not created yet}
|
||||
// - save the documents to Files Table - { name, extension, type, rawtext }
|
||||
|
||||
try
|
||||
{
|
||||
//try
|
||||
//{
|
||||
|
||||
// Analyze PDF with AI to extract structured data
|
||||
var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
||||
pdfText.ToString(),
|
||||
"You work for FruitBank. Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field."
|
||||
);
|
||||
// // Analyze PDF with AI to extract structured data
|
||||
// var lastPrompt = "You work for FruitBank. Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field.";
|
||||
|
||||
// Parse AI response (assuming it returns JSON)
|
||||
var extractedData = ParseShippingDocumentAIResponse(aiAnalysis);
|
||||
// var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
||||
// pdfText,
|
||||
// lastPrompt
|
||||
// );
|
||||
|
||||
//TODO: Save document record to database
|
||||
_logger.Detail("AI Analysis Result:");
|
||||
_logger.Detail(extractedData.RecipientName);
|
||||
_logger.Detail(extractedData.SenderName);
|
||||
_logger.Detail(extractedData.InvoiceNumber);
|
||||
_logger.Detail(extractedData.TotalAmount.ToString());
|
||||
_logger.Detail(extractedData.ItemCount.ToString());
|
||||
_logger.Detail(extractedData.Notes);
|
||||
// // Parse AI response (assuming it returns JSON)
|
||||
// var extractedData = ParseShippingDocumentAIResponse(aiAnalysis);
|
||||
|
||||
// //TODO: Save document record to database
|
||||
// _logger.Detail("AI Analysis Result:");
|
||||
// _logger.Detail(extractedData.RecipientName);
|
||||
// _logger.Detail(extractedData.SenderName);
|
||||
// _logger.Detail(extractedData.InvoiceNumber);
|
||||
// _logger.Detail(extractedData.TotalAmount.ToString());
|
||||
// _logger.Detail(extractedData.ItemCount.ToString());
|
||||
// _logger.Detail(extractedData.Notes);
|
||||
|
||||
|
||||
var documentId = 1; // Replace with: savedDocument.Id
|
||||
// var documentId = 1; // Replace with: savedDocument.Id
|
||||
|
||||
// Return structured document model
|
||||
var documentModel = new ShippingDocumentModel
|
||||
{
|
||||
Id = documentId,
|
||||
ShippingId = shippingDocumentId,
|
||||
FileName = file.FileName,
|
||||
FilePath = $"/uploads/shippingDocuments/{fileName}",
|
||||
FileSize = (int)(file.Length / 1024),
|
||||
DocumentDate = extractedData.DocumentDate,
|
||||
RecipientName = extractedData.RecipientName,
|
||||
SenderName = extractedData.SenderName,
|
||||
InvoiceNumber = extractedData.InvoiceNumber,
|
||||
TotalAmount = extractedData.TotalAmount,
|
||||
ItemCount = extractedData.ItemCount,
|
||||
Notes = extractedData.Notes,
|
||||
RawAIAnalysis = aiAnalysis // Store the raw AI response for debugging
|
||||
};
|
||||
// // Return structured document model
|
||||
// var documentModel = new ShippingDocumentModel
|
||||
// {
|
||||
// Id = documentId,
|
||||
// ShippingId = shippingDocumentId,
|
||||
// FileName = file.FileName,
|
||||
// FilePath = $"/uploads/shippingDocuments/{fileName}",
|
||||
// FileSize = (int)(file.Length / 1024),
|
||||
// DocumentDate = extractedData.DocumentDate,
|
||||
// RecipientName = extractedData.RecipientName,
|
||||
// SenderName = extractedData.SenderName,
|
||||
// InvoiceNumber = extractedData.InvoiceNumber,
|
||||
// TotalAmount = extractedData.TotalAmount,
|
||||
// ItemCount = extractedData.ItemCount,
|
||||
// Notes = extractedData.Notes,
|
||||
// RawAIAnalysis = aiAnalysis // Store the raw AI response for debugging
|
||||
// };
|
||||
|
||||
|
||||
|
||||
// var savedDocument = await _documentService.InsertDocumentAsync(document);
|
||||
// // var savedDocument = await _documentService.InsertDocumentAsync(document);
|
||||
|
||||
// Mock saved document ID
|
||||
// // Mock saved document ID
|
||||
|
||||
|
||||
//return Json(new
|
||||
//{
|
||||
// success = true,
|
||||
// document = documentModel
|
||||
//});
|
||||
// //return Json(new
|
||||
// //{
|
||||
// // success = true,
|
||||
// // document = documentModel
|
||||
// //});
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"Error uploading file: {ex.Message}", ex);
|
||||
//return Json(new { success = false, errorMessage = ex.Message });
|
||||
return BadRequest("No files were uploaded.");
|
||||
}
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// _logger.Error($"Error uploading file: {ex.Message}", ex);
|
||||
// //return Json(new { success = false, errorMessage = ex.Message });
|
||||
// return BadRequest("No files were uploaded.");
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -550,6 +694,47 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
// return Ok(new { message = "Files uploaded successfully." });
|
||||
//}
|
||||
|
||||
private string CleanJsonResponse(string response)
|
||||
{
|
||||
// Remove markdown code blocks if present
|
||||
response = response.Trim();
|
||||
if (response.StartsWith("```json"))
|
||||
{
|
||||
response = response.Substring(7);
|
||||
}
|
||||
else if (response.StartsWith("```"))
|
||||
{
|
||||
response = response.Substring(3);
|
||||
}
|
||||
|
||||
if (response.EndsWith("```"))
|
||||
{
|
||||
response = response.Substring(0, response.Length - 3);
|
||||
}
|
||||
|
||||
return response.Trim();
|
||||
}
|
||||
|
||||
public class ShippingDocumentAnalysisRequest
|
||||
{
|
||||
public string RawText { get; set; }
|
||||
}
|
||||
|
||||
public class ShippingDocumentAnalysisResult
|
||||
{
|
||||
public Partner Partner { get; set; }
|
||||
public ShippingDocument ShippingDocument { get; set; }
|
||||
public List<ShippingItem> ShippingItems { get; set; }
|
||||
}
|
||||
|
||||
public class ShippingDocumentDto
|
||||
{
|
||||
public string DocumentIdNumber { get; set; }
|
||||
public DateTime ShippingDate { get; set; }
|
||||
public string Country { get; set; }
|
||||
public int TotalPallets { get; set; }
|
||||
}
|
||||
|
||||
|
||||
private ExtractedDocumentData ParseShippingDocumentAIResponse(string aiResponse)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
var hideNotesBlock = await genericAttributeService.GetAttributeAsync<bool>(customer, hideNotesBlockAttributeName);
|
||||
}
|
||||
|
||||
<form asp-controller="Order" asp-action="Edit" method="post" id="order-form">
|
||||
<form asp-controller="CustomOrder" asp-action="Edit" method="post" id="order-form">
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
@T("Admin.Orders.EditOrderDetails") - @Model.CustomOrderNumber
|
||||
|
|
|
|||
|
|
@ -219,6 +219,8 @@
|
|||
UrlRead = new DataUrl("ProductList", "CustomProduct", null),
|
||||
SearchButtonId = "search-products",
|
||||
Length = Model.PageSize,
|
||||
Ordering = true,
|
||||
ServerSide = false,
|
||||
LengthMenu = Model.AvailablePageSizes,
|
||||
Filters = new List<FilterParameter>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@model Nop.Web.Models.Catalog.ProductOverviewModel
|
||||
|
||||
@if (Model != null)
|
||||
@* @if (Model != null)
|
||||
{
|
||||
<div class="ai-question-box">
|
||||
<h5>Kérdezz a termékről!</h5>
|
||||
|
|
@ -16,5 +16,5 @@
|
|||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
} *@
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@model Nop.Web.Models.Catalog.ProductDetailsModel
|
||||
|
||||
@if (Model != null)
|
||||
@* @if (Model != null)
|
||||
{
|
||||
<div class="ai-question-box">
|
||||
<h5>Kérdezz a termékről!</h5>
|
||||
|
|
@ -16,5 +16,5 @@
|
|||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
} *@
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue