v1.3.0 Restructure Saxonite workspace and upgrade to .NET 10 (#1)

Move Saxonite’s projects into a dedicated `Workspace` folder and align the solution with .NET 10 and the current Common workspace. Update encryption integration, test project structure and coverage, and repository guidance and upgrade notes.

- Relocate application and library projects while retaining Git rename history.
- Split tests into Admin, Online, and Shared projects; add representative behavioral and serialization tests.
- Update Common integration, application version, and repository documentation.

Application version: 1.3.0

Reviewed-on: #1
Co-authored-by: andy <andy@sarand.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-10-16 18:24:16 +01:00
committed by andy
parent 2c3fb146ab
commit 9f22b522df
1996 changed files with 1992 additions and 1179 deletions
@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Http;
using Sarand.Net.Standard.Base.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.Procs.Data
{
//=======================================================================
/// <summary>
/// Object to persist upload file after request has terminated, and the Form File has been disposed.
/// <para>- Too allow async processes to access the file after request completed.</para>
/// </summary>
public class UploadFileData
{
//-------------------------------------------------------------------
public UploadFileData(IFormFile dataFile)
{
ContentDisposition = dataFile.ContentDisposition;
ContentType = dataFile.ContentType;
FileName = dataFile.FileName;
Length = dataFile.Length;
Name = dataFile.Name;
foreach (var header in dataFile.Headers)
Headers.Add(header.Key, header.Value.ToString());
// Load file data.
using (MemoryStream stream = new MemoryStream())
{
dataFile.CopyTo(stream);
FileData = stream.ToArray();
}
}
//-------------------------------------------------------------------
// Properties.
/// <summary>Gets the raw Content-Disposition header of the uploaded file.</summary>
public String ContentDisposition { get; }
/// <summary>Gets the raw Content-Type header of the uploaded file.</summary>
public String ContentType { get; }
/// <summary>Gets the file name from the Content-Disposition header.</summary>
public String FileName { get; }
/// <summary>Gets the header dictionary of the uploaded file.</summary>
public Dictionary<String, String> Headers { get; } = new Dictionary<String, String>();
/// <summary>Gets the file length in bytes.</summary>
public long Length { get; }
/// <summary>Gets the form field name from the Content-Disposition header.</summary>
public String Name { get; }
/// <summary>File data.</summary>
public byte[] FileData { get; }
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,341 @@
using DocumentFormat.OpenXml.Drawing.Diagrams;
using DocumentFormat.OpenXml.Office2016.Excel;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Tasks;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus.Reference;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Saxonite.Net.Core.Web.Base.Procs
{
//=======================================================================
partial class SaxGolfUsgaDataLookup
{
//-------------------------------------------------------------------
private class ScheduledUsgaDataLookupControlData
{
public DateTime CountriesLastLookedUp { get; set; } = DateTime.Now;
}
private static ScheduledUsgaDataLookupControlData usgaDataLookupControl { get; } = new ScheduledUsgaDataLookupControlData();
/// <summary>
/// Determine which USGA data population process to run.
/// <para>- Method is invoked periodically (every 20 mins) by the Admin Hosted Service Console.</para>
/// <para>- 1. Populate countries if not already done or not done for a while (eg. 24 hours).</para>
/// <para>- 2. Populate provinces for a Country that has none or occasionally refresh one that has.</para>
/// <para>- 3. Populate Courses for a Province that has none or occasionally refresh one that has.</para>
/// <para>- 4. Populate Markers for a Course that has none or occasionally refresh one that has.</para>
/// </summary>
public static void ExecuteScheduledUsgaDataLookup(SaxSessionContext sessionContext, DbUpdateInfo dbUpdateInfo, SacsTaskData taskData)
{
SaLogger.Debug($"{SysUtil.Fn}");
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
SaxInternals internals = new SaxInternals(sessionContext);
disposableItems.Add(internals);
// Configure process.
SaxGolfUsgaDataLookup.Config courseLookupConfig = new SaxGolfUsgaDataLookup.Config(sessionContext, dbUpdateInfo);
SaxGolfUsgaDataLookup usgaDataLookup = new SaxGolfUsgaDataLookup(courseLookupConfig);
if (taskData.IsCancellationRequested) return;
// Determine which process to run.
//-----------------------------------------------------------
// 1. Countries.
{
// Check USGA country population.
List<SaxGolfUsgaCountry> usgaCountries = internals.ReferenceGolfMgr.GetUsgaCountryList(new SaxGolfUsgaCountry.Params());
if (!usgaCountries.Any() || (DateTime.Now - usgaDataLookupControl.CountriesLastLookedUp).TotalHours > MathUtil.Constrain(Sax.Config.Admin.Processes.Scheduled.UsgaGolfDataLookup.HoursBetweenCountryLookup, 1, 24 * 7))
{
// No countries currently found or long enough since last lookup.
// Lookup USGA countries.
List<SaxGolfUsgaDataLookup.CountryData> countriesData = usgaDataLookup.LookupUsgaCountries();
SaLogger.Info($"{SysUtil.Fn} USGA Countries populated: {countriesData.Count}.", true);
usgaDataLookupControl.CountriesLastLookedUp = DateTime.Now;
return;
}
if (taskData.IsCancellationRequested) return;
}
//-----------------------------------------------------------
// 2. Provinces.
// 2a. Population of active priority Country with no Provinces.
{
bool populatePriorityCountryNoProvinces = SacsRandomHelper.GetInt32(1, 5) == 1;
if (populatePriorityCountryNoProvinces)
{
SaxGolfUsgaCountry.Params prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCountry.HasProvinces = SacsHierarchyItem.Params.BoolState.False;
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
prmCountry.ResultsPaging.Set(1, 4);
prmCountry.RandomOrder = true;
List<SaxGolfUsgaCountry> countriesNoProvinces = internals.ReferenceGolfMgr.GetUsgaCountryList(prmCountry).Randomise();
disposableItems.AddRange(countriesNoProvinces);
if (countriesNoProvinces.Any())
{
SaxGolfUsgaCountry usgaCountry = countriesNoProvinces[0];
List<SaxGolfUsgaDataLookup.ProvinceData> provincesData = usgaDataLookup.LookupUsgaProvincesForCountry(usgaCountry.CountryCode);
SaLogger.Info($"{SysUtil.Fn} USGA Provinces populated for {usgaCountry.CountryCode} {usgaCountry.CountryName}" + (usgaCountry.Priority ? " [Priority]" : "") + $": {provincesData.Count}.", true);
return;
}
}
}
// 2b. Refresh active priority Country with Provinces.
{
bool refreshPriorityCountryWithProvinces = SacsRandomHelper.GetInt32(1, 25) == 1;
if (refreshPriorityCountryWithProvinces)
{
SaxGolfUsgaCountry.Params prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCountry.HasProvinces = SacsHierarchyItem.Params.BoolState.True;
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
prmCountry.ResultsPaging.Set(1, 4);
prmCountry.RandomOrder = true;
List<SaxGolfUsgaCountry> countriesWithProvinces = internals.ReferenceGolfMgr.GetUsgaCountryList(prmCountry).Randomise();
disposableItems.AddRange(countriesWithProvinces);
if (countriesWithProvinces.Any())
{
SaxGolfUsgaCountry usgaCountry = countriesWithProvinces[0];
List<SaxGolfUsgaDataLookup.ProvinceData> provincesData = usgaDataLookup.LookupUsgaProvincesForCountry(usgaCountry.CountryCode);
SaLogger.Info($"{SysUtil.Fn} USGA Provinces refreshed for {usgaCountry.CountryCode} {usgaCountry.CountryName}" + (usgaCountry.Priority ? " [Priority]" : "") + $": {provincesData.Count}.", true);
return;
}
}
}
//-----------------------------------------------------------
// 3. Courses.
// 3a. Populate active priority Province with no Facilities (Courses).
{
bool populatePriorityProvinceNoFacilities = SacsRandomHelper.GetInt32(1, 4) == 1;
if (populatePriorityProvinceNoFacilities)
{
// Active priority Provinces with no Facilities.
SaxGolfUsgaProvince.Params prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmProvince.HasFacilities = SacsHierarchyItem.Params.BoolState.False;
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
prmProvince.ResultsPaging.Set(1, 4);
prmProvince.RandomOrder = true;
List<SaxGolfUsgaProvince> provincesNoFacilities = internals.ReferenceGolfMgr.GetUsgaProvinceList(prmProvince).Randomise();
disposableItems.AddRange(provincesNoFacilities);
if (provincesNoFacilities.Any())
{
// Province has no Facilities - populate courses.
SaxGolfUsgaProvince usgaProvince = provincesNoFacilities[0];
List<SaxGolfUsgaDataLookup.CourseData> coursesData = LookupUsgaCourses(internals, usgaDataLookup, usgaProvince, false);
return;
}
}
}
// 3b. Refresh active priority Province with Facilities (courses).
{
bool refreshProvinceWithFacilities = SacsRandomHelper.GetInt32(1, 21) == 1;
if (refreshProvinceWithFacilities)
{
SaxGolfUsgaProvince.Params prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmProvince.HasFacilities = SacsHierarchyItem.Params.BoolState.True;
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
prmProvince.ResultsPaging.Set(1, 4);
prmProvince.RandomOrder = true;
List<SaxGolfUsgaProvince> provincesWithFacilities = internals.ReferenceGolfMgr.GetUsgaProvinceList(prmProvince).Randomise();
disposableItems.AddRange(provincesWithFacilities);
if (provincesWithFacilities.Any())
{
SaxGolfUsgaProvince usgaProvince = provincesWithFacilities[0];
List<SaxGolfUsgaDataLookup.CourseData> coursesData = LookupUsgaCourses(internals, usgaDataLookup, usgaProvince, true);
return;
}
}
}
//-----------------------------------------------------------
// 4. Markers.
// 4a. Populate active priority Course with no Markers.
{
bool populatePriorityCourseNoMarkers = SacsRandomHelper.GetInt32(1, 2) == 1;
if (populatePriorityCourseNoMarkers)
{
SaxGolfUsgaCourse.Params prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCourse.HasMarkers = SacsHierarchyItem.Params.BoolState.False;
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
prmCourse.ResultsPaging.Set(1, 4);
prmCourse.RandomOrder = true;
List<SaxGolfUsgaCourse> coursesNoMarkers = internals.ReferenceGolfMgr.GetUsgaCourseList(prmCourse).Randomise();
disposableItems.AddRange(coursesNoMarkers);
if (coursesNoMarkers.Any())
{
SaxGolfUsgaCourse usgaCourse = coursesNoMarkers[0];
List<SaxGolfUsgaDataLookup.MarkerData> markersData = LookupUsgaMarkers(internals, usgaDataLookup, usgaCourse, false);
return;
}
}
}
// 4b. Refresh active priority Course with Markers.
{
bool refreshPriorityCourseWithMarkers = SacsRandomHelper.GetInt32(1, 17) == 1;
if (refreshPriorityCourseWithMarkers)
{
// Active priority Course with no Markers.
SaxGolfUsgaCourse.Params prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCourse.HasMarkers = SacsHierarchyItem.Params.BoolState.True;
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
prmCourse.ResultsPaging.Set(1, 4);
prmCourse.RandomOrder = true;
List<SaxGolfUsgaCourse> coursesWithMarkers = internals.ReferenceGolfMgr.GetUsgaCourseList(prmCourse).Randomise();
disposableItems.AddRange(coursesWithMarkers);
if (coursesWithMarkers.Any())
{
// Refresh first Course with Markers.
SaxGolfUsgaCourse usgaCourse = coursesWithMarkers[0];
List<SaxGolfUsgaDataLookup.MarkerData> markersData = LookupUsgaMarkers(internals, usgaDataLookup, usgaCourse, true);
return;
}
}
}
//-----------------------------------------------------------
// 5. Fallback population.
// 5a. Active Country with no Provinces.
{
bool populateCountryNoProvinces = SacsRandomHelper.GetInt32(1, 17) == 1;
if (populateCountryNoProvinces)
{
SaxGolfUsgaCountry.Params prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.HasProvinces = SacsHierarchyItem.Params.BoolState.False;
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
prmCountry.ResultsPaging.Set(1,4);
prmCountry.RandomOrder = true;
List<SaxGolfUsgaCountry> countriesNoProvinces = internals.ReferenceGolfMgr.GetUsgaCountryList(prmCountry).Randomise();
disposableItems.AddRange(countriesNoProvinces);
if (countriesNoProvinces.Any())
{
SaxGolfUsgaCountry usgaCountry = countriesNoProvinces[0];
List<SaxGolfUsgaDataLookup.ProvinceData> provincesData = usgaDataLookup.LookupUsgaProvincesForCountry(usgaCountry.CountryCode);
SaLogger.Info($"{SysUtil.Fn} USGA Provinces populated for {usgaCountry.CountryCode} {usgaCountry.CountryName} [Fallback]: {provincesData.Count}.", true);
return;
}
}
}
// 5b. Active Province with no Facilities (courses).
{
bool populateProvinceNoFacilities = SacsRandomHelper.GetInt32(1, 3) == 1;
if (populateProvinceNoFacilities)
{
// Active Provinces with no Facilities.
SaxGolfUsgaProvince.Params prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.HasFacilities = SacsHierarchyItem.Params.BoolState.False;
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
prmProvince.ResultsPaging.Set(1, 4);
prmProvince.RandomOrder = true;
List<SaxGolfUsgaProvince> provincesNoFacilities = internals.ReferenceGolfMgr.GetUsgaProvinceList(prmProvince).Randomise();
disposableItems.AddRange(provincesNoFacilities);
if (provincesNoFacilities.Any())
{
// Province has no Facilities - populate courses.
SaxGolfUsgaProvince usgaProvince = provincesNoFacilities[0];
List<SaxGolfUsgaDataLookup.CourseData> coursesData = LookupUsgaCourses(internals, usgaDataLookup, usgaProvince, false, true);
return;
}
}
}
// 5c. Populate active Course with no Markers.
{
bool populateCourseNoMarkers = SacsRandomHelper.GetInt32(1, 2) == 1;
if (populateCourseNoMarkers)
{
SaxGolfUsgaCourse.Params prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.HasMarkers = SacsHierarchyItem.Params.BoolState.False;
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
prmCourse.ResultsPaging.Set(1, 4);
prmCourse.RandomOrder = true;
List<SaxGolfUsgaCourse> coursesNoMarkers = internals.ReferenceGolfMgr.GetUsgaCourseList(prmCourse).Randomise();
disposableItems.AddRange(coursesNoMarkers);
if (coursesNoMarkers.Any())
{
SaxGolfUsgaCourse usgaCourse = coursesNoMarkers[0];
List<SaxGolfUsgaDataLookup.MarkerData> markersData = LookupUsgaMarkers(internals, usgaDataLookup, usgaCourse, false, true);
return;
}
}
}
//-----------------------------------------------------------
SaLogger.Warn($"{SysUtil.Fn} No data population task found to execute.");
//-----------------------------------------------------------
}
}
//-------------------------------------------------------------------
private static List<SaxGolfUsgaDataLookup.CourseData> LookupUsgaCourses(SaxInternals internals, SaxGolfUsgaDataLookup usgaDataLookup, SaxGolfUsgaProvince usgaProvince, bool refresh, bool fallback = false)
{
List<SaxGolfUsgaDataLookup.CourseData> coursesData = new List<CourseData>();
SaxGolfUsgaCountry usgaCountry = internals.ReferenceGolfMgr.GetUsgaCountryById(usgaProvince.CountryId);
if (usgaCountry != null)
{
coursesData = usgaDataLookup.LookupUsgaCoursesForCountryAndProvince(usgaCountry.CountryCode, usgaProvince.ProvinceCode);
List<String> annotations = new List<string>();
if (usgaProvince.Priority) annotations.Add("Priority");
if (fallback) annotations.Add("Fallback");
SaLogger.Info($"{SysUtil.Fn} USGA Courses refreshed for {usgaCountry.CountryCode} {usgaCountry.CountryName} - {usgaProvince.ProvinceCode} {usgaProvince.ProvinceName}" + (annotations.Any() ? $" [{annotations.Concat(", ")}]" : "") + $": {coursesData.Count}.", true);
}
else SaLogger.Error($"{SysUtil.Fn} USGA Country not found for Id {usgaProvince.CountryId} for Province {usgaProvince.ProvinceCode} {usgaProvince.ProvinceName}.");
return coursesData;
}
//-------------------------------------------------------------------
private static List<SaxGolfUsgaDataLookup.MarkerData> LookupUsgaMarkers(SaxInternals internals, SaxGolfUsgaDataLookup usgaDataLookup, SaxGolfUsgaCourse usgaCourse, bool refresh, bool fallback = false)
{
SaxGolfUsgaFacility usgaFacility = internals.ReferenceGolfMgr.GetUsgaFacilityById(usgaCourse.FacilityId);
SaxGolfUsgaProvince usgaProvince = internals.ReferenceGolfMgr.GetUsgaProvinceById(usgaFacility?.ProvinceId);
SaxGolfUsgaCountry usgaCountry = internals.ReferenceGolfMgr.GetUsgaCountryById(usgaProvince?.CountryId);
List<SaxGolfUsgaDataLookup.MarkerData> markersData = new List<MarkerData>();
if (usgaFacility != null)
{
if (usgaProvince != null)
{
if (usgaCountry != null)
{
markersData = usgaDataLookup.LookupUsgaMarkersForCourse(usgaCourse.UsgaCourseId);
List<String> annotations = new List<string>();
if (usgaCourse.Priority) annotations.Add("Priority");
if (fallback) annotations.Add("Fallback");
SaLogger.Info($"{SysUtil.Fn} USGA Markers " + (refresh ? "refreshed" : "populated") + $" for {usgaCountry.CountryCode} {usgaCountry.CountryName} - {usgaProvince.ProvinceCode} {usgaProvince.ProvinceName} - {usgaCourse.Caption} [Id: {usgaCourse.UsgaCourseId}" + (annotations.Any() ? $"{annotations.Concat("", ", ")}" : "") + $"]: {markersData.Count}.", true);
}
else SaLogger.Error($"{SysUtil.Fn} Missing Country for Province {usgaProvince.ProvinceName}. Country Id: {usgaProvince.CountryId}.");
}
else SaLogger.Error($"{SysUtil.Fn} Missing Province for Facility {usgaFacility.FacilityName}. Province Id: {usgaFacility.ProvinceId}.");
}
else SaLogger.Error($"{SysUtil.Fn} Missing Facility for Course {usgaCourse.FullName}. Facility Id: {usgaCourse.FacilityId}.");
return markersData;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,178 @@
using DocumentFormat.OpenXml.InkML;
using HtmlAgilityPack;
using MailKit;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Net;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Bus.Reference;
using Saxonite.Net.Standard.Base.Mgr.Reference;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
namespace Saxonite.Net.Core.Web.Base.Procs
{
//======================================================================
public partial class SaxOrganisationEventsLookup : SaxProcess_Web
{
//-------------------------------------------------------------------
// Definitions.
public enum Task
{
LookupEvents
}
public new class Config : SaxProcess_Web.Config
{
public Config(SaxSessionContext sessionContext, DbUpdateInfo dbUpdateInfo) : base(sessionContext, dbUpdateInfo)
{ }
/// <summary>Organisations.</summary>
public HashSet<SysId> OrganisationIdsIncluded { get; } = new HashSet<SysId>();
/// <summary>Tasks to be run under this instance of the USGA Data Lookup process.</summary>
public HashSet<Task> Tasks { get; } = new HashSet<Task>();
}
private class EventData
{
public DateTime EventAt { get; set; }
public String Caption { get; set; }
}
//-------------------------------------------------------------------
// Data members.
private Config config;
//-------------------------------------------------------------------
// Construction.
public SaxOrganisationEventsLookup(Config config) : base(config)
{
this.config = config;
}
//-------------------------------------------------------------------
// Properties.
public override string ProcessDescription => "Organisation Events Lookup";
//-------------------------------------------------------------------
// Run the process.
protected override bool Run()
{
// Execute selected tasks.
foreach (Task task in config.Tasks)
{
switch (task)
{
case Task.LookupEvents:
LookupEventsForOrganisations();
break;
}
if (!OK) break;
}
return OK;
}
//------------------------------------------------------------------
// Event lookup.
//------------------------------------------------------------------
/// <summary>
/// Organisation event lookup.
/// </summary>
private bool LookupEventsForOrganisations()
{
if (!OK) return false;
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
foreach (SysId organisationId in config.OrganisationIdsIncluded)
{
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
if (organisation == null)
{
AddWarningMessage($"Organisation not found for Id: {organisationId}.");
continue;
}
LookupOrganisationEvents(organisation);
}
}
return OK;
}
/// <summary>
/// Organisation event lookup.
/// </summary>
private bool LookupOrganisationEvents(SaxOrganisation organisation)
{
List<EventData> eventsData = new List<EventData>();
try
{
// Query Countries on USGA website.
CancellationToken cancelToken = new CancellationToken();
SacsHttpRequestData request = new SacsHttpRequestData(cancelToken) { Url = "https://ncrdb.usga.org/" };
SacsHttpResponseData response = SacsHttpClient.DoRequest(request).Result;
String data = response.Data;
if (!StringUtil.IsBlank(response.Data))
{
// Parse HTML.
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(response.Data);
// Identify type of event schedule on page.
HtmlNode mobiscrollEventCalendarNode = htmlDoc.GetElementbyId("mobiscroll-event-calendar");
if (mobiscrollEventCalendarNode != null)
{
}
}
if (eventsData.Any())
{
}
}
catch (Exception exc)
{
SaLogger.Warn($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc, "querying organisation events", 8)}");
}
return OK;
}
/// <summary>
/// Organisation event lookup by MobiScroll calendar.
/// </summary>
private bool LookupOrganisationEvents_MobiScrollCalendar(SaxOrganisation organisation, List<EventData> eventsData, HtmlNode calendarNode)
{
HtmlNode headerNode = calendarNode.Descendants("div").First(node => node.HasClass("mbsc-calendar-header"));
if (headerNode != null)
{
}
return OK;
}
//------------------------------------------------------------------
}
//======================================================================
}
@@ -0,0 +1,53 @@
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Proc.Excel;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Procs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Base.Procs
{
//=======================================================================
public abstract class SaxProcess_Web : SaxProcess_Base
{
//-------------------------------------------------------------------
// Definitions.
public new class Config : SaxProcess_Base.Config
{
protected Config(SaxSessionContext sessionContext, DbUpdateInfo dbUpdateInfo) : base(sessionContext, dbUpdateInfo)
{ }
}
//-------------------------------------------------------------------
// Data members.
private Config config;
//-------------------------------------------------------------------
// Construction.
protected SaxProcess_Web(Config config) : base(config)
{
this.config = config;
}
//-------------------------------------------------------------------
// Excel Spreadsheet utilities.
//-------------------------------------------------------------------
protected override SaMsExcelColumnHeading_Config GetExcelColumnHeadingConfig()
{
SaMsExcelColumnHeading_Config configHeading = base.GetExcelColumnHeadingConfig();
return configHeading;
}
protected override SaMsExcelStyleAttr GetExcelDefaultWorkbookStyle()
{
SaMsExcelStyleAttr styleAttr = base.GetExcelDefaultWorkbookStyle();
return styleAttr;
}
//-------------------------------------------------------------------
}
//=======================================================================
}