This commit is contained in:
Mike Phares 2023-01-23 18:48:43 -07:00
parent 56ec81e080
commit 662863a11e
15 changed files with 247 additions and 52 deletions

View File

@ -40,7 +40,6 @@ public class ExportController : Controller
{ {
Export model = new() Export model = new()
{ {
ToolType = "",
StartTime = DateTime.Now.AddMonths(-1), StartTime = DateTime.Now.AddMonths(-1),
EndTime = DateTime.Now EndTime = DateTime.Now
}; };

View File

@ -1,6 +1,8 @@
@page "/Export/{Model?}" @page "/Export"
@using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web
@using MudBlazor
@using OI.Metrology.Shared.DataModels
@namespace OI.Metrology.ClientHub.Pages @namespace OI.Metrology.ClientHub.Pages
@ -10,31 +12,31 @@
<hr /> <hr />
<form asp-controller="Export" asp-action="ExportData" method="post" class="form-inline" style="min-height:480px;"> @if (_TimeSpan is null || _DateRange is null || _ToolTypeNameId is null || _ToolTypeNameIdCollection is null)
<div class="form-group"> {
<label for="ToolType">Tool Type</label> <p><em>Loading...</em></p>
<div class="form-control" id="ToolType" name="ToolType"></div> }
@* @Microsoft.AspNetCore.Html.ValidationMessage("ToolType", new { @class = "text-danger" }) *@ else
</div> {
<div class="form-group"> <table id="ExportData">
<label for="StartDate">Start Time</label> <tr>
<div class="form-control mb-2 mr-sm-2" id="StartDateControl"></div> <td>
<div class="form-control mb-2 mr-sm-2" id="StartTimeControl"></div> <MudSelect @bind-Value="_ToolTypeNameId" ToStringFunc="@_ConvertFunc" Label="Tool Type" Placeholder="Please Select" AdornmentColor="Color.Primary">
@* @Microsoft.AspNetCore.Html.ValidationMessage("StartDate", new { @class = "text-danger" }) *@ @foreach (ToolTypeNameId toolTypeNameId in _ToolTypeNameIdCollection)
</div> {
<div class="form-group"> <MudSelectItem Value="@toolTypeNameId" />
<label for="EndDate">End Time</label> }
<div class="form-control mb-2 mr-sm-2" id="EndDateControl"></div> </MudSelect>
<div class="form-control mb-2 mr-sm-2" id="EndTimeControl"></div> </td>
@* @Microsoft.AspNetCore.Html.ValidationMessage("EndDate", new { @class = "text-danger" }) *@ <td>
</div> <MudDateRangePicker Label="Date Range" @bind-DateRange="_DateRange" />
<div class="form-group"> </td>
<button type="submit" class="btn btn-primary">Export Data</button> <td>
</div> <MudTimePicker Label="Start and End Time" AmPm="true" @bind-Time="_TimeSpan" />
</td>
<div class="form-group"> <td>
@* @Microsoft.AspNetCore.Html.ValidationMessage("Exception", new { @class = "text-danger" }) *@ <MudButton Variant="Variant.Filled" Color="Color.Info" OnClick="DownloadAsync">Download</MudButton>
</div> </td>
<div asp-validation-summary="ModelOnly" class="text-danger"></div> </tr>
</table>
</form> }

View File

@ -1,24 +1,26 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using MudBlazor;
using OI.Metrology.Shared.DataModels;
using OI.Metrology.Shared.Models.Stateless;
using System.Net;
namespace OI.Metrology.ClientHub.Pages; namespace OI.Metrology.ClientHub.Pages;
public partial class Export public partial class Export
{ {
protected TimeSpan? _TimeSpan;
protected DateRange? _DateRange;
protected ToolTypeNameId? _ToolTypeNameId;
protected ToolTypeNameId[]? _ToolTypeNameIdCollection;
[Inject] protected HttpClient? HttpClient { get; set; }
[Inject] protected IJSRuntime? JSRuntime { get; set; } [Inject] protected IJSRuntime? JSRuntime { get; set; }
[Inject] protected Models.AppSettings? AppSettings { get; set; } [Inject] protected Models.AppSettings? AppSettings { get; set; }
[Parameter] public Metrology.Shared.ViewModels.Export? Model { get; set; } [Inject] protected ILogger<AwaitingDisposition>? Logger { get; set; }
public Export() protected Func<ToolTypeNameId, string> _ConvertFunc = toolTypeNameId => string.Concat(toolTypeNameId?.ToolTypeName);
{
Model ??= new()
{
ToolType = "",
StartTime = DateTime.Now.AddMonths(-1),
EndTime = DateTime.Now
};
}
protected override Task OnAfterRenderAsync(bool firstRender) protected override Task OnAfterRenderAsync(bool firstRender)
{ {
@ -33,4 +35,43 @@ public partial class Export
return Task.CompletedTask; return Task.CompletedTask;
} }
protected override async Task OnInitializedAsync()
{
_ToolTypeNameId ??= new();
_TimeSpan ??= new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, 00);
_DateRange ??= new DateRange(DateTime.Now.AddMonths(-1).Date, DateTime.Now.Date);
if (Logger is null)
throw new NullReferenceException(nameof(Logger));
if (HttpClient is null)
throw new NullReferenceException(nameof(HttpClient));
string controllerName = IToolTypesController<object>.GetRouteName();
try
{
Result<ToolTypeNameId[]>? result = await HttpClient.GetFromJsonAsync<Result<ToolTypeNameId[]>>($"api/{controllerName}");
if (result is not null)
_ToolTypeNameIdCollection = result.Results;
}
catch (Exception)
{
string json = await HttpClient.GetStringAsync($"api/{controllerName}");
Logger.LogInformation(message: json);
}
}
protected async Task DownloadAsync()
{
if (JSRuntime is null)
throw new NullReferenceException(nameof(JSRuntime));
if (HttpClient is null)
throw new NullReferenceException(nameof(HttpClient));
if (_TimeSpan is null || _DateRange is null || _ToolTypeNameId is null || _ToolTypeNameIdCollection is null || _ToolTypeNameId.ToolTypeName is null || _DateRange.Start is null || _DateRange.End is null)
return;
string controllerName = IToolTypesController<object>.GetRouteName();
string endTime = _DateRange.End.Value.AddTicks(_TimeSpan.Value.Ticks).ToString();
string startTime = _DateRange.Start.Value.AddTicks(_TimeSpan.Value.Ticks).ToString();
string fileName = $"Export_{_ToolTypeNameId.ToolTypeName}_{startTime:yyyyMMddHHmm}_to_{endTime:yyyyMMddHHmm}.csv";
string query = $"datebegin={startTime:MM/dd/yyyy hh:mm tt}&dateend={endTime:MM/dd/yyyy hh:mm tt}&filename={WebUtility.UrlEncode(fileName)}";
await JSRuntime.InvokeVoidAsync("triggerFileDownload", fileName, $"{HttpClient.BaseAddress}api/{controllerName}/{_ToolTypeNameId.ID}/csv?{query}");
}
} }

View File

@ -1,5 +1,4 @@
@page "/RunInfo/{Model?}" @page "/RunInfo/{ToolTypeId:int?}/{HeaderId:int?}"
@page "/RunInfo/{ToolTypeId:int?}/{HeaderId:int?}"
@using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web
@using MudBlazor @using MudBlazor

View File

@ -8,7 +8,6 @@ public partial class RunInfo
[Parameter] public int? HeaderId { get; set; } [Parameter] public int? HeaderId { get; set; }
[Parameter] public int? ToolTypeId { get; set; } [Parameter] public int? ToolTypeId { get; set; }
[Parameter] public Metrology.Shared.ViewModels.RunInfo? Model { get; set; }
[Inject] protected IJSRuntime? JSRuntime { get; set; } [Inject] protected IJSRuntime? JSRuntime { get; set; }
[Inject] protected Models.AppSettings? AppSettings { get; set; } [Inject] protected Models.AppSettings? AppSettings { get; set; }
@ -21,9 +20,10 @@ public partial class RunInfo
throw new NullReferenceException(nameof(JSRuntime)); throw new NullReferenceException(nameof(JSRuntime));
if (AppSettings is null) if (AppSettings is null)
throw new NullReferenceException(nameof(AppSettings)); throw new NullReferenceException(nameof(AppSettings));
int initialHeaderId = Model is not null ? Model.HeaderID : HeaderId is not null ? HeaderId.Value : 0; Metrology.Shared.ViewModels.RunInfo? model = null;
string initialHeaderAttachmentId = Model is not null ? Model.HeaderAttachmentID.ToString() : string.Empty; int initialHeaderId = model is not null ? model.HeaderID : HeaderId is not null ? HeaderId.Value : 0;
int initialToolTypeID = Model is not null ? Model.ToolTypeID : ToolTypeId is not null ? ToolTypeId.Value : 1; string initialHeaderAttachmentId = model is not null ? model.HeaderAttachmentID.ToString() : string.Empty;
int initialToolTypeID = model is not null ? model.ToolTypeID : ToolTypeId is not null ? ToolTypeId.Value : 1;
return JSRuntime.InvokeVoidAsync("initRunInfo", AppSettings.ApiUrl, initialToolTypeID, initialHeaderId, initialHeaderAttachmentId).AsTask(); return JSRuntime.InvokeVoidAsync("initRunInfo", AppSettings.ApiUrl, initialToolTypeID, initialHeaderId, initialHeaderAttachmentId).AsTask();
} }
return Task.CompletedTask; return Task.CompletedTask;

