Remove LocationParsingConfiguration and LocationPatternMatcher classes, along with related tests and UI components. Update EventOccurrenceParserService to include location validation logic, enhancing warning reporting for long locations and potential date/time patterns. Adjust appsettings and UI to reflect the removal of location parsing settings.

This commit is contained in:
2026-01-09 09:10:22 -05:00
parent 2eae3f205c
commit ea1a4a04ad
9 changed files with 95 additions and 547 deletions
@@ -21,17 +21,7 @@
<MudGrid>
<MudItem xs="12" md="6">
<MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="mb-4">
<MudText Typo="Typo.h5">Paste Event Occurrence Data</MudText>
<MudButton
Variant="Variant.Text"
Color="Color.Secondary"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.Settings"
OnClick="OpenLocationSettings">
Location Settings
</MudButton>
</MudStack>
<MudText Typo="Typo.h5" Class="mb-4">Paste Event Occurrence Data</MudText>
<MudStack Spacing="3">
<MudStack Row="true" Spacing="2">
@@ -138,6 +128,53 @@
</MudAlert>
}
@* Locations Summary *@
@if (_parseResult.IsSuccess && _parseResult.Occurrences.Any())
{
var allLocations = _parseResult.Occurrences.Values
.SelectMany(list => list)
.Select(eo => eo.Location)
.Where(loc => !string.IsNullOrWhiteSpace(loc))
.Distinct()
.OrderBy(loc => loc)
.ToList();
// Check which locations have warnings (long or contain date/time)
var dateTimePattern = new System.Text.RegularExpressions.Regex(
@"\b(January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2}\b|\b\d{1,2}:\d{2}\s*(a|p)\.?m\.?\b|\bNOON\b",
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
var longLocations = allLocations.Where(loc => loc.Length > 50).ToList();
var locationsWithDateTime = allLocations.Where(loc => dateTimePattern.IsMatch(loc)).ToList();
@if (allLocations.Any())
{
var warningCount = longLocations.Count + locationsWithDateTime.Count;
<MudText Typo="Typo.h6" Class="mt-4 mb-2">Parsed Locations (@allLocations.Count unique@(warningCount > 0 ? $", {warningCount} with warnings" : ""))</MudText>
<MudExpansionPanels Elevation="0">
<MudExpansionPanel Text="All Locations">
<MudList T="string">
@foreach (var location in allLocations)
{
var isLong = longLocations.Contains(location);
var hasDateTime = locationsWithDateTime.Contains(location);
var hasWarning = isLong || hasDateTime;
<MudListItem T="string">
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
@if (hasWarning)
{
<MudChip T="string" Color="Color.Warning" Size="Size.Small">Warning</MudChip>
}
<MudText Style="@(hasWarning ? "color: var(--mud-palette-warning);" : "")">@location</MudText>
</MudStack>
</MudListItem>
}
</MudList>
</MudExpansionPanel>
</MudExpansionPanels>
}
}
@* Parsed Occurrences List *@
@if (_parseResult.IsSuccess && _parseResult.Occurrences.Any())
{
@@ -307,9 +344,5 @@
};
}
private void OpenLocationSettings()
{
NavigationManager.NavigateTo("/calendar/event-occurrences/import/settings");
}
}
@@ -1,233 +0,0 @@
@page "/calendar/event-occurrences/import/settings"
@attribute [Authorize(Roles = AuthRoles.Administrator)]
@using Core.Models
@using WebApp.Authentication
@using WebApp.Components.Shared.Components
@using System.Text.Json
@using MudBlazor
@inject IWebHostEnvironment Environment
@inject IConfiguration Configuration
@inject NavigationManager NavigationManager
@inject ISnackbar Snackbar
@rendermode InteractiveServer
<PageHeader
Title="Location Parsing Settings"
Description="Configure location prefix patterns for parsing event occurrence locations. Patterns use '*' as a wildcard (e.g., 'Room *' matches 'Room 101', 'Room 202', etc.)."
ShowBackButton="true"
BackButtonUrl="/calendar/event-occurrences/import" />
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-4">
@if (_config != null)
{
<MudPaper Class="pa-6 mb-4">
<MudText Typo="Typo.h5" Class="mb-4">Location Patterns</MudText>
<MudText Typo="Typo.body2" Class="mb-4" Color="Color.Secondary">
Add prefix patterns to match location names. Use '*' as a wildcard to match any text after the prefix.
Examples: "Room *" matches "Room 101", "Room 202"; "Hall *" matches "Hall A", "Main Hall".
</MudText>
<MudStack Spacing="2">
@for (int i = 0; i < _config.LocationPatterns.Count; i++)
{
var index = i;
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
<MudTextField @bind-Value="_config.LocationPatterns[index]"
Label="Pattern"
Variant="Variant.Outlined"
Style="flex-grow: 1;"
Placeholder="Room *"
HelperText="Use * as wildcard" />
<MudButton Variant="Variant.Text"
Color="Color.Error"
StartIcon="@Icons.Material.Filled.Delete"
OnClick="@(() => RemovePattern(index))"
Disabled="_isSaving">
Remove
</MudButton>
</MudStack>
}
<MudButton Variant="Variant.Outlined"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Add"
OnClick="AddPattern"
Disabled="_isSaving"
Class="mt-2">
Add Pattern
</MudButton>
</MudStack>
</MudPaper>
<MudPaper Class="pa-6 mb-4">
<MudText Typo="Typo.h6" Class="mb-3">Pattern Examples</MudText>
<MudSimpleTable Dense="true">
<thead>
<tr>
<th>Pattern</th>
<th>Matches</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Room *</code></td>
<td>Room 101, Room 202, Room A</td>
</tr>
<tr>
<td><code>Hall *</code></td>
<td>Hall A, Hall B, Main Hall</td>
</tr>
<tr>
<td><code>Conference Room *</code></td>
<td>Conference Room A, Conference Room 1</td>
</tr>
<tr>
<td><code>Building *</code></td>
<td>Building 1, Building A, Building Main</td>
</tr>
</tbody>
</MudSimpleTable>
</MudPaper>
<MudPaper Class="pa-6">
<MudGrid>
<MudItem xs="12">
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Save"
OnClick="SaveConfiguration"
Disabled="_isSaving">
@if (_isSaving)
{
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
<span>Saving...</span>
}
else
{
<span>Save Configuration</span>
}
</MudButton>
<MudButton Variant="Variant.Text"
Class="ml-2"
OnClick="Cancel"
Disabled="_isSaving">
Cancel
</MudButton>
</MudItem>
</MudGrid>
</MudPaper>
@if (!string.IsNullOrEmpty(_statusMessage))
{
<MudAlert Severity="@_statusSeverity" Class="mt-4">@_statusMessage</MudAlert>
}
}
else
{
<MudProgressCircular Indeterminate="true" />
}
</MudContainer>
@code {
private LocationParsingConfiguration? _config;
private bool _isSaving;
private string? _statusMessage;
private Severity _statusSeverity = Severity.Success;
protected override void OnInitialized()
{
// Load from IConfiguration
_config = Configuration.GetSection("LocationParsingSettings").Get<LocationParsingConfiguration>()
?? LocationParsingConfiguration.Default;
// Create a copy to avoid modifying the original
_config = new LocationParsingConfiguration
{
LocationPatterns = new List<string>(_config.LocationPatterns)
};
}
private string GetAppSettingsPath()
{
return Path.Combine(
Environment.ContentRootPath,
"Data",
"appsettings.json");
}
private void AddPattern()
{
if (_config == null) return;
_config.LocationPatterns.Add("New Pattern *");
}
private void RemovePattern(int index)
{
if (_config == null || index < 0 || index >= _config.LocationPatterns.Count) return;
_config.LocationPatterns.RemoveAt(index);
}
private async Task SaveConfiguration()
{
if (_config == null) return;
_isSaving = true;
_statusMessage = null;
try
{
var appSettingsPath = GetAppSettingsPath();
// Ensure Data directory exists
var dataDir = Path.GetDirectoryName(appSettingsPath);
if (dataDir != null && !Directory.Exists(dataDir))
{
Directory.CreateDirectory(dataDir);
}
// Read existing appsettings or create new
Dictionary<string, object?> settings;
if (File.Exists(appSettingsPath))
{
var existingJson = await File.ReadAllTextAsync(appSettingsPath);
settings = JsonSerializer.Deserialize<Dictionary<string, object?>>(existingJson)
?? new Dictionary<string, object?>();
}
else
{
settings = new Dictionary<string, object?>();
}
// Update LocationParsingSettings section
settings["LocationParsingSettings"] = _config;
// Write back to file
var options = new JsonSerializerOptions { WriteIndented = true };
var json = JsonSerializer.Serialize(settings, options);
await File.WriteAllTextAsync(appSettingsPath, json);
_statusMessage = "Configuration saved successfully! Changes will take effect on next parse operation.";
_statusSeverity = Severity.Success;
Snackbar.Add("Location parsing settings saved successfully", Severity.Success);
}
catch (Exception ex)
{
_statusMessage = $"Error saving configuration: {ex.Message}";
_statusSeverity = Severity.Error;
Snackbar.Add($"Error saving settings: {ex.Message}", Severity.Error);
}
finally
{
_isSaving = false;
}
}
private void Cancel()
{
NavigationManager.NavigateTo("/calendar/event-occurrences/import");
}
}
-5
View File
@@ -45,11 +45,6 @@ if (!File.Exists(dataAppSettingsPath))
templateSettings["ValidationSettings"] = JsonSerializer.Deserialize<object>(validationSettings.GetRawText());
}
if (baseDoc.RootElement.TryGetProperty("LocationParsingSettings", out var locationParsingSettings))
{
templateSettings["LocationParsingSettings"] = JsonSerializer.Deserialize<object>(locationParsingSettings.GetRawText());
}
if (templateSettings.Any())
{
var json = JsonSerializer.Serialize(templateSettings, new JsonSerializerOptions
-15
View File
@@ -35,20 +35,5 @@
"EventCountSeverity": "Warning",
"MissingCaptainSeverity": "Warning",
"TooManyRegionalEventsSeverity": "Warning"
},
"LocationParsingSettings": {
"LocationPatterns": [
"Room *",
"Hall *",
"Conference Room *",
"Building *",
"Auditorium *",
"Exhibit Hall *",
"Mtg. Room *",
"Meeting Room *",
"Banquet Room *",
"Online",
"Virtual"
]
}
}