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:
@@ -1,39 +0,0 @@
|
|||||||
namespace Core.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configuration for location parsing patterns used in event occurrence parsing.
|
|
||||||
/// Supports venue-specific room naming conventions.
|
|
||||||
/// </summary>
|
|
||||||
public class LocationParsingConfiguration
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// List of location prefix patterns (e.g., ["Room *", "Hall *", "Conference Room *"]).
|
|
||||||
/// Patterns use "*" as wildcard to match any text after the prefix.
|
|
||||||
/// </summary>
|
|
||||||
public List<string> LocationPatterns { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default location parsing configuration with common patterns.
|
|
||||||
/// </summary>
|
|
||||||
public static LocationParsingConfiguration Default => new()
|
|
||||||
{
|
|
||||||
LocationPatterns = new List<string>
|
|
||||||
{
|
|
||||||
"Room *",
|
|
||||||
"Hall *",
|
|
||||||
"Exhibit Hall *",
|
|
||||||
"Conference Room *",
|
|
||||||
"Building *",
|
|
||||||
"Auditorium *",
|
|
||||||
"Mtg. Room *",
|
|
||||||
"Meeting Room *",
|
|
||||||
"Banquet Room *",
|
|
||||||
"Banquet Hall *",
|
|
||||||
"Online",
|
|
||||||
"Virtual",
|
|
||||||
"TBD"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace Core.Parsers.EventOccurrence;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Matches location strings against configurable patterns.
|
|
||||||
/// Supports exact matches and wildcard patterns (e.g., "Room *", "Exhibit Hall *").
|
|
||||||
/// </summary>
|
|
||||||
public static class LocationPatternMatcher
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Matches location text against configured patterns and returns the matched location.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="locationText">The location text to match.</param>
|
|
||||||
/// <param name="patterns">The list of patterns to match against.</param>
|
|
||||||
/// <returns>The matched location if a pattern matches, or empty string if no match is found.</returns>
|
|
||||||
public static string Match(string locationText, IReadOnlyList<string> patterns)
|
|
||||||
{
|
|
||||||
// Normalize location text for matching (trim and handle variations)
|
|
||||||
var normalizedLocation = locationText.Trim();
|
|
||||||
|
|
||||||
// If location is empty after normalization, return empty
|
|
||||||
if (string.IsNullOrWhiteSpace(normalizedLocation))
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
foreach (var pattern in patterns)
|
|
||||||
{
|
|
||||||
var normalizedPattern = pattern.Trim();
|
|
||||||
|
|
||||||
// Skip empty patterns
|
|
||||||
if (string.IsNullOrWhiteSpace(normalizedPattern))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Handle exact matches (patterns without wildcards like "Online", "Virtual", "TBD")
|
|
||||||
if (!normalizedPattern.Contains('*'))
|
|
||||||
{
|
|
||||||
if (string.Equals(normalizedPattern, normalizedLocation, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return normalizedLocation;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert pattern to regex: escape special chars, replace * with .*
|
|
||||||
// This handles patterns like "Exhibit Hall *", "Room *", "Mtg. Room *", etc.
|
|
||||||
var escapedPattern = Regex.Escape(normalizedPattern);
|
|
||||||
escapedPattern = escapedPattern.Replace(@"\*", ".*?");
|
|
||||||
|
|
||||||
// Use case-insensitive matching
|
|
||||||
// Note: For dynamic patterns, we compile on demand. This is acceptable since patterns
|
|
||||||
// are configured and reused across parsing sessions.
|
|
||||||
var regex = new Regex($"^{escapedPattern}$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
||||||
if (regex.IsMatch(normalizedLocation))
|
|
||||||
{
|
|
||||||
return normalizedLocation; // Return the full matched location
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using Core.Entities;
|
using Core.Entities;
|
||||||
using Core.Models;
|
using Core.Models;
|
||||||
using Core.Parsers;
|
using Core.Parsers;
|
||||||
@@ -93,6 +94,9 @@ public class EventOccurrenceParserService : IEventOccurrenceParserService
|
|||||||
|
|
||||||
// Copy skipped HS section headers from parser result
|
// Copy skipped HS section headers from parser result
|
||||||
result.SkippedHSSectionHeaders.AddRange(parserResult.SkippedHSSectionHeaders);
|
result.SkippedHSSectionHeaders.AddRange(parserResult.SkippedHSSectionHeaders);
|
||||||
|
|
||||||
|
// Validate locations and add warnings for problematic ones
|
||||||
|
ValidateLocations(result);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -121,5 +125,48 @@ public class EventOccurrenceParserService : IEventOccurrenceParserService
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates locations from parsed occurrences and adds warnings for problematic locations.
|
||||||
|
/// </summary>
|
||||||
|
private static void ValidateLocations(EventOccurrenceParseResult result)
|
||||||
|
{
|
||||||
|
// Collect all unique locations
|
||||||
|
var locations = result.Occurrences.Values
|
||||||
|
.SelectMany(list => list)
|
||||||
|
.Select(eo => eo.Location)
|
||||||
|
.Where(loc => !string.IsNullOrWhiteSpace(loc))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!locations.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check for long locations (>50 chars)
|
||||||
|
var longLocations = locations.Where(loc => loc != null && loc.Length > 50).ToList();
|
||||||
|
foreach (var loc in longLocations)
|
||||||
|
{
|
||||||
|
if (loc != null)
|
||||||
|
{
|
||||||
|
result.Warnings.Add($"Location '{loc}' is unusually long ({loc.Length} characters) and may contain multiple lines or extra text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for date/time patterns
|
||||||
|
// Pattern matches: month names with day numbers, time patterns (HH:MM AM/PM), and NOON
|
||||||
|
var dateTimePattern = new 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",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
var locationsWithDateTime = locations.Where(loc => loc != null && dateTimePattern.IsMatch(loc)).ToList();
|
||||||
|
foreach (var loc in locationsWithDateTime)
|
||||||
|
{
|
||||||
|
if (loc != null)
|
||||||
|
{
|
||||||
|
var match = dateTimePattern.Match(loc);
|
||||||
|
result.Warnings.Add($"Location '{loc}' may contain date/time information: '{match.Value}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
using Core.Parsers.EventOccurrence;
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace Tests.Parsers.EventOccurrence;
|
|
||||||
|
|
||||||
[TestFixture]
|
|
||||||
public class LocationPatternMatcher_Tests
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void Match_ExactMatch_ReturnsLocation()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room A", "Room B", "Hall C" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Room A", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.EqualTo("Room A"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_ExactMatch_CaseInsensitive_ReturnsLocation()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room A" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("room a", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.EqualTo("room a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_WildcardPattern_Matches_ReturnsLocation()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room *", "Hall *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Room 101", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.EqualTo("Room 101"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_WildcardPattern_MultipleMatches_ReturnsFirstMatch()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room *", "Exhibit Hall *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Room 202", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.EqualTo("Room 202"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_WildcardPattern_ExhibitHall_Matches()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Exhibit Hall *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Exhibit Hall C", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.EqualTo("Exhibit Hall C"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_WildcardPattern_MtgRoom_Matches()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Mtg. Room *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Mtg. Room 14", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.EqualTo("Mtg. Room 14"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_NoMatch_ReturnsEmpty()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room *", "Hall *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Unknown Location", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_EmptyLocation_ReturnsEmpty()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_WhitespaceLocation_ReturnsEmpty()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match(" ", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_EmptyPatterns_ReturnsEmpty()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Room A", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_PatternWithSpecialCharacters_EscapesCorrectly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room (A)" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match("Room (A)", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.EqualTo("Room (A)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Match_LocationWithWhitespace_Trims_ReturnsLocation()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var patterns = new List<string> { "Room *" };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = LocationPatternMatcher.Match(" Room 101 ", patterns);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
// LocationPatternMatcher returns the matched location after normalization (trim)
|
|
||||||
Assert.That(result, Is.EqualTo("Room 101"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Core.Entities;
|
using Core.Entities;
|
||||||
using Core.Models;
|
|
||||||
using Tests.Builders;
|
using Tests.Builders;
|
||||||
|
|
||||||
namespace Tests.Parsers;
|
namespace Tests.Parsers;
|
||||||
@@ -27,17 +26,6 @@ public static class EventOccurrenceParserTestHelpers
|
|||||||
return EventDefinitionBuilder.Individual(name).Build();
|
return EventDefinitionBuilder.Individual(name).Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a LocationParsingConfiguration for testing.
|
|
||||||
/// </summary>
|
|
||||||
public static LocationParsingConfiguration CreateLocationConfig(params string[] patterns)
|
|
||||||
{
|
|
||||||
return new LocationParsingConfiguration
|
|
||||||
{
|
|
||||||
LocationPatterns = patterns.ToList()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cleans up a temporary file.
|
/// Cleans up a temporary file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -21,17 +21,7 @@
|
|||||||
<MudGrid>
|
<MudGrid>
|
||||||
<MudItem xs="12" md="6">
|
<MudItem xs="12" md="6">
|
||||||
<MudPaper Elevation="2" Class="pa-3 pa-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" Class="mb-4">Paste Event Occurrence Data</MudText>
|
||||||
<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>
|
|
||||||
<MudStack Spacing="3">
|
<MudStack Spacing="3">
|
||||||
|
|
||||||
<MudStack Row="true" Spacing="2">
|
<MudStack Row="true" Spacing="2">
|
||||||
@@ -138,6 +128,53 @@
|
|||||||
</MudAlert>
|
</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 *@
|
@* Parsed Occurrences List *@
|
||||||
@if (_parseResult.IsSuccess && _parseResult.Occurrences.Any())
|
@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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -45,11 +45,6 @@ if (!File.Exists(dataAppSettingsPath))
|
|||||||
templateSettings["ValidationSettings"] = JsonSerializer.Deserialize<object>(validationSettings.GetRawText());
|
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())
|
if (templateSettings.Any())
|
||||||
{
|
{
|
||||||
var json = JsonSerializer.Serialize(templateSettings, new JsonSerializerOptions
|
var json = JsonSerializer.Serialize(templateSettings, new JsonSerializerOptions
|
||||||
|
|||||||
@@ -35,20 +35,5 @@
|
|||||||
"EventCountSeverity": "Warning",
|
"EventCountSeverity": "Warning",
|
||||||
"MissingCaptainSeverity": "Warning",
|
"MissingCaptainSeverity": "Warning",
|
||||||
"TooManyRegionalEventsSeverity": "Warning"
|
"TooManyRegionalEventsSeverity": "Warning"
|
||||||
},
|
|
||||||
"LocationParsingSettings": {
|
|
||||||
"LocationPatterns": [
|
|
||||||
"Room *",
|
|
||||||
"Hall *",
|
|
||||||
"Conference Room *",
|
|
||||||
"Building *",
|
|
||||||
"Auditorium *",
|
|
||||||
"Exhibit Hall *",
|
|
||||||
"Mtg. Room *",
|
|
||||||
"Meeting Room *",
|
|
||||||
"Banquet Room *",
|
|
||||||
"Online",
|
|
||||||
"Virtual"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user