Compare commits

..

1 Commits

Author SHA1 Message Date
9bd5774e08 fix data display 2025-05-29 09:23:13 +08:00
3 changed files with 166 additions and 125 deletions

View File

@ -11,6 +11,7 @@ using System.Collections.Generic;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using MySqlConnector; using MySqlConnector;
using Org.BouncyCastle.Asn1.Cms;
namespace PSTW_CentralSystem.Areas.MMS.Controllers namespace PSTW_CentralSystem.Areas.MMS.Controllers
{ {
@ -21,8 +22,8 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
public required string StationID { get; set; } public required string StationID { get; set; }
public required string Longitude { get; set; } public required string Longitude { get; set; }
public required string Latitude { get; set; } public required string Latitude { get; set; }
public required DateTime DateSample { get; set; } public DateTime DateSample { get; set; }
public required TimeSpan TimeSample { get; set; } public TimeSpan TimeSample { get; set; }
public required string ClassifyID { get; set; } public required string ClassifyID { get; set; }
public string? OptionalName1 { get; set; } public string? OptionalName1 { get; set; }
public string? OptionalName2 { get; set; } public string? OptionalName2 { get; set; }
@ -37,6 +38,21 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
public required string LevelName { get; set; } // From tbl_level public required string LevelName { get; set; } // From tbl_level
} }
public class TarBallGroupViewModel
{
public List<int> Id { get; set; } = new List<int>();
public required string StationID { get; set; }
public required string Date { get; set; }
public required int TimeSampleCount { get; set; }
public required string LatestTime {get; set; }
public List<TarBallTimeSample> TimeSamples { get; set; } = new List<TarBallTimeSample>();
}
public class TarBallTimeSample
{
public required int Id { get; set; }
public string Time { get; set; }
}
[Area("MMS")] [Area("MMS")]
public class MarineController : Controller public class MarineController : Controller
{ {
@ -55,28 +71,37 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
return View(); return View();
} }
public IActionResult TarBallForm()//Queries the database and returns a view with tarball data public class StationDateGroup
{
public required string StationID { get; set; }
public DateTime DateSample { get; set; }
public List<TarballPdfDto> TimeSamples { get; set; } = new List<TarballPdfDto>();
}
public IActionResult TarBallForm()
{ {
try try
{ {
var marineTarballs = _context.MarineTarballs //ERRORRRRRRR====================================== var marineTarballs = _context.MarineTarballs
.Select(t => new .Select(t => new
{ {
t.Id, t.Id,
Date = t.DateSample.ToString("yyyy/MM/dd"), station = t.StationID,
Station = t.StationID date = t.DateSample.ToString("yyyy/MM/dd"),
time = t.TimeSample.ToString(@"hh\:mm\:ss"),
fullDate = t.DateSample, // Keep DateTime for sorting
t.TimeSample
}) })
.AsEnumerable()
.OrderByDescending(t => t.fullDate)
.ThenByDescending(t => t.TimeSample)
.ToList(); .ToList();
Console.WriteLine($"Marine Tarballs Count: {marineTarballs.Count}");
return View(marineTarballs); return View(marineTarballs);
} }
catch (Exception ex) catch (Exception ex)
{ {
// Show the real error in the browser (for debugging only)
return Content($"Error: {ex.Message}<br/>{ex.StackTrace}", "text/html"); return Content($"Error: {ex.Message}<br/>{ex.StackTrace}", "text/html");
} }
} }
[HttpGet] // Explicitly mark as a GET endpoint [HttpGet] // Explicitly mark as a GET endpoint
@ -176,8 +201,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
// Determine content type based on extension // Determine content type based on extension
string contentType = "image/jpeg"; // default string contentType = "image/jpeg"; // default
string extension = Path.GetExtension(sanitizedFileName).ToLower(); string extension = Path.GetExtension(sanitizedFileName)?.ToLower();
//string extension = Path.GetExtension(sanitizedFileName)?.ToLower();
if (extension == ".png") if (extension == ".png")
{ {
@ -223,7 +247,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
} }
} }
public async Task<IActionResult> GenerateReport(int id)//calls GeneratePdfResponse to generate a PDF for inline viewing public async Task<IActionResult> GenerateReport(int id)
{ {
return await GeneratePdfResponse(id, true); return await GeneratePdfResponse(id, true);
} }
@ -240,7 +264,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
{ {
// Add timeout for safety // Add timeout for safety
var task = Task.Run(() => GeneratePdfResponse(id, false)); var task = Task.Run(() => GeneratePdfResponse(id, false));
if (task.Wait(TimeSpan.FromSeconds(30))) // 30 second timeout if (task.Wait(TimeSpan.FromSeconds(30))) // 30-second timeout
{ {
return task.Result; return task.Result;
} }
@ -255,24 +279,15 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
private async Task<IActionResult> GeneratePdfResponse(int id, bool forceDownload) private async Task<IActionResult> GeneratePdfResponse(int id, bool forceDownload)
{ {
Console.WriteLine($"Requested ID in {(forceDownload ? "GenerateReport" : "ViewPDF")}: {id}");
// Test network connection first
try
{
_networkAccessService.ConnectToNetworkPath();
_networkAccessService.DisconnectFromNetworkShare();
}
catch (Exception ex)
{
return StatusCode(500, $"Cannot access network: {ex.Message}");
}
try try
{ {
_networkAccessService.ConnectToNetworkPath(); _networkAccessService.ConnectToNetworkPath();
// ===== 1. Get Data from Database ===== var record = await _context.MarineTarballs.FirstOrDefaultAsync(t => t.Id == id);
if (record == null)
return NotFound($"No record found for Id: {id}");
// Retrieve all entries for the same StationID and DateSample
var query = @" var query = @"
SELECT SELECT
marine.*, marine.*,
@ -285,116 +300,135 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
JOIN tbl_state state ON station.StateID = state.StateID JOIN tbl_state state ON station.StateID = state.StateID
JOIN tbl_user user ON marine.FirstSampler = user.FullName JOIN tbl_user user ON marine.FirstSampler = user.FullName
JOIN tbl_level level ON user.LevelID = level.LevelID JOIN tbl_level level ON user.LevelID = level.LevelID
WHERE marine.Id = @id"; WHERE marine.Id=@id";
var tarball = await _context.Database var tarballEntries = await _context.Database
.SqlQueryRaw<TarballPdfDto>(query, new MySqlParameter("@id", id)) .SqlQueryRaw<TarballPdfDto>(query, new MySqlParameter("@id", id))
.FirstOrDefaultAsync(); .ToListAsync();
if (tarball == null) if (!tarballEntries.Any())
return NotFound("Record not found"); return NotFound($"No records found for Id: {id}");
// Prepare boolean values for PDF var pdfList = new List<IActionResult>();
bool tarBallYes = tarball.ClassifyID != "NO";
bool tarBallNo = tarball.ClassifyID == "NO";
bool isSand = tarball.ClassifyID == "SD";
bool isNonSandy = tarball.ClassifyID == "NS";
bool isCoquina = tarball.ClassifyID == "CO";
// ===== 2. Get Images ===== // Iterate over each unique TimeSample within the same DateSample
// For date (stored as DATE in DB → "2025-01-30" becomes "20250130") foreach (var timeGroup in tarballEntries.GroupBy(t => t.TimeSample))
var sampleDateString = tarball.DateSample.ToString("yyyyMMdd");
// For time (stored as TIME in DB → "16:49:02" becomes "164902")
var sampleTimeString = tarball.TimeSample.ToString("hhmmss");
var stationFolder = Path.Combine(PhotoBasePath, tarball.StationID);
var stationImages = new Dictionary<string, string>();
var foundAnyImages = false;
if (Directory.Exists(stationFolder))
{ {
var basePattern = $"{tarball.StationID}_{sampleDateString}_{sampleTimeString}_"; var tarball = timeGroup.First(); // Use first entry per time sample
var allImages = Directory.GetFiles(stationFolder, $"{basePattern}*");
foreach (var imagePath in allImages) // Classification logic remains unchanged
bool tarBallYes = tarball.ClassifyID != "NO";
bool tarBallNo = tarball.ClassifyID == "NO";
bool isSand = tarball.ClassifyID == "SD";
bool isNonSandy = tarball.ClassifyID == "NS";
bool isCoquina = tarball.ClassifyID == "CO";
// Construct image paths ensuring correct naming format
var stationFolder = Path.Combine(PhotoBasePath, tarball.StationID);
var sampleDateString = tarball.DateSample.ToString("yyyyMMdd");
var sampleTimeString = tarball.TimeSample.ToString(@"hhmmss");
var stationImages = new Dictionary<string, string>();
if (Directory.Exists(stationFolder))
{ {
var fileName = Path.GetFileNameWithoutExtension(imagePath); var imageTypes = new Dictionary<string, string>
var type = fileName.Split('_').Last(); {
stationImages[type] = imagePath; { "LEFTSIDECOASTALVIEW", null },
foundAnyImages = true; { "RIGHTSIDECOASTALVIEW", null },
{ "DRAWINGVERTICALLINES", null },
{ "DRAWINGHORIZONTALLINES", null },
{ "OPTIONAL01", null },
{ "OPTIONAL02", null },
{ "OPTIONAL03", null },
{ "OPTIONAL04", null }
};
foreach (var imagePath in Directory.GetFiles(stationFolder))
{
var fileName = Path.GetFileNameWithoutExtension(imagePath);
foreach (var type in imageTypes.Keys.ToList())
{
if (fileName.StartsWith($"{tarball.StationID}_{sampleDateString}_{sampleTimeString}") &&
fileName.Contains(type, StringComparison.OrdinalIgnoreCase))
{
imageTypes[type] = imagePath;
break;
}
}
}
stationImages = imageTypes;
} }
}
if (!foundAnyImages) // Validate mandatory images exist
{ var mandatoryImages = new List<string>
return StatusCode(404, "No images found for this record");
}
// Verify mandatory images exist
var mandatoryImages = new[] {
"LEFTSIDECOASTALVIEW",
"RIGHTSIDECOASTALVIEW",
"DRAWINGVERTICALLINES",
"DRAWINGHORIZONTALLINES"
};
foreach (var type in mandatoryImages)
{
if (!stationImages.ContainsKey(type))
{ {
return StatusCode(400, $"Missing mandatory image: {type}"); "LEFTSIDECOASTALVIEW",
"RIGHTSIDECOASTALVIEW",
"DRAWINGVERTICALLINES",
"DRAWINGHORIZONTALLINES"
};
foreach (var mandatoryType in mandatoryImages)
{
if (!stationImages.ContainsKey(mandatoryType) || stationImages[mandatoryType] == null)
{
return StatusCode(400, $"Missing mandatory image: {mandatoryType} for {tarball.StationID} on {tarball.DateSample}");
}
} }
// Generate PDF using existing classification and sorting logic
var pdfDocument = new TarBallPDF(
tarball.StateName,
tarball.StationID,
tarball.LocationName,
tarball.Longitude,
tarball.Latitude,
tarball.DateSample,
tarball.TimeSample,
tarball.ClassifyID,
tarBallYes,
tarBallNo,
isSand,
isNonSandy,
isCoquina,
stationImages["LEFTSIDECOASTALVIEW"],
stationImages["RIGHTSIDECOASTALVIEW"],
stationImages["DRAWINGVERTICALLINES"],
stationImages["DRAWINGHORIZONTALLINES"],
stationImages.GetValueOrDefault("OPTIONAL01"),
stationImages.GetValueOrDefault("OPTIONAL02"),
stationImages.GetValueOrDefault("OPTIONAL03"),
stationImages.GetValueOrDefault("OPTIONAL04"),
tarball.OptionalName1,
tarball.OptionalName2,
tarball.OptionalName3,
tarball.OptionalName4,
tarball.FirstSampler,
tarball.FullName,
tarball.LevelName
).GeneratePdf();
string pdfFileName = $"{tarball.StationID}_{tarball.DateSample:yyyyMMdd}_{tarball.TimeSample:hhmmss}.pdf";
pdfList.Add(
forceDownload
? File(pdfDocument, "application/pdf", pdfFileName)
: File(pdfDocument, "application/pdf")
);
} }
// ===== 3. Generate PDF ===== return pdfList.Count == 1 ? pdfList.First() : Ok(pdfList);
var pdf = new TarBallPDF(
tarball.StateName,
tarball.StationID,
tarball.LocationName,
tarball.Longitude,
tarball.Latitude,
tarball.DateSample,
tarball.TimeSample,
tarball.ClassifyID,
tarBallYes,
tarBallNo,
isSand,
isNonSandy,
isCoquina,
stationImages["LEFTSIDECOASTALVIEW"],
stationImages["RIGHTSIDECOASTALVIEW"],
stationImages["DRAWINGVERTICALLINES"],
stationImages["DRAWINGHORIZONTALLINES"],
stationImages.GetValueOrDefault("OPTIONAL01"),
stationImages.GetValueOrDefault("OPTIONAL02"),
stationImages.GetValueOrDefault("OPTIONAL03"),
stationImages.GetValueOrDefault("OPTIONAL04"),
tarball.OptionalName1,
tarball.OptionalName2,
tarball.OptionalName3,
tarball.OptionalName4,
tarball.FirstSampler,
tarball.FullName,
tarball.LevelName
).GeneratePdf();
// ===== 4. Return PDF =====
var downloadName = $"{tarball.StationID}_{sampleDateString}_{sampleTimeString}.pdf";
return forceDownload
? File(pdf, "application/pdf", downloadName)
: File(pdf, "application/pdf");
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, $"PDF generation failed: {ex.Message}"); return StatusCode(500, $"Error: {ex.Message}");
} }
finally finally
{ {
_networkAccessService.DisconnectFromNetworkShare(); _networkAccessService.DisconnectFromNetworkShare();
} }
} }
private bool IsImageValid(string imagePath) private bool IsImageValid(string imagePath)
{ {
try try

View File

@ -11,8 +11,8 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
string longitude, string latitude, DateTime dateSample, TimeSpan timeSample, string longitude, string latitude, DateTime dateSample, TimeSpan timeSample,
string classifyID, bool tarBallYes, bool tarBallNo, bool isSand, bool isNonSandy, string classifyID, bool tarBallYes, bool tarBallNo, bool isSand, bool isNonSandy,
bool isCoquina, string photoPath1, string photoPath2, string photoPath3, string photoPath4, bool isCoquina, string photoPath1, string photoPath2, string photoPath3, string photoPath4,
string? photoPath5, string? photoPath6, string? photoPath7, string? photoPath8, string photoPath5, string photoPath6, string photoPath7, string photoPath8,
string? optionalName1, string? optionalName2, string? optionalName3, string? optionalName4, string optionalName1, string optionalName2, string optionalName3, string optionalName4,
string firstSampler, string fullName, string levelName string firstSampler, string fullName, string levelName
) )
: IDocument : IDocument
@ -360,7 +360,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
table.Cell().Element(CellStyle).Text("Signature").FontSize(12); table.Cell().Element(CellStyle).Text("Signature").FontSize(12);
table.Cell().Element(CellStyle).Text(""); table.Cell().Element(CellStyle).Text("");
table.Cell().Element(CellStyle).Text("Date").FontSize(12); table.Cell().Element(CellStyle).Text("Date").FontSize(12);
table.Cell().Element(CellStyle).Text($"{_dateSample:yyyyMMdd}").FontSize(12); table.Cell().Element(CellStyle).Text(_dateSample.ToString("yyyy/MM/dd")).FontSize(12);
table.Cell().Element(CellStyle).Text("Designation").FontSize(12); table.Cell().Element(CellStyle).Text("Designation").FontSize(12);
table.Cell().ColumnSpan(3).Element(CellStyle).Text(_levelName).FontSize(12); table.Cell().ColumnSpan(3).Element(CellStyle).Text(_levelName).FontSize(12);

View File

@ -102,15 +102,21 @@
return []; // Return an empty array if no data is available return []; // Return an empty array if no data is available
} }
// Extract all years from the data // Extract all years from the data (handling both FormattedDate and date)
const allYears = this.dataFromServer.map(data => new Date(data.date).getFullYear()); const allYears = this.dataFromServer.map(data => {
const dateStr = data.Date || data.date; // Handle both cases
return new Date(dateStr).getFullYear();
});
// Find the minimum and maximum years // Find the minimum and maximum years
const minYear = Math.min(...allYears); const minYear = Math.min(...allYears);
const maxYear = Math.max(...allYears); const maxYear = Math.max(...allYears);
// Generate a range of years from minYear to maxYear // Generate a range of years from minYear to maxYear
return Array.from({ length: maxYear - minYear + 1 }, (_, i) => (minYear + i).toString()); return Array.from(
{ length: maxYear - minYear + 1 },
(_, i) => (minYear + i).toString()
);
}, },
sortedFilteredData() { sortedFilteredData() {
// If no filters are applied, return all data sorted by descending date // If no filters are applied, return all data sorted by descending date
@ -154,12 +160,11 @@
"data": null, "data": null,
"render": (data, type, row, meta) => meta.row + 1 // Dynamically generate "No." "render": (data, type, row, meta) => meta.row + 1 // Dynamically generate "No."
}, },
{ "data": "date", "render": (data) => new Date(data).toLocaleDateString('en-GB') }, { "data": "date"},
{ "data": "station" }, { "data": "station" },
{ {
"data": null, "data": null,
"render": () => ` "render": (data) => `
<button class="btn btn-success">Approve</button> <button class="btn btn-success">Approve</button>
<button class="btn btn-danger">Reject</button> <button class="btn btn-danger">Reject</button>
` `
@ -167,8 +172,10 @@
{ {
"data": null, "data": null,
"render": (data) => ` "render": (data) => `
<a href="/MMS/Marine/ViewPDF?id=${data.id}" class="btn btn-primary" target="_blank">View PDF</a> <a href="/MMS/Marine/ViewPDF?stationId=${data.Id}"
<a href="/MMS/Marine/GenerateReport?id=${data.id}" class="btn btn-primary">Download PDF</a> class="btn btn-primary" target="_blank">View PDF</a>
<a href="/MMS/Marine/GenerateReport?stationId=${data.Id}"
class="btn btn-primary">Download PDF</a>
` `
} }
], ],