View File

@ -30,6 +30,7 @@ internal class Program
_ = builder.Services.AddSingleton(_ => appSettings); _ = builder.Services.AddSingleton(_ => appSettings);
_ = builder.Services.AddSingleton<WeatherForecastService>(); _ = builder.Services.AddSingleton<WeatherForecastService>();
_ = builder.Services.AddSingleton<WeatherForecastService>();
_ = builder.Services.AddScoped(serviceProvider => new HttpClient { BaseAddress = new Uri(appSettings.ApiUrl) }); _ = builder.Services.AddScoped(serviceProvider => new HttpClient { BaseAddress = new Uri(appSettings.ApiUrl) });
WebApplication app = builder.Build(); WebApplication app = builder.Build();

View File

@ -538,3 +538,11 @@ function initRunInfo(apiUrl, initialToolTypeID, initialHeaderId, initialHeaderAt
} }
}, 180000); }, 180000);
}; };
function triggerFileDownload(fileName, url) {
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
}

View File

@ -1,7 +1,7 @@
body { body {
padding-top: 50px; padding-top: 50px;
padding-bottom: 20px; padding-bottom: 20px;
background-color:darkgrey; background-color: darkgrey;
} }
/* Set padding to keep content from hitting the edges */ /* Set padding to keep content from hitting the edges */
@ -104,3 +104,7 @@ div.modal-content-warning {
.mud-table-cell { .mud-table-cell {
font-size: 1.875rem; font-size: 1.875rem;
} }
#ExportData {
zoom: 2;
}

