using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Repositories; using OI.Metrology.Shared.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace OI.Metrology.Archive.Controllers; public class ExportController : Controller { private ILogger Logger { get; } protected IMetrologyRepo _Repo; public ExportController(ILogger logger, IMetrologyRepo repo) { Logger = logger; _Repo = repo; } protected static void LoadToolTypes(Export m, IEnumerable toolTypes) { m.ToolTypes = toolTypes .Where(tt => tt.ExportSPName != null) .Select(tt => new Shared.Models.SelectListItem(tt.ToolTypeName, tt.ID.ToString())).ToList(); } [HttpGet] [Route("/Export")] public ActionResult Index() { Export model = new(); LoadToolTypes(model, _Repo.GetToolTypes()); model.ToolType = ""; model.StartTime = DateTime.Now.AddMonths(-1); model.EndTime = DateTime.Now; return View(model); } [HttpPost] [Route("/ExportData")] public ActionResult ExportData(Export model) { IEnumerable toolTypes = _Repo.GetToolTypes(); // is tooltype valid ToolType toolType = toolTypes.Where(tt => tt.ID.ToString() == model.ToolType).SingleOrDefault(); if (toolType == null) ModelState.AddModelError("ToolType", "Invalid selection"); else if (string.IsNullOrWhiteSpace(toolType.ExportSPName)) ModelState.AddModelError("ToolType", "Tool type is not exportable"); // is enddate after startdate if (model.StartTime > model.EndTime) ModelState.AddModelError("EndTime", "End time must be after start time"); if (ModelState.IsValid) { try { DateTime startDT = model.StartDate.Date.Add(model.StartTime.TimeOfDay); DateTime endDT = model.EndDate.Date.Add(model.EndTime.TimeOfDay); return DoCSVExport(toolType.ToolTypeName, toolType.ExportSPName, startDT, endDT); } catch (Exception ex) { ModelState.AddModelError("", "Error exporting data"); ModelState.AddModelError("", ex.Message); string errorMessage = $"Error exporting: {ex}"; Logger.LogError(message: errorMessage); } } LoadToolTypes(model, toolTypes); return View("Index", model); } protected ActionResult DoCSVExport(String toolTypeName, string spName, DateTime startTime, DateTime endTime) { string fileName = string.Format("Export_{0}_{1:yyyyMMddHHmm}_to_{2:yyyyMMddHHmm}.csv", toolTypeName, startTime, endTime); StringBuilder sb = new(); System.Data.DataTable dt = _Repo.ExportData(spName, startTime, endTime); _ = sb.AppendLine(GetColumnHeaders(dt)); foreach (System.Data.DataRow dr in dt.Rows) { _ = sb.AppendLine(GetRowData(dr)); } byte[] contents = Encoding.UTF8.GetBytes(sb.ToString()); return File(contents, "application/octet-stream", fileName); } protected static string GetRowData(System.Data.DataRow dr) { StringBuilder r = new(); for (int i = 0; i < dr.Table.Columns.Count; i++) { if (i > 0) _ = r.Append(','); object v = dr[i]; if (!Convert.IsDBNull(v)) _ = r.Append(FormatForCSV(Convert.ToString(v))); } return r.ToString(); } protected static string GetColumnHeaders(System.Data.DataTable dt) { StringBuilder r = new(); for (int i = 0; i < dt.Columns.Count; i++) { if (i > 0) _ = r.Append(','); _ = r.Append(FormatForCSV(dt.Columns[i].ColumnName.TrimEnd('_'))); } return r.ToString(); } protected static string FormatForCSV(string v) { StringBuilder r = new(v.Length + 2); bool doubleQuoted = false; if (v.StartsWith(' ') || v.EndsWith(' ') || v.Contains(',') || v.Contains('"')) { _ = r.Append('"'); doubleQuoted = true; } foreach (char c in v) { _ = c switch { '\r' or '\n' => r.Append(' '), '"' => r.Append("\"\""), _ => r.Append(c), }; } if (doubleQuoted) _ = r.Append('"'); return r.ToString(); } }