@page "/calendar/event-occurrences/import"
@attribute [Authorize]
@using Core.Models
@using Core.Services
@using Microsoft.EntityFrameworkCore
@inject IEventOccurrenceParserService ParserService
@inject AppDbContext Context
@inject NavigationManager NavigationManager
@inject ISnackbar Snackbar
@inject IDialogService DialogService
Paste Event Occurrence Data
Parse
Clear
Parsed Results
@if (_isParsing)
{
Parsing...
}
else if (_parseResult == null)
{
Parse text to see results here
}
else
{
@* Errors *@
@if (_parseResult.Errors.Any())
{
@foreach (var error in _parseResult.Errors)
{
@error
}
}
@* Warnings *@
@if (_parseResult.Warnings.Any())
{
@foreach (var warning in _parseResult.Warnings)
{
@warning
}
}
@* Detailed Parsing Issues *@
@if (_parseResult.Issues.Any())
{
i.LineNumber).Distinct().Count()} line(s))")"
Icon="@Icons.Material.Filled.Warning"
iconcolor="Color.Warning">
Line
Type
Content
Message
@context.LineNumber
@context.IssueType
@context.LineContent
@context.Message
}
@* Summary *@
@if (_parseResult.IsSuccess && _parseResult.TotalParsed > 0)
{
Successfully parsed @_parseResult.TotalParsed occurrence(s) from @_parseResult.Occurrences.Count event definition(s)
@if (_parseResult.SkippedEventCount > 0)
{
(Skipped @_parseResult.SkippedEventCount event occurrence(s) from other school level)
}
}
@* Skipped Section Headers *@
@if (_parseResult.SkippedSectionHeaders.Any() && _parseResult.IsSuccess)
{
@foreach (var header in _parseResult.SkippedSectionHeaders)
{
@header
}
}
@* 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))
.Cast() // Cast to non-nullable string after null check
.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;
Parsed Locations (@allLocations.Count unique@(warningCount > 0 ? $", {warningCount} with warnings" : ""))
@foreach (var location in allLocations)
{
var isLong = longLocations.Contains(location);
var hasDateTime = locationsWithDateTime.Contains(location);
var hasWarning = isLong || hasDateTime;
@if (hasWarning)
{
Warning
}
@location
}
}
}
@* Parsed Occurrences List *@
@if (_parseResult.IsSuccess && _parseResult.Occurrences.Any())
{
Occurrences by Event:
@foreach (var kvp in _parseResult.Occurrences.OrderBy(x => GetEventName(x.Key)))
{
Name
Date
Time
Location
@context.Name
@context.Date
@context.Time
@(context.Location ?? "-")
}
Save to Database
Clear Results
}
else if (_parseResult.IsSuccess && _parseResult.TotalParsed == 0)
{
No occurrences were parsed from the text. Please check the format.
}
}
@code {
private string _inputText = string.Empty;
private EventOccurrenceParseResult? _parseResult;
private bool _isParsing = false;
private bool _isSaving = false;
private async Task HandleParse()
{
if (string.IsNullOrWhiteSpace(_inputText))
{
Snackbar.Add("Please enter text to parse", Severity.Warning);
return;
}
_isParsing = true;
try
{
// Get EventDefinitions from database
var events = await Context.Events.ToListAsync();
// Parse the text
_parseResult = ParserService.ParseFromText(_inputText, events);
}
catch (Exception ex)
{
Snackbar.Add($"Error parsing text: {ex.Message}", Severity.Error);
_parseResult = new EventOccurrenceParseResult
{
Errors = { $"Error: {ex.Message}" }
};
}
finally
{
_isParsing = false;
}
}
private void HandleClear()
{
_inputText = string.Empty;
_parseResult = null;
}
private void HandleClearResults()
{
_parseResult = null;
}
private async Task HandleSaveToDatabase()
{
if (_parseResult == null || !_parseResult.IsSuccess || _parseResult.TotalParsed == 0)
{
Snackbar.Add("No valid parsed occurrences to save", Severity.Warning);
return;
}
_isSaving = true;
try
{
var savedCount = 0;
var skippedCount = 0;
foreach (var kvp in _parseResult.Occurrences)
{
foreach (var occurrence in kvp.Value)
{
// Check for duplicates before adding
var isDuplicate = await IsDuplicate(occurrence);
if (isDuplicate)
{
skippedCount++;
continue;
}
// Add each occurrence to the database
await Context.EventOccurrences.AddAsync(occurrence);
savedCount++;
}
}
await Context.SaveChangesAsync();
var message = $"Successfully saved {savedCount} occurrence(s) to database";
if (skippedCount > 0)
{
message += $" ({skippedCount} duplicate(s) skipped)";
}
Snackbar.Add(message, Severity.Success);
// Clear input and output instead of navigating
_inputText = string.Empty;
_parseResult = null;
}
catch (Exception ex)
{
Snackbar.Add($"Error saving to database: {ex.Message}", Severity.Error);
}
finally
{
_isSaving = false;
}
}
private async Task IsDuplicate(EventOccurrence occurrence)
{
// Check if an occurrence with the same name, date, time, location, and event definition already exists
var query = Context.EventOccurrences
.Where(eo => eo.Name == occurrence.Name &&
eo.StartTime.Date == occurrence.StartTime.Date &&
eo.Time == occurrence.Time &&
eo.Location == occurrence.Location);
// Match by EventDefinitionId if it exists, otherwise match by SpecialEventType
if (occurrence.EventDefinitionId.HasValue)
{
query = query.Where(eo => eo.EventDefinitionId == occurrence.EventDefinitionId);
}
else if (!string.IsNullOrEmpty(occurrence.SpecialEventType))
{
query = query.Where(eo => eo.SpecialEventType == occurrence.SpecialEventType);
}
else
{
// If neither EventDefinitionId nor SpecialEventType is set, match by both being null/empty
query = query.Where(eo => eo.EventDefinitionId == null && string.IsNullOrEmpty(eo.SpecialEventType));
}
return await query.AnyAsync();
}
private string GetEventName(EventDefinition eventDefinition)
{
if (eventDefinition == EventDefinition.GeneralSchedule)
return "General Schedule";
if (eventDefinition == EventDefinition.MeetTheCandidates)
return "Meet the Candidates";
if (eventDefinition == EventDefinition.ChapterOfficerMeeting)
return "Chapter Officer Meeting";
if (eventDefinition == EventDefinition.VotingDelegateMeeting)
return "Voting Delegate Meeting";
if (eventDefinition == EventDefinition.SocialGathering)
return "Social Gathering";
return eventDefinition.Name;
}
private Color GetIssueTypeColor(ParsingIssueType issueType)
{
return issueType switch
{
ParsingIssueType.UnmatchedLine => Color.Info,
ParsingIssueType.MissingEventDefinition => Color.Warning,
ParsingIssueType.TimeParseFailure => Color.Error,
ParsingIssueType.DateParseFailure => Color.Error,
ParsingIssueType.InvalidFormat => Color.Error,
_ => Color.Default
};
}
}