View File

@ -66,6 +66,23 @@ public class ToolTypesController : Controller, IToolTypesController<IActionResul
return Content(json); return Content(json);
} }
[HttpGet]
[Route("{toolTypeId}/export")]
public IActionResult GetExportData(int toolTypeId, [FromQuery] DateTime? datebegin, [FromQuery] DateTime? dateend)
{
Shared.DataModels.Result<DataTable> r = _ToolTypesRepository.GetExportData(_MetrologyRepo, toolTypeId, datebegin, dateend);
string json = JsonConvert.SerializeObject(r);
return Content(json);
}
[HttpGet]
[Route("{toolTypeId}/csv")]
public IActionResult GetCSVExport(int toolTypeId, [FromQuery] DateTime? datebegin, [FromQuery] DateTime? dateend, [FromQuery] string? filename)
{
byte[] r = _ToolTypesRepository.GetCSVExport(_MetrologyRepo, toolTypeId, datebegin, dateend);
return File(r, "application/octet-stream", filename);
}
[HttpGet] [HttpGet]
[Route("{toolTypeId}/{tabletype}/files/{attachmentId}/{filename}")] [Route("{toolTypeId}/{tabletype}/files/{attachmentId}/{filename}")]
public IActionResult GetAttachment(int toolTypeId, string tabletype, string attachmentId, string filename) public IActionResult GetAttachment(int toolTypeId, string tabletype, string attachmentId, string filename)

