improvements, fixes
This commit is contained in:
parent
9b0afb2d2c
commit
d84276e3ac
|
|
@ -483,8 +483,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
|
|
||||||
await _orderService.UpdateOrderAsync(order);
|
await _orderService.UpdateOrderAsync(order);
|
||||||
|
|
||||||
var orderDto = await _dbContext.OrderDtos.GetByIdAsync(order.Id, true);
|
//var orderDto = await _dbContext.OrderDtos.GetByIdAsync(order.Id, true);
|
||||||
await _sendToClient.SendMeasuringNotification("Módosult a rendelés, mérjétek újra!", orderDto);
|
//await _sendToClient.SendMeasuringNotification("Módosult a rendelés, mérjétek újra!", orderDto);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ using Nop.Web.Framework;
|
||||||
using Nop.Web.Framework.Mvc.Filters;
|
using Nop.Web.Framework.Mvc.Filters;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using AyCode.Core.Consts;
|
||||||
using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
|
using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
|
||||||
|
using AyCode.Core.Extensions;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -68,6 +70,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
testGridModel2.Configuration = new GridConfiguration();
|
testGridModel2.Configuration = new GridConfiguration();
|
||||||
testGridModel2.Configuration.ShowChildGridsAsTabs = true;
|
testGridModel2.Configuration.ShowChildGridsAsTabs = true;
|
||||||
testGridModel2.ChildGrids = new List<TestGridModel>();
|
testGridModel2.ChildGrids = new List<TestGridModel>();
|
||||||
|
|
||||||
var childGrid1 = new TestGridModel
|
var childGrid1 = new TestGridModel
|
||||||
{
|
{
|
||||||
GridName = "TestGrid",
|
GridName = "TestGrid",
|
||||||
|
|
@ -88,7 +91,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
};
|
};
|
||||||
testGridModel2.ChildGrids.Add(childGrid2);
|
testGridModel2.ChildGrids.Add(childGrid2);
|
||||||
|
|
||||||
|
|
||||||
testPageModel.Grids.Add(testGridModel2);
|
testPageModel.Grids.Add(testGridModel2);
|
||||||
|
|
||||||
var testGridModel = new TestGridModel();
|
var testGridModel = new TestGridModel();
|
||||||
|
|
@ -231,19 +233,17 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
[RequestFormLimits(MultipartBodyLengthLimit = 10485760)]
|
[RequestFormLimits(MultipartBodyLengthLimit = 10485760)]
|
||||||
public async Task<IActionResult> UploadFile(List<IFormFile> files, int shippingDocumentId, int? partnerId)
|
public async Task<IActionResult> UploadFile(List<IFormFile> files, int shippingDocumentId, int? partnerId)
|
||||||
{
|
{
|
||||||
|
|
||||||
//an empty shippingdocument created by the user
|
//an empty shippingdocument created by the user
|
||||||
var shippingDocument = await _dbContext.ShippingDocuments.GetByIdAsync(shippingDocumentId);
|
var shippingDocument = await _dbContext.ShippingDocuments.GetByIdAsync(shippingDocumentId);
|
||||||
|
|
||||||
|
var shippingDocumentAnalysisResult = new ShippingDocumentAnalysisResult
|
||||||
|
{
|
||||||
|
Partner = new Partner(),
|
||||||
|
ShippingDocument = shippingDocument,
|
||||||
|
ShippingItems = []
|
||||||
|
};
|
||||||
|
|
||||||
|
shippingDocumentAnalysisResult.ShippingDocument.ShippingDocumentToFiles = [];
|
||||||
ShippingDocumentAnalysisResult shippingDocumentAnalysisResult = new ShippingDocumentAnalysisResult();
|
|
||||||
shippingDocumentAnalysisResult.Partner = new Partner();
|
|
||||||
shippingDocumentAnalysisResult.ShippingDocument = shippingDocument;
|
|
||||||
shippingDocumentAnalysisResult.ShippingItems = new List<ShippingItem>();
|
|
||||||
shippingDocumentAnalysisResult.ShippingDocument.ShippingDocumentToFiles = new List<ShippingDocumentToFiles>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//checks
|
//checks
|
||||||
// - files exist
|
// - files exist
|
||||||
|
|
@ -265,55 +265,51 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
|
|
||||||
var filesList = new List<Files>();
|
var filesList = new List<Files>();
|
||||||
|
|
||||||
|
|
||||||
//iteratation 1: iterate documents to determine their type by AI
|
//iteratation 1: iterate documents to determine their type by AI
|
||||||
|
|
||||||
|
var responseRawPdfTexts = string.Empty;
|
||||||
for (int i = 0; i < files.Count; i++)
|
foreach (var currentFile in files)
|
||||||
{
|
{
|
||||||
|
var fileName = currentFile.FileName;
|
||||||
var fileName = files[i].FileName;
|
var fileSize = currentFile.Length;
|
||||||
var fileSize = files[i].Length;
|
|
||||||
var dbFile = new Files();
|
var dbFile = new Files();
|
||||||
string pdfText = "";
|
var pdfText = "";
|
||||||
|
|
||||||
_logger.Detail($"Received file: {fileName} for Document ID: {shippingDocumentId}, content type: {files[i].ContentType}");
|
_logger.Detail($"Received file: {fileName} for Document ID: {shippingDocumentId}, content type: {currentFile.ContentType}");
|
||||||
|
|
||||||
if (!files[i].ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) && !files[i].ContentType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase))
|
if (!currentFile.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) && !currentFile.ContentType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase))
|
||||||
return Json(new { success = false, errorMessage = "Only PDF or jpg files are allowed" });
|
return Json(new { success = false, errorMessage = "Only PDF or jpg files are allowed" });
|
||||||
|
|
||||||
// Validate file size (max 20MB)
|
// Validate file size (max 20MB)
|
||||||
if (files[i].Length > 20 * 1024 * 1024)
|
if (currentFile.Length > 20 * 1024 * 1024)
|
||||||
return Json(new { success = false, errorMessage = "File size must be less than 10MB" });
|
return Json(new { success = false, errorMessage = "File size must be less than 10MB" });
|
||||||
|
|
||||||
// - get text extracted from pdf
|
// - get text extracted from pdf
|
||||||
// Validate file type (PDF only)
|
// Validate file type (PDF only)
|
||||||
//if (file.Length > 0 && file.ContentType == "application/pdf")
|
//if (file.Length > 0 && file.ContentType == "application/pdf")
|
||||||
if (files[i].Length > 0)
|
|
||||||
|
if (currentFile.Length > 0)
|
||||||
{
|
{
|
||||||
if (files[i].ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
|
if (currentFile.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Open the PDF from the IFormFile's stream directly in memory
|
// Open the PDF from the IFormFile's stream directly in memory
|
||||||
using (var stream = files[i].OpenReadStream())
|
await using var stream = currentFile.OpenReadStream();
|
||||||
using (var pdf = UglyToad.PdfPig.PdfDocument.Open(stream))
|
using var pdf = UglyToad.PdfPig.PdfDocument.Open(stream);
|
||||||
{
|
|
||||||
// Now you can analyze the PDF content
|
// Now you can analyze the PDF content
|
||||||
|
|
||||||
foreach (var page in pdf.GetPages())
|
foreach (var page in pdf.GetPages())
|
||||||
{
|
|
||||||
// Extract text from each page
|
|
||||||
pdfText += ContentOrderTextExtractor.GetText(page);
|
pdfText += ContentOrderTextExtractor.GetText(page);
|
||||||
|
|
||||||
}
|
|
||||||
//still nothing? let's send it to AI
|
//still nothing? let's send it to AI
|
||||||
if (string.IsNullOrWhiteSpace(pdfText))
|
if (string.IsNullOrWhiteSpace(pdfText))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// ✅ Use the service we implemented earlier
|
// ✅ Use the service we implemented earlier
|
||||||
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, files[i].FileName, "Please extract all readable text from this PDF.");
|
//pdfText = await _openAIApiService.AnalyzePdfAsync(stream, currentFile.FileName, "Please extract all readable text from this PDF.");
|
||||||
|
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, currentFile.FileName, DefaultFullPrompt);
|
||||||
}
|
}
|
||||||
catch (Exception aiEx)
|
catch (Exception aiEx)
|
||||||
{
|
{
|
||||||
|
|
@ -323,14 +319,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
// For demonstration, let's just log the extracted text
|
// For demonstration, let's just log the extracted text
|
||||||
_logger.Detail($"Extracted text from {files[i].FileName}: {pdfText}");
|
_logger.Detail($"Extracted text from {currentFile.FileName}: {pdfText}");
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Handle potential exceptions during PDF processing
|
// Handle potential exceptions during PDF processing
|
||||||
Console.Error.WriteLine($"Error processing PDF file {files[i].FileName}: {ex.Message}");
|
Console.Error.WriteLine($"Error processing PDF file {currentFile.FileName}: {ex.Message}");
|
||||||
return StatusCode(500, $"Error processing PDF file: {ex.Message}");
|
return StatusCode(500, $"Error processing PDF file: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -339,13 +333,11 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Open the Image from the IFormFile's stream directly in memory
|
// Open the Image from the IFormFile's stream directly in memory
|
||||||
using (var stream = files[i].OpenReadStream())
|
await using var stream = currentFile.OpenReadStream();
|
||||||
{
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// ✅ Use the service we implemented earlier
|
// ✅ Use the service we implemented earlier
|
||||||
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, files[i].FileName, "Please extract all readable text from this image.");
|
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, currentFile.FileName, "Please extract all readable text from this image.");
|
||||||
}
|
}
|
||||||
catch (Exception aiEx)
|
catch (Exception aiEx)
|
||||||
{
|
{
|
||||||
|
|
@ -354,33 +346,66 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
// For demonstration, let's just log the extracted text
|
// For demonstration, let's just log the extracted text
|
||||||
_logger.Detail($"Extracted text from {files[i].FileName}: {pdfText}");
|
_logger.Detail($"Extracted text from {currentFile.FileName}: {pdfText}");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Handle potential exceptions during PDF processing
|
// Handle potential exceptions during PDF processing
|
||||||
Console.Error.WriteLine($"Error processing PDF file {files[i].FileName}: {ex.Message}");
|
Console.Error.WriteLine($"Error processing PDF file {currentFile.FileName}: {ex.Message}");
|
||||||
return StatusCode(500, $"Error processing PDF file: {ex.Message}");
|
return StatusCode(500, $"Error processing PDF file: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//we should have some kind of text now
|
//we should have some kind of text now
|
||||||
_logger.Detail(pdfText);
|
_logger.Detail(pdfText);
|
||||||
}
|
}
|
||||||
|
|
||||||
string analysisPrompt = "Extract the document identification number from this document, determine the type of the " +
|
//ORIGINAL!!! - J.
|
||||||
"document IN ENGLISH from the available list, and return them as JSON: documentNumber, documentType. " +
|
shippingDocumentAnalysisResult = await ProcessRawText(shippingDocumentId, partnerId, pdfText, shippingDocumentAnalysisResult, filesList, dbFile);
|
||||||
$"Available filetypes: {nameof(DocumentType.Invoice)}, {nameof(DocumentType.ShippingDocument)} , {nameof(DocumentType.OrderConfirmation)}, {nameof(DocumentType.Unknown)}" +
|
await SaveDocumentToFileSystem(currentFile, shippingDocumentId, $"{Guid.NewGuid()}_{currentFile.FileName}");//shippingDocumentAnalysisResult.Partner.Name);
|
||||||
"If you can't find information of any of these, return null value for that field.";
|
|
||||||
|
|
||||||
//here I can start preparing the file entity
|
//Ez rész a catch végéig nekem teszt! - J
|
||||||
var metaAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), analysisPrompt);
|
//try
|
||||||
|
//{
|
||||||
|
// responseRawPdfTexts += $"{AcEnv.NL}{AcEnv.NL}{currentFile.Name}:{AcEnv.NL}{pdfText}";
|
||||||
|
|
||||||
var extractedMetaData = ParseMetaDataAIResponse(metaAnalyzis);
|
// var transactionSuccess = await SaveFileInfoToDb(shippingDocumentId, shippingDocumentAnalysisResult, filesList, dbFile, pdfText);
|
||||||
bool transactionSuccess = await SaveFileInfoToDB(shippingDocumentId, shippingDocumentAnalysisResult, filesList, dbFile, pdfText, extractedMetaData);
|
// if (!transactionSuccess)
|
||||||
|
// {
|
||||||
|
// _logger.Error($"(transactionSuccess == false)");
|
||||||
|
// return BadRequest($"Error saving file! RawText:{AcEnv.NL}{pdfText}");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (await SaveDocumentToFileSystem(currentFile, shippingDocumentId, $"{Guid.NewGuid()}_{currentFile.FileName}"))
|
||||||
|
// {
|
||||||
|
// //var partnerResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
||||||
|
// // pdfText,
|
||||||
|
// // $"{DefaultFullPrompt}"
|
||||||
|
// //);
|
||||||
|
|
||||||
|
// //responseRawPdfTexts += $"{AcEnv.NL}{AcEnv.NL}{AcEnv.NL}Response json:{AcEnv.NL}{partnerResponse}";
|
||||||
|
// }
|
||||||
|
// //return Ok(shippingDocumentAnalysisResult);
|
||||||
|
//}
|
||||||
|
//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.");
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
//iteration 2: iterate documents again
|
||||||
|
// - determine the items listed in the documents by AI
|
||||||
|
// try to fill all shippingitems information from the iteration
|
||||||
|
|
||||||
|
|
||||||
|
return Ok($"Files for Shipping Document ID {shippingDocumentId} were uploaded successfully!{AcEnv.NL}{AcEnv.NL}{responseRawPdfTexts}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ShippingDocumentAnalysisResult> ProcessRawText(int shippingDocumentId, int? partnerId, string pdfText, ShippingDocumentAnalysisResult shippingDocumentAnalysisResult, List<Files> filesList, Files dbFile)
|
||||||
|
{
|
||||||
|
var transactionSuccess = await SaveFileInfoToDb(shippingDocumentId, shippingDocumentAnalysisResult, filesList, dbFile, pdfText);
|
||||||
if (!transactionSuccess) _logger.Error($"(transactionSuccess == false)");
|
if (!transactionSuccess) _logger.Error($"(transactionSuccess == false)");
|
||||||
|
|
||||||
// - IF WE DON'T HAVE PARTNERID ALREADY: read partner information
|
// - IF WE DON'T HAVE PARTNERID ALREADY: read partner information
|
||||||
|
|
@ -388,19 +413,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
// save partner information to partners table { Id, Name, TaxId, CertificationNumber, PostalCode, Country, State, County, City, Street }
|
// save partner information to partners table { Id, Name, TaxId, CertificationNumber, PostalCode, Country, State, County, City, Street }
|
||||||
if (partnerId == null)
|
if (partnerId == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
//find partner in DB... there is a serious chance that the partner Name and Taxid determines the partner
|
//find partner in DB... there is a serious chance that the partner Name and Taxid determines the partner
|
||||||
|
|
||||||
var partners = await _dbContext.Partners.GetAll().ToListAsync();
|
var partners = await _dbContext.Partners.GetAll().ToListAsync();
|
||||||
foreach (var dbpartner in partners)
|
foreach (var dbpartner in partners.Where(dbpartner => pdfText.Contains(dbpartner.Name) || (dbpartner.TaxId != null && pdfText.Contains(dbpartner.TaxId))))
|
||||||
{
|
|
||||||
if (pdfText.Contains(dbpartner.Name) || (dbpartner.TaxId != null && pdfText.Contains(dbpartner.TaxId)))
|
|
||||||
{
|
{
|
||||||
partnerId = dbpartner.Id;
|
partnerId = dbpartner.Id;
|
||||||
_logger.Detail($"Found existing partner in DB: {dbpartner.Name} (ID: {dbpartner.Id})");
|
_logger.Detail($"Found existing partner in DB: {dbpartner.Name} (ID: {dbpartner.Id})");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (partnerId == null)
|
if (partnerId == null)
|
||||||
{
|
{
|
||||||
|
|
@ -465,20 +486,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
shippingDocumentAnalysisResult.Partner = shippingDocumentAnalysisResult.Partner;
|
shippingDocumentAnalysisResult.Partner = shippingDocumentAnalysisResult.Partner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//shortcut
|
//shortcut
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// Step 2: Extract Shipping Document Information
|
// 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:
|
var shippingDocPrompt = @"Extract the shipping document information from the following text and return ONLY a valid JSON object with these exact fields:
|
||||||
{
|
{
|
||||||
|
|
@ -540,41 +553,17 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
ShippingItems = items
|
ShippingItems = items
|
||||||
};
|
};
|
||||||
|
|
||||||
|
result.ShippingDocument.ShippingDocumentToFiles = shippingDocumentAnalysisResult.ShippingDocument.ShippingDocumentToFiles;
|
||||||
|
|
||||||
|
return result;
|
||||||
//return Ok(result);
|
//return Ok(result);
|
||||||
}
|
}
|
||||||
catch (JsonException ex)
|
catch (JsonException ex)
|
||||||
{
|
{
|
||||||
return BadRequest($"Failed to parse AI response: {ex.Message}");
|
_logger.Error($"ProcessRawText ERROR; {ex.Message}", ex);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// - 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()}_{files[i].FileName}";
|
|
||||||
var filePath = Path.Combine(uploadsPath, fileName);
|
|
||||||
|
|
||||||
// Save file
|
|
||||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
|
||||||
{
|
|
||||||
await files[i].CopyToAsync(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(shippingDocumentAnalysisResult);
|
|
||||||
}
|
|
||||||
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 null;
|
||||||
|
|
||||||
// - create a list of documents to read information and to save the document's information to DB
|
// - create a list of documents to read information and to save the document's information to DB
|
||||||
// - INSIDE ITERATION:
|
// - INSIDE ITERATION:
|
||||||
|
|
@ -650,20 +639,62 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
// //return Json(new { success = false, errorMessage = ex.Message });
|
// //return Json(new { success = false, errorMessage = ex.Message });
|
||||||
// return BadRequest("No files were uploaded.");
|
// return BadRequest("No files were uploaded.");
|
||||||
//}
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the documents to file system - wwwroot/uploads/orders/order-{orderId}/fileId-documentId.pdf where documentId is the number or id IN the document
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file"></param>
|
||||||
|
/// <param name="shippingDocumentId"></param>
|
||||||
|
/// <param name="fileName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<bool> SaveDocumentToFileSystem(IFormFile file, int shippingDocumentId, string fileName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create upload directory if it doesn't exist
|
||||||
|
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "orders", "order" + shippingDocumentId, "documents");
|
||||||
|
Directory.CreateDirectory(uploadsPath);
|
||||||
|
|
||||||
|
// Generate unique filename
|
||||||
|
//var fileName = $"{Guid.NewGuid()}_{file.FileName}";
|
||||||
|
var filePath = Path.Combine(uploadsPath, fileName);
|
||||||
|
|
||||||
//iteration 2: iterate documents again
|
// Save file
|
||||||
// - determine the items listed in the documents by AI
|
await using var stream = new FileStream(filePath, FileMode.Create);
|
||||||
// try to fill all shippingitems information from the iteration
|
await file.CopyToAsync(stream);
|
||||||
|
|
||||||
|
return true;
|
||||||
return Ok($"Files for Shipping Document ID {shippingDocumentId} were uploaded successfully!");
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"Error saving file: {ex.Message}", ex);
|
||||||
|
//return Json(new { success = false, errorMessage = ex.Message });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> SaveFileInfoToDB(int shippingDocumentId, ShippingDocumentAnalysisResult shippingDocumentAnalysisResult, List<Files> filesList, Files dbFile, string pdfText, ExtractedDocumentMetaData extractedMetaData)
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ExtractedDocumentMetaData> GetDocumentTypeFromRawText(string pdfText)
|
||||||
|
{
|
||||||
|
const 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)}" +
|
||||||
|
"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 metaAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText, analysisPrompt);
|
||||||
|
|
||||||
|
return ParseMetaDataAIResponse(metaAnalyzis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> SaveFileInfoToDb(int shippingDocumentId, ShippingDocumentAnalysisResult shippingDocumentAnalysisResult, List<Files> filesList, Files dbFile, string pdfText)
|
||||||
|
{
|
||||||
|
var extractedMetaData = await GetDocumentTypeFromRawText(pdfText);
|
||||||
|
return await SaveFileInfoToDb(shippingDocumentId, shippingDocumentAnalysisResult, filesList, dbFile, pdfText, extractedMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> SaveFileInfoToDb(int shippingDocumentId, ShippingDocumentAnalysisResult shippingDocumentAnalysisResult, List<Files> filesList, Files dbFile, string pdfText, ExtractedDocumentMetaData extractedMetaData)
|
||||||
{
|
{
|
||||||
if (extractedMetaData.DocumentNumber != null)
|
if (extractedMetaData.DocumentNumber != null)
|
||||||
{
|
{
|
||||||
|
|
@ -672,7 +703,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
dbFile.FileName = extractedMetaData.DocumentNumber;
|
dbFile.FileName = extractedMetaData.DocumentNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
var transactionSuccess = await _dbContext.TransactionSafeAsync(async _ =>
|
return await _dbContext.TransactionSafeAsync(async _ =>
|
||||||
{
|
{
|
||||||
await _dbContext.Files.InsertAsync(dbFile);
|
await _dbContext.Files.InsertAsync(dbFile);
|
||||||
filesList.Add(dbFile);
|
filesList.Add(dbFile);
|
||||||
|
|
@ -689,7 +720,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
await _dbContext.ShippingDocumentToFiles.InsertAsync(shippingDocumentToFiles);
|
await _dbContext.ShippingDocumentToFiles.InsertAsync(shippingDocumentToFiles);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
return transactionSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -746,7 +776,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
public string RawText { get; set; }
|
public string RawText { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ShippingDocumentAnalysisResult
|
public class ShippingDocumentAnalysisResult
|
||||||
{
|
{
|
||||||
public Partner Partner { get; set; }
|
public Partner Partner { get; set; }
|
||||||
|
|
@ -762,15 +791,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
public int TotalPallets { get; set; }
|
public int TotalPallets { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ExtractedDocumentData ParseShippingDocumentAIResponse(string aiResponse)
|
||||||
private ExtractedDocumentData ParseShippingDocumentAIResponse(string aiResponse)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to parse as JSON first
|
// Try to parse as JSON first
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize<ExtractedDocumentData>(
|
var data = JsonSerializer.Deserialize<ExtractedDocumentData>(
|
||||||
aiResponse,
|
aiResponse,
|
||||||
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
||||||
);
|
);
|
||||||
return data ?? new ExtractedDocumentData();
|
return data ?? new ExtractedDocumentData();
|
||||||
}
|
}
|
||||||
|
|
@ -784,15 +812,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractedDocumentMetaData ParseMetaDataAIResponse(string aiResponse)
|
private static ExtractedDocumentMetaData ParseMetaDataAIResponse(string aiResponse)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to parse as JSON first
|
// Try to parse as JSON first
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize<ExtractedDocumentMetaData>(
|
var data = JsonSerializer.Deserialize<ExtractedDocumentMetaData>(aiResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||||
aiResponse,
|
|
||||||
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
|
||||||
);
|
|
||||||
return data ?? new ExtractedDocumentMetaData();
|
return data ?? new ExtractedDocumentMetaData();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|
@ -806,14 +831,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractedPartnerData ParsePartnerDataAIResponse(string aiResponse)
|
private static ExtractedPartnerData ParsePartnerDataAIResponse(string aiResponse)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to parse as JSON first
|
// Try to parse as JSON first
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize<ExtractedPartnerData>(
|
var data = JsonSerializer.Deserialize<ExtractedPartnerData>(
|
||||||
aiResponse,
|
aiResponse,
|
||||||
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
||||||
);
|
);
|
||||||
return data ?? new ExtractedPartnerData();
|
return data ?? new ExtractedPartnerData();
|
||||||
}
|
}
|
||||||
|
|
@ -887,13 +912,11 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
|
|
||||||
var fullPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", filePath.TrimStart('/'));
|
var fullPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", filePath.TrimStart('/'));
|
||||||
|
|
||||||
if (System.IO.File.Exists(fullPath))
|
if (!System.IO.File.Exists(fullPath)) return Json(new { success = false, message = "File not found" });
|
||||||
{
|
|
||||||
System.IO.File.Delete(fullPath);
|
System.IO.File.Delete(fullPath);
|
||||||
return Json(new { success = true });
|
return Json(new { success = true });
|
||||||
}
|
|
||||||
|
|
||||||
return Json(new { success = false, message = "File not found" });
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -901,6 +924,98 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GetJson()
|
||||||
|
{
|
||||||
|
var partner = new Partner();
|
||||||
|
var jsonResolver = new IgnoreAndRenamePropertySerializerContractResolver();
|
||||||
|
|
||||||
|
jsonResolver.IgnoreProperty(typeof(Partner), nameof(partner.Name));
|
||||||
|
jsonResolver.IgnoreProperty(typeof(Partner), "Title");
|
||||||
|
jsonResolver.IgnoreProperty(typeof(Partner), "Title");
|
||||||
|
|
||||||
|
jsonResolver.IncludesProperty(typeof(Partner), "Title");
|
||||||
|
|
||||||
|
jsonResolver.RenameProperty(typeof(Partner), "FirstName", "firstName");
|
||||||
|
|
||||||
|
var serializerSettings = new Newtonsoft.Json.JsonSerializerSettings();
|
||||||
|
serializerSettings.ContractResolver = jsonResolver;
|
||||||
|
|
||||||
|
var json = Newtonsoft.Json.JsonConvert.SerializeObject(partner, serializerSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string DefaultFullPrompt = $"Role:\r\nYou are an AI data extraction assistant for Fruitbank, a " +
|
||||||
|
$"fruit and vegetable wholesale company. Your task is to analyze a list of extracted pdf " +
|
||||||
|
$"documents (delivery notes, invoices, or order confirmations) and extract structured information about " +
|
||||||
|
$"the shipment and its items.\r\n\r\n🎯 Goal:\r\nRead the provided extracted pdf text and extract all shipment " +
|
||||||
|
$"details and items according to the data model below.\r\n Generate the complete JSON output following this " +
|
||||||
|
$"structure.\r\n\r\n🧩 Data Models:\r\n\r\npublic " +
|
||||||
|
$"class Partner\r\n{{\r\n " +
|
||||||
|
$"/// <summary>\r\n /// Partner entity primary key\r\n /// </summary>\r\n " +
|
||||||
|
$"public int Id {{ get; set; }}\r\n " +
|
||||||
|
$"/// <summary>\r\n /// Partner company name\r\n /// </summary>\r\n " +
|
||||||
|
$"public string Name {{ get; set; }}\r\n " +
|
||||||
|
$"/// <summary>\r\n /// Partner company TaxId\r\n /// </summary>\r\n " +
|
||||||
|
$"public string TaxId {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Partner company Certification if exists\r\n /// </summary>\r\n " +
|
||||||
|
$"public string CertificationNumber {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Partner company address PostalCode\r\n /// </summary>\r\n " +
|
||||||
|
$"public string PostalCode {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Partner company address Country\r\n /// </summary>\r\n " +
|
||||||
|
$"public string Country {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Partner company address State if exists\r\n /// </summary>\r\n " +
|
||||||
|
$"public string State {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Partner company address County if exists\r\n /// </summary>\r\n " +
|
||||||
|
$"public string County {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Partner company address City\r\n /// </summary>\r\n " +
|
||||||
|
$"public string City {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Partner company address Street\r\n /// </summary>\r\n " +
|
||||||
|
$"public string Street {{ get; set; }}\r\n\t/// <summary>\r\n " +
|
||||||
|
$"/// Entities of ShippingDocument\r\n /// </summary>\r\n\tpublic List<ShippingDocument> " +
|
||||||
|
$"ShippingDocuments {{ get; set; }}\t\r\n}}\r\n\r\npublic class ShippingDocument\r\n{{\r\n " +
|
||||||
|
$"/// <summary>\r\n /// ShippingItem entity primary key\r\n /// </summary>\r\n " +
|
||||||
|
$"public int Id {{ get; set; }}\r\n /// <summary>\r\n /// Partner entity primary key\r\n " +
|
||||||
|
$"/// </summary>\r\n public int PartnerId {{ get; set; }}\t\r\n\t/// <summary>\r\n " +
|
||||||
|
$"/// Entities of ShippingItem\r\n /// </summary>\r\n\t" +
|
||||||
|
$"public List<ShippingItem> ShippingItems {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// DocumentIdNumber if exists\r\n /// </summary>\r\n public string DocumentIdNumber {{ get; set; }}\r\n " +
|
||||||
|
$"/// <summary>\r\n /// \r\n /// </summary>\r\n public DateTime ShippingDate {{ get; set; }}\r\n " +
|
||||||
|
$"/// <summary>\r\n /// Shipping pickup Contry of origin\r\n /// </summary>\r\n " +
|
||||||
|
$"public string Country {{ get; set; }}\r\n\t/// <summary>\r\n /// Sum of ShippingItem pallets\r\n " +
|
||||||
|
$"/// </summary>\r\n public int TotalPallets {{ get; set; }}\r\n\t/// <summary>\r\n " +
|
||||||
|
$"/// Filename of pdf\r\n /// </summary>\r\n\tpublic string PdfFileName {{ get; set; }}\r\n}}\r\n\r\n" +
|
||||||
|
$"public class ShippingItem\r\n{{\r\n /// <summary>\r\n /// ShippingItem entity primary key\r\n /// " +
|
||||||
|
$"</summary>\r\n public int Id {{ get; set; }}\r\n /// <summary>\r\n /// " +
|
||||||
|
$"ShippingDocument entity primary key\r\n /// </summary>\r\n " +
|
||||||
|
$"public int ShippingDocumentId {{ get; set; }}\r\n /// " +
|
||||||
|
$"<summary>\r\n /// Name of the fruit or vegitable\r\n /// </summary>\r\n " +
|
||||||
|
$"public string Name {{ get; set; }}\r\n\t/// <summary>\r\n /// Translated Name to Hungarian\r\n " +
|
||||||
|
$"/// </summary>\r\n public string HungarianName {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Pallets of fruit or vegitable item\r\n /// </summary>\r\n " +
|
||||||
|
$"public int PalletsOnDocument {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Quantity of fruit or vegitable item\r\n /// </summary>\r\n " +
|
||||||
|
$"public int QuantityOnDocument {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Net weight in kg. of fruit or vegitable item\r\n /// </summary>\r\n " +
|
||||||
|
$"public double NetWeightOnDocument {{ get; set; }}\r\n /// <summary>\r\n " +
|
||||||
|
$"/// Gross weight in kg. of fruit or vegitable item\r\n /// </summary>\r\n " +
|
||||||
|
$"public double GrossWeightOnDocument {{ get; set; }}\r\n}}\r\n\r\n🧾 Output Requirements\r\n- " +
|
||||||
|
$"Output must be a single valid JSON object containing:\r\n- One Partner object\r\n- " +
|
||||||
|
$"One ShippingDocument object\r\n- A list of all related ShippingItem objects\r\n\r\n- " +
|
||||||
|
$"Primary keys (Partner.Id, ShippingDocument.Id, ShippingItem.Id) should be auto-generated integers " +
|
||||||
|
$"(e.g. sequential: 1, 2, 3…).\r\n\r\n- When a field is missing or unclear, return it as an empty " +
|
||||||
|
$"string or 0 (depending on type).\r\nDo not omit any fields.\r\n\r\n- " +
|
||||||
|
$"All dates must be in ISO 8601 format (yyyy-MM-dd).\r\n\r\n🧭 Instructions to the AI\r\n" +
|
||||||
|
$"1. Analyze the provided PDF files one by one carefully.\r\n" +
|
||||||
|
$"2. Identify the Partner/Company details, " +
|
||||||
|
$"document identifiers, and each shipment item.\r\n" +
|
||||||
|
$"3. FruitBank is not a partner! Always look for THE OTHER partner on the document. \r\n " +
|
||||||
|
$"4. Generate a complete hierarchical JSON of ALL received documents in ONE JSON structure according to the " +
|
||||||
|
$"data model above.\r\n5. Do not include any explanations or text outside the JSON output. " +
|
||||||
|
$"Only return the structured JSON.\r\n" +
|
||||||
|
$"6. A teljes ShippingItem.Name-et tedd bele a ShippingItem.HungarianName-be " +
|
||||||
|
$"és a zöldség vagy gyümölcs nevét fordítsd le magyarra!\r\n" +
|
||||||
|
$"7. A ShippingDocument-et tedd bele a Partner entitásba!\r\n" +
|
||||||
|
$"8. ShippingItem-eket tedd bele a ShippingDocument-be!\r\n" +
|
||||||
|
$"9. Do not assume or modify any data, if you don't find a value, return null, if you find a value, keep it unmodified.";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UploadModel
|
public class UploadModel
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue