Files
Saxonite/SaxoniteCoreWebAdmin/Admin/Procs/SaxOrganisationUploader.cs
T
2024-02-27 15:45:47 +00:00

260 lines
9.5 KiB
C#

using Microsoft.AspNetCore.Http;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Proc.Excel;
using Sarand.Net.Standard.Proc.Procs;
using Saxonite.Net.Core.Web.Base.Procs.Data;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Procs;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Procs
{
//=======================================================================
public class SaxOrganisationUploader : SaxProcess_Admin
{
//-------------------------------------------------------------------
// Definitions.
// Process configuration.
public new class Config : SaxProcess_Admin.Config
{
public Config(SaxSessionContext sessionContext, IFormFile dataFile) : base(sessionContext)
{
this.DataFile = new UploadFileData(dataFile);
}
// Properties.
public UploadFileData DataFile { get; }
public String SheetNameOrNumber { get; set; }
public uint HeaderRowPos { get; set; }
public bool ValidateOnly { get; set; }
public bool AllowCreateOrganisation { get; set; }
public bool RequireOrganisationIdForUpdate { get; set; }
}
// Supported content types.
private static HashSet<String> validContentTypes { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
SacsContentTypeHelper.ContentType_Excel_Legacy, /* xls */
SacsContentTypeHelper.ContentType_Excel_Open, /* xlsx */
SacsContentTypeHelper.ContentType_Excel_MacroEnabled, /* xlsm */
SacsContentTypeHelper.ContentType_Excel_BinaryMacroEnabled /* xlsb */
};
/// <summary>Allowed content types.</summary>
public static List<String> ValidContentTypes { get { return new List<String>(validContentTypes); } }
//-------------------------------------------------------------------
// Organisation data profile.
private enum OrganisationProperty
{
// Property columns.
OrganisationId, OrganisationName, Sport, Region,
// Address.
AddressLine1, AddressLine2, AddressTown, AddressPostcode,
// Contact personal.
ContactName, ContactRole, ContactEmail,
// Contact.
ContactPhone, ContactWeb, ContactMap, ContactWiki, ContactFacebook, ContactInstagram, ContactTwitter,
}
private class OrganisationPropertyData
{
public OrganisationPropertyData(OrganisationProperty property, String columnHeaders)
{
this.Property = property;
foreach (String value in Regex.Split(columnHeaders, "[\\s,]+", RegexOptions.None))
if (!StringUtil.IsBlank(value)) this.ColumnHeaders.Add(value);
}
public OrganisationProperty Property { get; }
public HashSet<String> ColumnHeaders { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public uint ColumnPos { get; set; }
public bool IsValid { get { return ColumnPos > 0; } }
}
private class OrganisationDataProfile
{
public OrganisationDataProfile()
{
// Property columns.
AddProperty(OrganisationProperty.OrganisationId, "Id, Organisation Id");
AddProperty(OrganisationProperty.OrganisationName, "Name, Organisation Name");
AddProperty(OrganisationProperty.Sport, "Sport");
AddProperty(OrganisationProperty.Region, "Region");
// Address.
AddProperty(OrganisationProperty.AddressLine1, "Address Line 1, Line 1, Street");
AddProperty(OrganisationProperty.AddressLine2, "Address Line 2, Line 2");
AddProperty(OrganisationProperty.AddressTown, "Town,City,Address Town,Address City");
AddProperty(OrganisationProperty.AddressPostcode, "Postcode");
// Contact personal.
AddProperty(OrganisationProperty.ContactName, "Contact Name");
AddProperty(OrganisationProperty.ContactRole, "Contact Role");
AddProperty(OrganisationProperty.ContactEmail, "Contatc Email");
// Contact.
AddProperty(OrganisationProperty.ContactPhone, "Phone");
AddProperty(OrganisationProperty.ContactWeb, "Web, Web Link, Website");
AddProperty(OrganisationProperty.ContactMap, "Web Map, Map Link");
AddProperty(OrganisationProperty.ContactWiki, "Wiki, Wikipedia, Wiki Link");
AddProperty(OrganisationProperty.ContactFacebook, "Facebook");
AddProperty(OrganisationProperty.ContactInstagram, "Instagram");
AddProperty(OrganisationProperty.ContactTwitter, "X, Twitter");
}
public Dictionary<OrganisationProperty, OrganisationPropertyData> Properties { get; } = new Dictionary<OrganisationProperty, OrganisationPropertyData>();
private void AddProperty(OrganisationProperty property, String columnHeaders)
{
Properties.Add(property, new OrganisationPropertyData(property, columnHeaders));
}
public bool IsValidProperty(OrganisationProperty property)
{
OrganisationPropertyData propertyData = Properties[property];
return propertyData != null && propertyData.IsValid;
}
}
// Processing control data.
private class ProcessControlData
{
public Config config { get; set; }
public SaMsSpreadsheet spreadsheet { get; set; }
public OrganisationDataProfile dataProfile { get; } = new OrganisationDataProfile();
}
//-------------------------------------------------------------------
// Data members.
private ProcessControlData control { get; } = new ProcessControlData();
//-------------------------------------------------------------------
// Construction.
public SaxOrganisationUploader(Config config) : base(config)
{
control.config = config;
}
//-------------------------------------------------------------------
// Properties.
public override string ProcessDescription => "Organisation Upload";
//-------------------------------------------------------------------
// Run the process.
protected override bool Run()
{
String fn = GetType().Name + ".Run";
// Verify file.
if (control.config.DataFile == null)
{
AddErrorMessage("Data file must be supplied.");
SaLogger.Warn(fn + " " + GetErrorMessage());
return OK;
}
else if (!validContentTypes.Contains(control.config.DataFile.ContentType))
{
AddErrorMessage("Unsupported content type: " + control.config.DataFile.ContentType + ".");
SaLogger.Warn(fn + " " + GetErrorMessage());
}
AddInfoMessage("Data file: " + control.config.DataFile.FileName);
AddInfoMessage("Sheet: " + control.config.SheetNameOrNumber);
try
{
if (OK)
{
// Open spreadsheet.
using (SaMsSpreadsheet_POI spreadsheet = new SaMsSpreadsheet_POI())
using (Stream stream = new MemoryStream())
{
stream.Write(control.config.DataFile.FileData, 0, (int)control.config.DataFile.Length);
if (!spreadsheet.OpenWorkbook(stream))
{
// Failed to open workbook.
String msg = "Failed to open workbook - " + spreadsheet.ErrorStateMessage;
AddErrorMessage(msg);
SaLogger.Warn(fn + " " + msg);
}
if (OK && !spreadsheet.OpenWorksheet(1))
{
// Failed to open workbook.
String msg = "Failed to open worksheet - " + spreadsheet.ErrorStateMessage;
AddErrorMessage(msg);
SaLogger.Warn(fn + " " + msg);
}
if (OK)
{
control.spreadsheet = spreadsheet;
PopulateDataProfile();
}
if (OK)
{
uint firstRowPos = spreadsheet.FirstRowPos;
for (uint rowPos = firstRowPos; rowPos <= firstRowPos; rowPos++)
{
for (uint colPos = 1; colPos <= 5; colPos++)
{
SaLogger.Info("Row: " + rowPos + ", colPos: " + colPos + ": '" + spreadsheet.GetCellText(colPos, rowPos) + "'");
}
}
}
// Clean-up.
control.spreadsheet = null;
}
}
}
catch (Exception exc)
{
String msg = exc.GetType().Name + " uploading organisation data.";
AddErrorMessage(msg);
SaLogger.Warn(fn + " " + msg + " " + exc.Message + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t"));
}
return OK;
}
//-------------------------------------------------------------------
private bool PopulateDataProfile()
{
String fn = GetType().Name + ".PopulateDataProfile";
// Identify header row.
uint headerRowPos = control.config.HeaderRowPos;
if (headerRowPos < 1)
headerRowPos = control.spreadsheet.FirstRowPos;
// Column headings.
List<String> columnHeadings = new List<string>();
for (uint colPos = 1; colPos <= 100; colPos++)
{
columnHeadings.Add(control.spreadsheet.GetCellText(colPos, headerRowPos));
}
SaLogger.Info(fn + " Column headings: " + columnHeadings.Concat(", "));
// Populate header columns in profile.
foreach (OrganisationPropertyData propertyData in control.dataProfile.Properties.Values)
{
for (uint colPos = 1; colPos <= columnHeadings.Count; colPos++)
{
String columnHeading = columnHeadings[(int)(colPos - 1)];
if (propertyData.ColumnHeaders.ContainsCI(columnHeading))
{
if (control.dataProfile.Properties.Values.ToList().Exists(item => item.ColumnPos == colPos))
AddWarningMessage("Multiple properties associated with column '" + columnHeading + "'.");
propertyData.ColumnPos = colPos;
break;
}
}
}
if (!control.dataProfile.IsValidProperty(OrganisationProperty.OrganisationId) && !control.dataProfile.IsValidProperty(OrganisationProperty.OrganisationName))
return AddErrorMessage("Organisation Id or Name column must be present");
return OK;
}
//-------------------------------------------------------------------
}
//=======================================================================
}