View File

@ -29,13 +29,13 @@ public class ExportController : Controller
base.OnActionExecuted(context); base.OnActionExecuted(context);
ViewBag.IsTestDatabase = _IsTestDatabase; ViewBag.IsTestDatabase = _IsTestDatabase;
} }
[HttpGet] [HttpGet]
[Route("/Export")] [Route("/Export")]
public ActionResult Index() public ActionResult Index()
{ {
Export model = new() Export model = new()
{ {
ToolType = "",
StartTime = DateTime.Now.AddMonths(-1), StartTime = DateTime.Now.AddMonths(-1),
EndTime = DateTime.Now EndTime = DateTime.Now
}; };

View File

@ -0,0 +1 @@
{"Results":[],"TotalRows":0}

View File

@ -82,6 +82,9 @@
<None Include="Data\Tests\GetDataApi.json"> <None Include="Data\Tests\GetDataApi.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Include="Data\Tests\GetExportDataApi.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Data\Tests\GetHeaderFieldsApi.json"> <None Include="Data\Tests\GetHeaderFieldsApi.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>

View File

@ -2,6 +2,7 @@ using OI.Metrology.Shared.DataModels;
using OI.Metrology.Shared.Models.Stateless; using OI.Metrology.Shared.Models.Stateless;
using OI.Metrology.Shared.Services; using OI.Metrology.Shared.Services;
using System.Data; using System.Data;
using System.Text;
using System.Text.Json; using System.Text.Json;
namespace OI.Metrology.Server.Repository; namespace OI.Metrology.Server.Repository;
@ -216,7 +217,7 @@ public class ToolTypesRepository : IToolTypesRepository
// The second table has the header data // The second table has the header data
if (ds.Tables[1].Rows.Count != 1) if (ds.Tables[1].Rows.Count != 1)
throw new Exception("Error exporting, invalid header data"); throw new Exception("Error exporting, invalid header data");
System.Text.StringBuilder sb = new(); StringBuilder sb = new();
foreach (object? o in ds.Tables[1].Rows[0].ItemArray) foreach (object? o in ds.Tables[1].Rows[0].ItemArray)
{ {
if ((o is not null) && (!Convert.IsDBNull(o))) if ((o is not null) && (!Convert.IsDBNull(o)))
@ -244,4 +245,96 @@ public class ToolTypesRepository : IToolTypesRepository
return result; return result;
} }
Result<DataTable> IToolTypesRepository.GetExportData(IMetrologyRepository metrologyRepository, int toolTypeId, DateTime? datebegin, DateTime? dateend)
{
Result<DataTable>? r;
if (!string.IsNullOrEmpty(_MockRoot))
{
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetExportDataApi.json"));
r = Newtonsoft.Json.JsonConvert.DeserializeObject<Result<DataTable>>(json);
if (r is null)
throw new NullReferenceException(nameof(r));
}
else
{
dateend ??= DateTime.Now;
datebegin ??= dateend.Value.AddMonths(-1);
ToolType tt = metrologyRepository.GetToolTypeByID(toolTypeId);
if (string.IsNullOrEmpty(tt.ExportSPName))
throw new NullReferenceException(nameof(tt.ExportSPName));
DataTable dataTable = metrologyRepository.ExportData(tt.ExportSPName, datebegin.Value, dateend.Value);
r = new()
{
Results = dataTable,
TotalRows = dataTable.Rows.Count,
};
}
return r;
}
protected static string GetRowData(DataRow dr)
{
StringBuilder result = new();
for (int i = 0; i < dr.Table.Columns.Count; i++)
{
if (i > 0)
_ = result.Append(',');
object v = dr[i];
if (v is not null && !Convert.IsDBNull(v))
_ = result.Append(FormatForCSV(string.Concat(Convert.ToString(v))));
}
return result.ToString();
}
protected static string GetColumnHeaders(DataTable dataTable)
{
StringBuilder result = new();
for (int i = 0; i < dataTable.Columns.Count; i++)
{
if (i > 0)
_ = result.Append(',');
_ = result.Append(FormatForCSV(dataTable.Columns[i].ColumnName.TrimEnd('_')));
}
return result.ToString();
}
protected static string FormatForCSV(string v)
{
StringBuilder result = new(v.Length + 2);
bool doubleQuoted = false;
if (v.StartsWith(' ') || v.EndsWith(' ') || v.Contains(',') || v.Contains('"'))
{
_ = result.Append('"');
doubleQuoted = true;
}
foreach (char c in v)
{
_ = c switch
{
'\r' or '\n' => result.Append(' '),
'"' => result.Append("\"\""),
_ => result.Append(c),
};
}
if (doubleQuoted)
_ = result.Append('"');
return result.ToString();
}
byte[] IToolTypesRepository.GetCSVExport(IMetrologyRepository metrologyRepository, int toolTypeId, DateTime? datebegin, DateTime? dateend)
{
byte[] results;
Result<DataTable> result;
IToolTypesRepository repository = this;
result = repository.GetExportData(metrologyRepository, toolTypeId, datebegin, dateend);
if (result.Results is null)
throw new NullReferenceException(nameof(result.Results));
StringBuilder stringBuilder = new();
_ = stringBuilder.AppendLine(GetColumnHeaders(result.Results));
foreach (DataRow dr in result.Results.Rows)
_ = stringBuilder.AppendLine(GetRowData(dr));
results = Encoding.UTF8.GetBytes(stringBuilder.ToString());
return results;
}
} }

View File

@ -15,5 +15,6 @@ public interface IToolTypesRepository
Result<DataTable> GetData(IMetrologyRepository metrologyRepository, int id, long headerid); Result<DataTable> GetData(IMetrologyRepository metrologyRepository, int id, long headerid);
(string?, string?, Stream?) GetAttachment(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, int toolTypeId, string tabletype, string attachmentId, string filename); (string?, string?, Stream?) GetAttachment(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, int toolTypeId, string tabletype, string attachmentId, string filename);
Exception? OIExport(IMetrologyRepository metrologyRepository, string oiExportPath, int toolTypeId, long headerid); Exception? OIExport(IMetrologyRepository metrologyRepository, string oiExportPath, int toolTypeId, long headerid);
Result<DataTable> GetExportData(IMetrologyRepository metrologyRepository, int toolTypeId, DateTime? datebegin, DateTime? dateend);
byte[] GetCSVExport(IMetrologyRepository metrologyRepository, int toolTypeId, DateTime? datebegin, DateTime? dateend);
} }

View File

@ -201,6 +201,32 @@ public class UnitTestToolTypesController
_Logger.Information($"{_TestContext?.TestName} completed"); _Logger.Information($"{_TestContext?.TestName} completed");
} }
[TestMethod]
public void GetExportData()
{
_Logger.Information("Starting Web Application");
IServiceProvider serviceProvider = _WebApplicationFactory.Services.CreateScope().ServiceProvider;
IMetrologyRepository metrologyRepository = serviceProvider.GetRequiredService<IMetrologyRepository>();
IToolTypesRepository toolTypesRepository = serviceProvider.GetRequiredService<IToolTypesRepository>();
Result<DataTable> result = toolTypesRepository.GetExportData(metrologyRepository, toolTypeId: 1, datebegin: null, dateend: null);
Assert.IsNotNull(result?.Results);
Assert.IsNotNull(result.Results.Rows.Count > 0);
_Logger.Information($"{_TestContext?.TestName} completed");
}
[TestMethod]
public async Task GetExportDataApi()
{
HttpClient httpClient = _WebApplicationFactory.CreateClient();
_Logger.Information("Starting Web Application");
string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/1/export");
File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{nameof(GetExportDataApi)}.json"), json);
Result<DataTable>? result = Newtonsoft.Json.JsonConvert.DeserializeObject<Result<DataTable>>(json);
Assert.IsNotNull(result?.Results);
Assert.IsNotNull(result.Results.Rows.Count > 0);
_Logger.Information($"{_TestContext?.TestName} completed");
}
[TestMethod] [TestMethod]
[Ignore] [Ignore]
public void GetAttachment() public void GetAttachment()