Compare commits
20
Commits
9668ec162d
...
01056401e5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01056401e5 | ||
|
|
83522ac52c | ||
|
|
c6fb00c7f4 | ||
|
|
3bd076afb3 | ||
|
|
1d3167710d | ||
|
|
7266ab609b | ||
|
|
2c9aa1c223 | ||
|
|
065a83442c | ||
|
|
0358763601 | ||
|
|
06b2db0b4c | ||
|
|
8967d0f8a4 | ||
|
|
d0fd7469af | ||
|
|
f144f0f8f0 | ||
|
|
62b6ae06fd | ||
|
|
b3d7577c32 | ||
|
|
c9ef169989 | ||
|
|
c462ed4561 | ||
|
|
cd34be1f82 | ||
|
|
3a809f18a6 | ||
|
|
eaefdfaedd |
@@ -0,0 +1,19 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Core.Entities;
|
||||||
|
|
||||||
|
public class Career
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(200, MinimumLength = 1)]
|
||||||
|
[Display(Name = "Career Name")]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
namespace Core.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a career field cluster that groups related careers together.
|
||||||
|
/// </summary>
|
||||||
|
public class CareerField
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unique identifier for the career field (1-25)
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Display name of the career field
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Short description of the career field
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exact career names that belong to this field
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<string> DirectCareerMatches { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keywords for pattern matching (case-insensitive substring matching)
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<string> PatternKeywords { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new CareerField instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Unique identifier</param>
|
||||||
|
/// <param name="name">Display name</param>
|
||||||
|
/// <param name="description">Short description of the career field</param>
|
||||||
|
/// <param name="directCareerMatches">Exact career name matches</param>
|
||||||
|
/// <param name="patternKeywords">Keywords for pattern matching</param>
|
||||||
|
public CareerField(int id, string name, string description, IReadOnlyList<string> directCareerMatches, IReadOnlyList<string> patternKeywords)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
Description = description;
|
||||||
|
DirectCareerMatches = directCareerMatches ?? Array.Empty<string>();
|
||||||
|
PatternKeywords = patternKeywords ?? Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ public class EventDefinition
|
|||||||
? MinTeamSize.ToString()
|
? MinTeamSize.ToString()
|
||||||
: $"{MinTeamSize.ToString()}-{MaxTeamSize.ToString()}";
|
: $"{MinTeamSize.ToString()}-{MaxTeamSize.ToString()}";
|
||||||
|
|
||||||
[StringLength(100, MinimumLength = 3)]
|
[StringLength(100)]
|
||||||
public string? SemifinalistActivity { get; set; }
|
public string? SemifinalistActivity { get; set; }
|
||||||
|
|
||||||
public bool InterviewOrPresentation
|
public bool InterviewOrPresentation
|
||||||
@@ -53,7 +53,7 @@ public class EventDefinition
|
|||||||
// || Name.Contains("Chapter")
|
// || Name.Contains("Chapter")
|
||||||
// || Name.Contains("Podcast"));
|
// || Name.Contains("Podcast"));
|
||||||
|
|
||||||
[StringLength(1024, MinimumLength = 3)]
|
[StringLength(1024)]
|
||||||
public string? Notes { get; set; }
|
public string? Notes { get; set; }
|
||||||
|
|
||||||
[Range(0, 3)]
|
[Range(0, 3)]
|
||||||
@@ -79,12 +79,20 @@ public class EventDefinition
|
|||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public int? LevelOfEffort { get; set; }
|
public int? LevelOfEffort { get; set; }
|
||||||
|
|
||||||
|
public ICollection<Career> RelatedCareers { get; set; } = new List<Career>();
|
||||||
|
|
||||||
|
[System.ComponentModel.DataAnnotations.Schema.NotMapped]
|
||||||
|
public string? RelatedCareersText { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly EventDefinition GeneralSchedule = new(){Name = "General Schedule"};
|
public static readonly EventDefinition GeneralSchedule = new(){Name = "General Schedule"};
|
||||||
public static readonly EventDefinition VotingDelegates = new(){Name = "Voting Delegates"};
|
public static readonly EventDefinition MeetTheCandidates = new(){Name = "Meet the Candidates"};
|
||||||
|
public static readonly EventDefinition ChapterOfficerMeeting = new(){Name = "Chapter Officer Meeting"};
|
||||||
|
public static readonly EventDefinition VotingDelegateMeeting = new(){Name = "Voting Delegate Meeting"};
|
||||||
|
public static readonly EventDefinition SocialGathering = new(){Name = "Social Gathering"};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,22 @@
|
|||||||
|
namespace Core.Entities
|
||||||
namespace Core.Entities
|
|
||||||
{
|
{
|
||||||
public class EventOccurrence
|
public class EventOccurrence
|
||||||
{
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int? EventDefinitionId { get; set; }
|
||||||
|
|
||||||
|
public string? SpecialEventType { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; } = null!;
|
public string Name { get; set; } = null!;
|
||||||
public string Time { get; set; } = null!;
|
public string Time { get; set; } = null!;
|
||||||
public string Date { get; set; } = null!;
|
public string Date { get; set; } = null!;
|
||||||
public DateTime StartTime { get; set; }
|
public DateTime StartTime { get; set; }
|
||||||
public DateTime? EndTime { get; set; }
|
public DateTime? EndTime { get; set; }
|
||||||
public string Location { get; set; } = null!;
|
public string? Location { get; set; }
|
||||||
|
|
||||||
|
// Navigation property
|
||||||
|
public EventDefinition? EventDefinition { get; set; }
|
||||||
|
|
||||||
public bool SignupSubmitPickup =>
|
public bool SignupSubmitPickup =>
|
||||||
Name.Contains("Sign-up") ||
|
Name.Contains("Sign-up") ||
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
|
||||||
|
namespace Core.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of parsing event occurrence text data.
|
||||||
|
/// Contains parsed occurrences, errors, and warnings.
|
||||||
|
/// </summary>
|
||||||
|
public class EventOccurrenceParseResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dictionary of parsed event occurrences, keyed by EventDefinition.
|
||||||
|
/// For special events (GeneralSchedule, MeetTheCandidates, ChapterOfficerMeeting, VotingDelegateMeeting, SocialGathering),
|
||||||
|
/// the EventDefinition key will be the static instance.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<EventDefinition, List<EventOccurrence>> Occurrences { get; set; } = new Dictionary<EventDefinition, List<EventOccurrence>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of parsing errors (critical issues that prevented parsing).
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Errors { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of parsing warnings (non-critical issues that occurred during parsing).
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Warnings { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of event occurrences successfully parsed.
|
||||||
|
/// </summary>
|
||||||
|
public int TotalParsed => Occurrences.Values.Sum(list => list.Count);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether parsing was successful (no errors).
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSuccess => Errors.Count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -52,33 +52,31 @@ public class EventOccurrenceParser
|
|||||||
currentEventDefinition = evt;
|
currentEventDefinition = evt;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line == "General Schedule")
|
if (line == "General Schedule" || line == "General Session")
|
||||||
{
|
{
|
||||||
currentEventDefinition = EventDefinition.GeneralSchedule;
|
currentEventDefinition = EventDefinition.GeneralSchedule;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line == "Voting Delegates")
|
// "Voting Delegates" section header is no longer used - occurrences are categorized by name pattern
|
||||||
{
|
// Continue without setting currentEventDefinition for this section
|
||||||
currentEventDefinition = EventDefinition.VotingDelegates;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentEventDefinition == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var occurrenceName = match.Groups["Name"].Captures[0].Value;
|
var occurrenceName = match.Groups["Name"].Captures[0].Value;
|
||||||
var month = match.Groups["Month"].Captures[0].Value;
|
var month = match.Groups["Month"].Captures[0].Value;
|
||||||
var dayOfMonth = match.Groups["DayOfMonth"].Captures[0].Value;
|
var dayOfMonth = match.Groups["DayOfMonth"].Captures[0].Value;
|
||||||
var timeAndLocation = match.Groups["TimeAndLocation"].Captures[0].Value;
|
var timeAndLocation = match.Groups["TimeAndLocation"].Captures[0].Value;
|
||||||
|
|
||||||
|
|
||||||
occurrenceName = Regex.Replace(occurrenceName,
|
occurrenceName = Regex.Replace(occurrenceName,
|
||||||
@"(?<Weekday>Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\s?$", "").Trim();
|
@"(?<Weekday>Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\s?$", "").Trim();
|
||||||
|
|
||||||
|
// Determine event definition based on occurrence name pattern or current section
|
||||||
|
EventDefinition? eventDefinition = DetermineEventDefinition(occurrenceName, currentEventDefinition);
|
||||||
|
|
||||||
|
// Skip if we can't determine the event definition
|
||||||
|
if (eventDefinition == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
timeAndLocation = SanitizeInput(timeAndLocation);
|
timeAndLocation = SanitizeInput(timeAndLocation);
|
||||||
var timeAndLocationMatch = _timeLocationRegex.Match(timeAndLocation);
|
var timeAndLocationMatch = _timeLocationRegex.Match(timeAndLocation);
|
||||||
@@ -103,14 +101,41 @@ public class EventOccurrenceParser
|
|||||||
Location = location
|
Location = location
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!occurrences.ContainsKey(currentEventDefinition))
|
if (!occurrences.ContainsKey(eventDefinition))
|
||||||
occurrences.Add(currentEventDefinition, []);
|
occurrences.Add(eventDefinition, []);
|
||||||
occurrences[currentEventDefinition].Add(eventOccurrence);
|
occurrences[eventDefinition].Add(eventOccurrence);
|
||||||
}
|
}
|
||||||
|
|
||||||
return occurrences;
|
return occurrences;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the EventDefinition for an occurrence based on its name pattern or current section context.
|
||||||
|
/// </summary>
|
||||||
|
private EventDefinition? DetermineEventDefinition(string occurrenceName, EventDefinition? currentEventDefinition)
|
||||||
|
{
|
||||||
|
// Check for special event name patterns first (regardless of current section)
|
||||||
|
if (occurrenceName.Contains("Meet the Candidates Session", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return EventDefinition.MeetTheCandidates;
|
||||||
|
|
||||||
|
if (occurrenceName.Contains("Chapter Officer Meeting", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return EventDefinition.ChapterOfficerMeeting;
|
||||||
|
|
||||||
|
if (occurrenceName.Contains("Voting Delegate Meeting", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return EventDefinition.VotingDelegateMeeting;
|
||||||
|
|
||||||
|
// If we're in a General Schedule/Session section and no pattern matched, use GeneralSchedule
|
||||||
|
if (currentEventDefinition == EventDefinition.GeneralSchedule)
|
||||||
|
return EventDefinition.GeneralSchedule;
|
||||||
|
|
||||||
|
// If we have a current event definition from section header (e.g., regular events), use it
|
||||||
|
if (currentEventDefinition != null)
|
||||||
|
return currentEventDefinition;
|
||||||
|
|
||||||
|
// Cannot determine event definition
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private string SanitizeInput(string input)
|
private string SanitizeInput(string input)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Core.Entities;
|
||||||
|
using Core.Models;
|
||||||
|
using Core.Parsers;
|
||||||
|
|
||||||
|
namespace Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service implementation for parsing event occurrence text data.
|
||||||
|
/// Wraps EventOccurrenceParser to support text input and error collection.
|
||||||
|
/// </summary>
|
||||||
|
public class EventOccurrenceParserService : IEventOccurrenceParserService
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public EventOccurrenceParseResult ParseFromText(string text, ICollection<EventDefinition> events)
|
||||||
|
{
|
||||||
|
var result = new EventOccurrenceParseResult();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
result.Errors.Add("Input text is empty or whitespace.");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create a temporary file from the text content
|
||||||
|
var tempFile = Path.GetTempFileName();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(tempFile, text, Encoding.UTF8);
|
||||||
|
var fileInfo = new FileInfo(tempFile);
|
||||||
|
|
||||||
|
// Use the existing EventOccurrenceParser
|
||||||
|
var parser = new EventOccurrenceParser(fileInfo, events);
|
||||||
|
var parsedOccurrences = parser.Parse();
|
||||||
|
|
||||||
|
// Convert parsed occurrences to result format, handling special event types
|
||||||
|
foreach (var kvp in parsedOccurrences)
|
||||||
|
{
|
||||||
|
var eventDefinition = kvp.Key;
|
||||||
|
var occurrences = kvp.Value;
|
||||||
|
|
||||||
|
// Check if this is a special event type (not stored in database)
|
||||||
|
if (eventDefinition == EventDefinition.GeneralSchedule ||
|
||||||
|
eventDefinition == EventDefinition.MeetTheCandidates ||
|
||||||
|
eventDefinition == EventDefinition.ChapterOfficerMeeting ||
|
||||||
|
eventDefinition == EventDefinition.VotingDelegateMeeting ||
|
||||||
|
eventDefinition == EventDefinition.SocialGathering)
|
||||||
|
{
|
||||||
|
// For special events, set EventDefinitionId to null and set SpecialEventType
|
||||||
|
foreach (var occurrence in occurrences)
|
||||||
|
{
|
||||||
|
occurrence.EventDefinitionId = null;
|
||||||
|
occurrence.SpecialEventType = eventDefinition switch
|
||||||
|
{
|
||||||
|
var ed when ed == EventDefinition.GeneralSchedule => "GeneralSchedule",
|
||||||
|
var ed when ed == EventDefinition.MeetTheCandidates => "MeetTheCandidates",
|
||||||
|
var ed when ed == EventDefinition.ChapterOfficerMeeting => "ChapterOfficerMeeting",
|
||||||
|
var ed when ed == EventDefinition.VotingDelegateMeeting => "VotingDelegateMeeting",
|
||||||
|
var ed when ed == EventDefinition.SocialGathering => "SocialGathering",
|
||||||
|
_ => throw new InvalidOperationException($"Unknown special event type: {eventDefinition.Name}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to result with the special EventDefinition as key
|
||||||
|
result.Occurrences[eventDefinition] = occurrences;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For regular events, set EventDefinitionId and ensure SpecialEventType is null
|
||||||
|
foreach (var occurrence in occurrences)
|
||||||
|
{
|
||||||
|
occurrence.EventDefinitionId = eventDefinition.Id;
|
||||||
|
occurrence.SpecialEventType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Occurrences[eventDefinition] = occurrences;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track any occurrences without a matching EventDefinition
|
||||||
|
// (This would be detected if parser.Parse() returns occurrences with null EventDefinition keys,
|
||||||
|
// but the current parser implementation doesn't do this - all occurrences have a currentEventDefinition)
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Clean up temporary file
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(tempFile))
|
||||||
|
{
|
||||||
|
File.Delete(tempFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Errors.Add($"Error parsing text: {ex.Message}");
|
||||||
|
if (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
result.Errors.Add($"Inner exception: {ex.InnerException.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
using Core.Models;
|
||||||
|
|
||||||
|
namespace Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service interface for parsing event occurrence text data.
|
||||||
|
/// </summary>
|
||||||
|
public interface IEventOccurrenceParserService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parses event occurrence text data and returns parse results with occurrences, errors, and warnings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text content to parse (typically multi-line text from paste/import)</param>
|
||||||
|
/// <param name="events">Collection of EventDefinitions to match against during parsing</param>
|
||||||
|
/// <returns>ParseResult containing parsed occurrences, errors, and warnings</returns>
|
||||||
|
EventOccurrenceParseResult ParseFromText(string text, ICollection<EventDefinition> events);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
|
||||||
|
namespace Core.Utility;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static class that defines all 25 career field clusters and provides methods to match careers to fields.
|
||||||
|
/// </summary>
|
||||||
|
public static class CareerFieldDefinitions
|
||||||
|
{
|
||||||
|
private static readonly Lazy<IReadOnlyList<CareerField>> _allFields = new(() => CreateAllCareerFields());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all 25 career field definitions
|
||||||
|
/// </summary>
|
||||||
|
public static IReadOnlyList<CareerField> GetAllCareerFields() => _allFields.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the career fields that are related to the given careers.
|
||||||
|
/// Uses both direct name matching and pattern-based keyword matching.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="careers">The careers to match against</param>
|
||||||
|
/// <returns>Distinct list of related CareerFields</returns>
|
||||||
|
public static IReadOnlyList<CareerField> GetRelatedCareerFields(IEnumerable<Career> careers)
|
||||||
|
{
|
||||||
|
if (careers == null)
|
||||||
|
return Array.Empty<CareerField>();
|
||||||
|
|
||||||
|
var careerNames = careers
|
||||||
|
.Where(c => !string.IsNullOrWhiteSpace(c.Name))
|
||||||
|
.Select(c => c.Name.Trim())
|
||||||
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (!careerNames.Any())
|
||||||
|
return Array.Empty<CareerField>();
|
||||||
|
|
||||||
|
var matchingFields = new HashSet<CareerField>();
|
||||||
|
var allFields = GetAllCareerFields();
|
||||||
|
|
||||||
|
foreach (var field in allFields)
|
||||||
|
{
|
||||||
|
bool matches = false;
|
||||||
|
|
||||||
|
// Check direct matches
|
||||||
|
foreach (var directMatch in field.DirectCareerMatches)
|
||||||
|
{
|
||||||
|
if (careerNames.Contains(directMatch))
|
||||||
|
{
|
||||||
|
matches = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pattern matches if no direct match found
|
||||||
|
if (!matches)
|
||||||
|
{
|
||||||
|
foreach (var keyword in field.PatternKeywords)
|
||||||
|
{
|
||||||
|
if (careerNames.Any(name => name.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
matches = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches)
|
||||||
|
{
|
||||||
|
matchingFields.Add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingFields.OrderBy(f => f.Id).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<CareerField> CreateAllCareerFields()
|
||||||
|
{
|
||||||
|
return new List<CareerField>
|
||||||
|
{
|
||||||
|
// 1. Aerospace & Automotive Engineering
|
||||||
|
new CareerField(
|
||||||
|
1,
|
||||||
|
"Aerospace & Automotive Engineering",
|
||||||
|
"Careers focused on designing and engineering aircraft, spacecraft, and vehicles for transportation.",
|
||||||
|
new[] { "Aeronautical engineer", "Aircraft systems engineer", "Automobile designer", "Automotive designer", "Automotive modeler", "Race car engineer" },
|
||||||
|
new[] { "aeronautical", "aircraft", "automobile", "automotive", "race car" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 2. Mechanical & Robotics Engineering
|
||||||
|
new CareerField(
|
||||||
|
2,
|
||||||
|
"Mechanical & Robotics Engineering",
|
||||||
|
"Engineering disciplines involving mechanical systems, machinery design, and automated robotic systems.",
|
||||||
|
new[] { "Machine designer", "Mechanical drafter", "Mechanical engineer", "Robotics engineer" },
|
||||||
|
new[] { "mechanical", "robotics", "machine" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 3. Electrical & Electronics Engineering
|
||||||
|
new CareerField(
|
||||||
|
3,
|
||||||
|
"Electrical & Electronics Engineering",
|
||||||
|
"Careers involving electrical systems, circuits, and electronic device design and maintenance.",
|
||||||
|
new[] { "Electrical engineer", "Electrical technician", "Electrician", "Electromechanical engineer", "Electronic analyst", "Electronic designer" },
|
||||||
|
new[] { "electrical", "electronic", "electrician" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 4. Civil & Structural Engineering
|
||||||
|
new CareerField(
|
||||||
|
4,
|
||||||
|
"Civil & Structural Engineering",
|
||||||
|
"Engineering fields focused on infrastructure, buildings, bridges, and construction project management.",
|
||||||
|
new[] { "Civil engineer", "Construction analyst", "Construction manager", "General contractor", "Structural engineer", "Structural iron and steel work technician" },
|
||||||
|
new[] { "civil", "construction", "structural", "contractor" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 5. Environmental & Energy Engineering
|
||||||
|
new CareerField(
|
||||||
|
5,
|
||||||
|
"Environmental & Energy Engineering",
|
||||||
|
"Engineering careers focused on sustainable energy solutions, environmental protection, and chemical processes.",
|
||||||
|
new[] { "Chemical engineer", "Energy efficiency technician", "Environmental engineer", "Solar engineer", "Solar panel installer", "Solar sales consultant" },
|
||||||
|
new[] { "chemical", "energy", "environmental", "solar" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 6. General Engineering & Quality
|
||||||
|
new CareerField(
|
||||||
|
6,
|
||||||
|
"General Engineering & Quality",
|
||||||
|
"Broad engineering roles including management, quality assurance, and standards compliance across various industries.",
|
||||||
|
new[] { "Engineer", "Engineering manager", "Engineering technician", "Quality assurance engineer", "Quality engineer", "Standards engineer" },
|
||||||
|
new[] { "engineer", "quality", "standards" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 7. Architecture & Urban Planning
|
||||||
|
new CareerField(
|
||||||
|
7,
|
||||||
|
"Architecture & Urban Planning",
|
||||||
|
"Design and planning careers focused on buildings, spaces, and community development.",
|
||||||
|
new[] { "Architect", "Community planner", "Interior designer", "Urban and regional planner" },
|
||||||
|
new[] { "architect", "planner", "interior design", "urban" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 8. Software Development
|
||||||
|
new CareerField(
|
||||||
|
8,
|
||||||
|
"Software Development",
|
||||||
|
"Careers in creating, designing, and developing computer software applications and systems.",
|
||||||
|
new[] { "Computer programmer", "Computer software engineer", "Programming & software development", "Software designer", "Software engineer" },
|
||||||
|
new[] { "programming", "programmer", "software", "developer" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 9. IT & Networking
|
||||||
|
new CareerField(
|
||||||
|
9,
|
||||||
|
"IT & Networking",
|
||||||
|
"Information technology careers involving computer systems, networks, technical support, and telecommunications.",
|
||||||
|
new[] { "Computer engineer", "Computer network specialist", "Computer technician", "Information support & services", "Network systems", "Technical support specialist", "Telecommunications manager" },
|
||||||
|
new[] { "network", "computer", "technical support", "telecommunications", "IT" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 10. Cybersecurity & Digital Forensics
|
||||||
|
new CareerField(
|
||||||
|
10,
|
||||||
|
"Cybersecurity & Digital Forensics",
|
||||||
|
"Security-focused careers protecting digital systems, investigating cybercrimes, and ensuring information security.",
|
||||||
|
new[] { "Cryptographer", "Cyber Crime Investigator", "Cyber defense incident responder", "Cyber forensics expert", "Cyber legal advisor", "Cyber operator", "Cybersecurity engineer", "Vulnerability assessor" },
|
||||||
|
new[] { "cyber", "security", "forensics", "cryptography", "vulnerability" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 11. Data Science & Analytics
|
||||||
|
new CareerField(
|
||||||
|
11,
|
||||||
|
"Data Science & Analytics",
|
||||||
|
"Careers analyzing data, applying mathematical and statistical methods to solve problems and make decisions.",
|
||||||
|
new[] { "Actuary", "Data analyst", "Data scientist", "Economist", "Mathematician", "Operations research analyst" },
|
||||||
|
new[] { "data", "analyst", "actuary", "economist", "mathematician", "research" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 12. CAD, CNC & Manufacturing
|
||||||
|
new CareerField(
|
||||||
|
12,
|
||||||
|
"CAD, CNC & Manufacturing",
|
||||||
|
"Careers in computer-aided design, manufacturing processes, and production planning.",
|
||||||
|
new[] { "CAD professional", "CNC programmer", "Manufacturing", "Production planner" },
|
||||||
|
new[] { "CAD", "CNC", "manufacturing", "production" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 13. Industrial & Product Design
|
||||||
|
new CareerField(
|
||||||
|
13,
|
||||||
|
"Industrial & Product Design",
|
||||||
|
"Design careers creating products, commercial goods, and industrial solutions with focus on form and function.",
|
||||||
|
new[] { "Appraiser", "Commercial and industrial design", "Designer", "Industrial designer", "Product designer" },
|
||||||
|
new[] { "designer", "design", "industrial", "product", "appraiser" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 14. Visual Arts & Animation
|
||||||
|
new CareerField(
|
||||||
|
14,
|
||||||
|
"Visual Arts & Animation",
|
||||||
|
"Creative careers in visual design, illustration, animation, and digital art creation.",
|
||||||
|
new[] { "Animator", "Artist", "Computer animator", "Graphic artist", "Illustrator", "Multimedia designer" },
|
||||||
|
new[] { "animator", "artist", "graphic", "illustrator", "multimedia" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 15. Game Design & Interactive Media
|
||||||
|
new CareerField(
|
||||||
|
15,
|
||||||
|
"Game Design & Interactive Media",
|
||||||
|
"Careers in video game design, development, testing, and professional gaming.",
|
||||||
|
new[] { "Game designer", "Game Play Tester", "Professional Gamer" },
|
||||||
|
new[] { "game", "gamer", "gaming" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 16. Audio & Music Production
|
||||||
|
new CareerField(
|
||||||
|
16,
|
||||||
|
"Audio & Music Production",
|
||||||
|
"Careers in audio engineering, music composition, sound design, and broadcast technology.",
|
||||||
|
new[] { "Audio designer or engineer", "Audio Engineer", "Audio operator or technician", "Broadcast technician", "Music composer" },
|
||||||
|
new[] { "audio", "music", "broadcast", "sound" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 17. Video & Film Production
|
||||||
|
new CareerField(
|
||||||
|
17,
|
||||||
|
"Video & Film Production",
|
||||||
|
"Careers in video production, filmmaking, directing, and television broadcasting.",
|
||||||
|
new[] { "Audiovisual technician", "Director", "Entertainment/television broadcaster", "Videographer" },
|
||||||
|
new[] { "video", "film", "director", "television", "broadcast", "videographer" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 18. Web & Digital Communications
|
||||||
|
new CareerField(
|
||||||
|
18,
|
||||||
|
"Web & Digital Communications",
|
||||||
|
"Careers in web design, digital communication, and instructional technology.",
|
||||||
|
new[] { "Instructional technologist", "Web & digital communications", "Webmaster", "Website designer" },
|
||||||
|
new[] { "web", "website", "digital", "communications", "webmaster" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 19. Writing & Publishing
|
||||||
|
new CareerField(
|
||||||
|
19,
|
||||||
|
"Writing & Publishing",
|
||||||
|
"Careers in writing, editing, publishing, and content creation across various media formats.",
|
||||||
|
new[] { "Ad copy writer", "Editor", "Publisher", "Screenplay writer", "Speech writer", "Technical writer", "Writer" },
|
||||||
|
new[] { "writing", "writer", "editor", "publisher", "copy" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 20. Journalism & Public Relations
|
||||||
|
new CareerField(
|
||||||
|
20,
|
||||||
|
"Journalism & Public Relations",
|
||||||
|
"Careers in news reporting, photojournalism, public relations, and communications management.",
|
||||||
|
new[] { "Internal communications manager", "Motivational speaker", "Photojournalist", "Reporter" },
|
||||||
|
new[] { "journalism", "reporter", "photojournalist", "communications", "speaker" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 21. Forensics & Criminal Investigation
|
||||||
|
new CareerField(
|
||||||
|
21,
|
||||||
|
"Forensics & Criminal Investigation",
|
||||||
|
"Careers in criminal investigation, forensic science, and analyzing evidence for legal proceedings.",
|
||||||
|
new[] { "Crime scene investigator", "Detective", "Forensic accountant", "Forensic anthropologist", "Forensic engineering scientist", "Forensic pathologist" },
|
||||||
|
new[] { "forensic", "detective", "investigator", "crime" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 22. Healthcare & Medical Technology
|
||||||
|
new CareerField(
|
||||||
|
22,
|
||||||
|
"Healthcare & Medical Technology",
|
||||||
|
"Medical and healthcare careers providing patient care, medical technology, and health services.",
|
||||||
|
new[] { "Dietitian", "Doctor", "Epidemiologist", "Medical technologist", "Nurse", "Pharmacist", "Prosthetics practitioner" },
|
||||||
|
new[] { "medical", "health", "doctor", "nurse", "pharmacist", "dietitian", "epidemiology" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 23. Science & Research
|
||||||
|
new CareerField(
|
||||||
|
23,
|
||||||
|
"Science & Research",
|
||||||
|
"Scientific research careers across biology, physics, meteorology, and other scientific disciplines.",
|
||||||
|
new[] { "Botanist", "Food scientist", "Meteorologist", "Molecular biologist", "Physics instructor", "Plant geneticist", "Research and development scientist", "Research assistant", "Researcher" },
|
||||||
|
new[] { "scientist", "research", "biology", "physics", "botanist", "meteorologist", "geneticist" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 24. Education & Training
|
||||||
|
new CareerField(
|
||||||
|
24,
|
||||||
|
"Education & Training",
|
||||||
|
"Careers in teaching, training, and educational instruction across various subjects and technologies.",
|
||||||
|
new[] { "Educator", "Teacher/trainer", "Technology education instructor" },
|
||||||
|
new[] { "educator", "teacher", "trainer", "education", "instructor" }
|
||||||
|
),
|
||||||
|
|
||||||
|
// 25. Business, Legal & Government
|
||||||
|
new CareerField(
|
||||||
|
25,
|
||||||
|
"Business, Legal & Government",
|
||||||
|
"Careers in business management, legal services, government, politics, and public policy.",
|
||||||
|
new[] { "Creative consultant", "Entrepreneur", "Government Official", "Lawyer", "Legal Aide", "Lobbyist", "Management executive", "Market researcher", "Marketing strategist", "Parliamentarian", "Politician", "Project manager", "Public affairs specialist", "Public policy specialist", "Recording Clerk", "Small business owner", "Volunteer manager" },
|
||||||
|
new[] { "business", "legal", "lawyer", "government", "politician", "manager", "marketing", "consultant", "entrepreneur" }
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Core.Utility;
|
||||||
|
|
||||||
|
public static class CareerNormalizer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Normalizes career names from multiline text input.
|
||||||
|
/// Strips bullet points, trims whitespace, and returns distinct normalized names.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Multiline text input containing career names</param>
|
||||||
|
/// <returns>Collection of normalized career names (trimmed, with bullets removed)</returns>
|
||||||
|
public static IEnumerable<string> NormalizeCareerNames(string? input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)
|
||||||
|
.Select(line => NormalizeSingleCareerName(line))
|
||||||
|
.Where(name => !string.IsNullOrWhiteSpace(name))
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalizes a single career name by stripping bullet points and trimming whitespace.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="careerName">The career name to normalize</param>
|
||||||
|
/// <returns>Normalized career name</returns>
|
||||||
|
private static string NormalizeSingleCareerName(string careerName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(careerName))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove common bullet point characters (•, -, *, etc.) and trim
|
||||||
|
var normalized = Regex.Replace(careerName.Trim(), @"^[\u2022\u2023\u25E6\u2043\u2219\-\*\•]\s*", string.Empty, RegexOptions.Compiled);
|
||||||
|
|
||||||
|
return normalized.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds or creates a career name for case-insensitive duplicate detection.
|
||||||
|
/// Returns the normalized (lowercase) version for comparison.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="careerName">The career name to normalize for comparison</param>
|
||||||
|
/// <returns>Lowercase normalized name for duplicate detection</returns>
|
||||||
|
public static string GetNormalizedKey(string careerName)
|
||||||
|
{
|
||||||
|
return NormalizeSingleCareerName(careerName).ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+18
-11
@@ -11,17 +11,18 @@ This approach allows you to edit credentials without rebuilding the container.
|
|||||||
**Steps:**
|
**Steps:**
|
||||||
|
|
||||||
1. **Generate Password Hashes** (on your development machine):
|
1. **Generate Password Hashes** (on your development machine):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run the app locally and navigate to:
|
# Run the app locally and navigate to:
|
||||||
https://localhost:<port>/dev/hash-password?password=YourPassword
|
https://localhost:<port>/dev/hash-password?password=YourPassword
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Create `auth-secrets.json`** on your Docker host:
|
2. **Create `auth-secrets.json`** on your Docker host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp auth-secrets.example.json auth-secrets.json
|
cp auth-secrets.example.json auth-secrets.json
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Edit `auth-secrets.json`** and replace the placeholder hashes:
|
3. **Edit `auth-secrets.json`** and replace the placeholder hashes:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"Authentication": {
|
"Authentication": {
|
||||||
@@ -36,19 +37,20 @@ This approach allows you to edit credentials without rebuilding the container.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Mount the file in Docker Compose**:
|
4. **Mount the file in Docker Compose**:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
volumes:
|
volumes:
|
||||||
- ./auth-secrets.json:/app/secrets/auth-secrets.json:ro
|
- ./auth-secrets.json:/app/secrets/auth-secrets.json:ro
|
||||||
```
|
```
|
||||||
|
|
||||||
5. **Update credentials**: Simply edit `auth-secrets.json` on the host and restart the container:
|
5. **Update credentials**: Simply edit `auth-secrets.json` on the host and restart the container:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose restart webapp
|
docker-compose restart webapp
|
||||||
```
|
```
|
||||||
|
|
||||||
**Security Note**: Set proper file permissions on the host:
|
**Security Note**: Set proper file permissions on the host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod 600 auth-secrets.json
|
chmod 600 auth-secrets.json
|
||||||
```
|
```
|
||||||
@@ -60,6 +62,7 @@ chmod 600 auth-secrets.json
|
|||||||
This approach is useful for container orchestration platforms (Kubernetes, Docker Swarm, etc.).
|
This approach is useful for container orchestration platforms (Kubernetes, Docker Swarm, etc.).
|
||||||
|
|
||||||
**Docker Compose Example**:
|
**Docker Compose Example**:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
environment:
|
||||||
- TSA_Authentication__Users__0__Email=admin@example.com
|
- TSA_Authentication__Users__0__Email=admin@example.com
|
||||||
@@ -73,6 +76,7 @@ environment:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Docker Run Example**:
|
**Docker Run Example**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
-p 8080:8080 \
|
-p 8080:8080 \
|
||||||
@@ -85,6 +89,7 @@ docker run -d \
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Kubernetes Secret Example**:
|
**Kubernetes Secret Example**:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
@@ -138,11 +143,13 @@ docker-compose logs -f webapp
|
|||||||
### Adding a New User
|
### Adding a New User
|
||||||
|
|
||||||
**With Volume-Mounted File:**
|
**With Volume-Mounted File:**
|
||||||
|
|
||||||
1. Edit `auth-secrets.json` on the host
|
1. Edit `auth-secrets.json` on the host
|
||||||
2. Add new user entry to the `Users` array
|
2. Add new user entry to the `Users` array
|
||||||
3. Restart the container: `docker-compose restart webapp`
|
3. Restart the container: `docker-compose restart webapp`
|
||||||
|
|
||||||
**With Environment Variables:**
|
**With Environment Variables:**
|
||||||
|
|
||||||
1. Add new environment variables (increment the index number)
|
1. Add new environment variables (increment the index number)
|
||||||
2. Recreate the container: `docker-compose up -d`
|
2. Recreate the container: `docker-compose up -d`
|
||||||
|
|
||||||
@@ -162,24 +169,22 @@ docker-compose logs -f webapp
|
|||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
1. **File Permissions**:
|
1. **File Permissions**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod 600 auth-secrets.json
|
chmod 600 auth-secrets.json
|
||||||
chown root:root auth-secrets.json
|
chown root:root auth-secrets.json
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Never Commit Secrets**: Add to `.gitignore`:
|
2. **Never Commit Secrets**: Add to `.gitignore`:
|
||||||
|
|
||||||
```
|
```
|
||||||
auth-secrets.json
|
auth-secrets.json
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Use HTTPS in Production**: Configure SSL/TLS certificates
|
3. **Use HTTPS in Production**: Configure SSL/TLS certificates
|
||||||
|
|
||||||
4. **Backup Credentials**: Store encrypted backups of `auth-secrets.json`
|
4. **Backup Credentials**: Store encrypted backups of `auth-secrets.json`
|
||||||
|
|
||||||
5. **Password Rotation**: Periodically regenerate password hashes
|
5. **Password Rotation**: Periodically regenerate password hashes
|
||||||
|
|
||||||
6. **Monitor Access**: Review application logs for failed login attempts:
|
6. **Monitor Access**: Review application logs for failed login attempts:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose logs webapp | grep "Failed login"
|
docker-compose logs webapp | grep "Failed login"
|
||||||
```
|
```
|
||||||
@@ -191,6 +196,7 @@ docker-compose logs -f webapp
|
|||||||
### Container Won't Start
|
### Container Won't Start
|
||||||
|
|
||||||
Check logs:
|
Check logs:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose logs webapp
|
docker-compose logs webapp
|
||||||
```
|
```
|
||||||
@@ -198,16 +204,17 @@ docker-compose logs webapp
|
|||||||
### Can't Login
|
### Can't Login
|
||||||
|
|
||||||
1. Verify `auth-secrets.json` is properly mounted:
|
1. Verify `auth-secrets.json` is properly mounted:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec tsa-app ls -la /app/secrets/
|
docker exec tsa-app ls -la /app/secrets/
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Check if the file is being loaded:
|
2. Check if the file is being loaded:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose logs webapp | grep "secrets"
|
docker-compose logs webapp | grep "secrets"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Verify JSON syntax:
|
3. Verify JSON syntax:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat auth-secrets.json | jq .
|
cat auth-secrets.json | jq .
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace Data
|
|||||||
public DbSet<Student> Students { get; set; }
|
public DbSet<Student> Students { get; set; }
|
||||||
public DbSet<Team> Teams { get; set; }
|
public DbSet<Team> Teams { get; set; }
|
||||||
public DbSet<StudentEventRanking> StudentEventRanking { get; set; }
|
public DbSet<StudentEventRanking> StudentEventRanking { get; set; }
|
||||||
|
public DbSet<EventOccurrence> EventOccurrences { get; set; }
|
||||||
|
public DbSet<Career> Careers { get; set; }
|
||||||
|
|
||||||
public AppDbContext()
|
public AppDbContext()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Data.Configurations
|
||||||
|
{
|
||||||
|
public class CareerConfiguration : IEntityTypeConfiguration<Career>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<Career> builder)
|
||||||
|
{
|
||||||
|
builder.HasKey(c => c.Id);
|
||||||
|
|
||||||
|
// Indexes
|
||||||
|
builder.HasIndex(c => c.Name).IsUnique();
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
builder.Property(c => c.Name)
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -42,6 +42,14 @@ namespace Data.Configurations
|
|||||||
builder.Property(e => e.EventFormat)
|
builder.Property(e => e.EventFormat)
|
||||||
.HasConversion<string>()
|
.HasConversion<string>()
|
||||||
.HasMaxLength(50);
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
// Ignore RelatedCareersText (not mapped to database)
|
||||||
|
builder.Ignore(e => e.RelatedCareersText);
|
||||||
|
|
||||||
|
// Many-to-many relationship with Career
|
||||||
|
builder.HasMany(e => e.RelatedCareers)
|
||||||
|
.WithMany()
|
||||||
|
.UsingEntity(j => j.ToTable("EventDefinitionCareers"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Data.Configurations
|
||||||
|
{
|
||||||
|
public class EventOccurrenceConfiguration : IEntityTypeConfiguration<EventOccurrence>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<EventOccurrence> builder)
|
||||||
|
{
|
||||||
|
builder.HasKey(e => e.Id);
|
||||||
|
|
||||||
|
// Indexes
|
||||||
|
builder.HasIndex(e => e.StartTime);
|
||||||
|
builder.HasIndex(e => e.EventDefinitionId);
|
||||||
|
builder.HasIndex(e => e.SpecialEventType);
|
||||||
|
|
||||||
|
// Foreign key relationship (optional)
|
||||||
|
builder.HasOne(e => e.EventDefinition)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(e => e.EventDefinitionId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict); // Don't cascade delete if EventDefinition is deleted
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
builder.Property(e => e.Name)
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
builder.Property(e => e.Time)
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
builder.Property(e => e.Date)
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
builder.Property(e => e.Location)
|
||||||
|
.HasMaxLength(500);
|
||||||
|
|
||||||
|
builder.Property(e => e.SpecialEventType)
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
// Validation: Either EventDefinitionId OR SpecialEventType must be set (enforced at application level)
|
||||||
|
// EF Core doesn't support mutually exclusive constraints directly, so we'll handle this in validation attributes or service layer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20251227205816_EventOccurrence")]
|
||||||
|
partial class EventOccurrence
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ChapterEligibilityCountRegionals")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ChapterEligibilityCountState")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Documentation")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Eligibility")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EventFormat")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("LevelOfEffort")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MaxTeamSize")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MinTeamSize")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("OnSiteActivity")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("Presubmission")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SemifinalistActivity")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Theme")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventFormat");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Events");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventOccurrence", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Date")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EndTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("EventDefinitionId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SpecialEventType")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("StartTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Time")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("SpecialEventType");
|
||||||
|
|
||||||
|
b.HasIndex("StartTime");
|
||||||
|
|
||||||
|
b.ToTable("EventOccurrences");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Grade")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("NationalId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OfficerRole")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RegionalId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("StateId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("TsaYear")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Email");
|
||||||
|
|
||||||
|
b.HasIndex("Grade");
|
||||||
|
|
||||||
|
b.HasIndex("FirstName", "LastName");
|
||||||
|
|
||||||
|
b.ToTable("Students");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("StudentId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EventDefinitionId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Rank")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("StudentId", "EventDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("EventDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("Rank");
|
||||||
|
|
||||||
|
b.HasIndex("StudentId");
|
||||||
|
|
||||||
|
b.ToTable("StudentEventRanking");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("CaptainId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EventId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Identifier")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CaptainId");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.HasIndex("EventId", "Identifier");
|
||||||
|
|
||||||
|
b.ToTable("Teams");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("StudentTeam", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("StudentsId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TeamsId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("StudentsId", "TeamsId");
|
||||||
|
|
||||||
|
b.HasIndex("TeamsId");
|
||||||
|
|
||||||
|
b.ToTable("TeamStudents", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventOccurrence", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.Navigation("EventDefinition");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.Student", "Student")
|
||||||
|
.WithMany("EventRankings")
|
||||||
|
.HasForeignKey("StudentId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("EventDefinition");
|
||||||
|
|
||||||
|
b.Navigation("Student");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.Student", "Captain")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CaptainId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", "Event")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Captain");
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("StudentTeam", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.Student", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("StudentsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.Team", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TeamsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("EventRankings");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class EventOccurrence : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "EventOccurrences",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
EventDefinitionId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||||
|
SpecialEventType = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||||
|
Time = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
Date = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
StartTime = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
EndTime = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
Location = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_EventOccurrences", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_EventOccurrences_Events_EventDefinitionId",
|
||||||
|
column: x => x.EventDefinitionId,
|
||||||
|
principalTable: "Events",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EventOccurrences_EventDefinitionId",
|
||||||
|
table: "EventOccurrences",
|
||||||
|
column: "EventDefinitionId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EventOccurrences_SpecialEventType",
|
||||||
|
table: "EventOccurrences",
|
||||||
|
column: "SpecialEventType");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EventOccurrences_StartTime",
|
||||||
|
table: "EventOccurrences",
|
||||||
|
column: "StartTime");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "EventOccurrences");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,388 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20251228200039_RelatedCareers")]
|
||||||
|
partial class RelatedCareers
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("CareerEventDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("EventDefinitionId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("RelatedCareersId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("EventDefinitionId", "RelatedCareersId");
|
||||||
|
|
||||||
|
b.HasIndex("RelatedCareersId");
|
||||||
|
|
||||||
|
b.ToTable("EventDefinitionCareers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Career", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Careers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ChapterEligibilityCountRegionals")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ChapterEligibilityCountState")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Documentation")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Eligibility")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EventFormat")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("LevelOfEffort")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MaxTeamSize")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MinTeamSize")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("OnSiteActivity")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("Presubmission")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SemifinalistActivity")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Theme")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventFormat");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Events");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventOccurrence", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Date")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EndTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("EventDefinitionId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SpecialEventType")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("StartTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Time")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("SpecialEventType");
|
||||||
|
|
||||||
|
b.HasIndex("StartTime");
|
||||||
|
|
||||||
|
b.ToTable("EventOccurrences");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Grade")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("NationalId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OfficerRole")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RegionalId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("StateId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("TsaYear")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Email");
|
||||||
|
|
||||||
|
b.HasIndex("Grade");
|
||||||
|
|
||||||
|
b.HasIndex("FirstName", "LastName");
|
||||||
|
|
||||||
|
b.ToTable("Students");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("StudentId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EventDefinitionId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Rank")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("StudentId", "EventDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("EventDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("Rank");
|
||||||
|
|
||||||
|
b.HasIndex("StudentId");
|
||||||
|
|
||||||
|
b.ToTable("StudentEventRanking");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("CaptainId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EventId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Identifier")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CaptainId");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.HasIndex("EventId", "Identifier");
|
||||||
|
|
||||||
|
b.ToTable("Teams");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("StudentTeam", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("StudentsId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TeamsId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("StudentsId", "TeamsId");
|
||||||
|
|
||||||
|
b.HasIndex("TeamsId");
|
||||||
|
|
||||||
|
b.ToTable("TeamStudents", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CareerEventDefinition", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.Career", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RelatedCareersId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventOccurrence", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.Navigation("EventDefinition");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.Student", "Student")
|
||||||
|
.WithMany("EventRankings")
|
||||||
|
.HasForeignKey("StudentId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("EventDefinition");
|
||||||
|
|
||||||
|
b.Navigation("Student");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.Student", "Captain")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CaptainId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", "Event")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Captain");
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("StudentTeam", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.Student", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("StudentsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.Team", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TeamsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("EventRankings");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class RelatedCareers : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Careers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Careers", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "EventDefinitionCareers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
EventDefinitionId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
RelatedCareersId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_EventDefinitionCareers", x => new { x.EventDefinitionId, x.RelatedCareersId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_EventDefinitionCareers_Careers_RelatedCareersId",
|
||||||
|
column: x => x.RelatedCareersId,
|
||||||
|
principalTable: "Careers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_EventDefinitionCareers_Events_EventDefinitionId",
|
||||||
|
column: x => x.EventDefinitionId,
|
||||||
|
principalTable: "Events",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Careers_Name",
|
||||||
|
table: "Careers",
|
||||||
|
column: "Name",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EventDefinitionCareers_RelatedCareersId",
|
||||||
|
table: "EventDefinitionCareers",
|
||||||
|
column: "RelatedCareersId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "EventDefinitionCareers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Careers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
using Data;
|
using Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
@@ -16,6 +17,40 @@ namespace Data.Migrations
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("CareerEventDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("EventDefinitionId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("RelatedCareersId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("EventDefinitionId", "RelatedCareersId");
|
||||||
|
|
||||||
|
b.HasIndex("RelatedCareersId");
|
||||||
|
|
||||||
|
b.ToTable("EventDefinitionCareers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Career", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Careers");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -93,6 +128,55 @@ namespace Data.Migrations
|
|||||||
b.ToTable("Events");
|
b.ToTable("Events");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventOccurrence", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Date")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EndTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("EventDefinitionId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SpecialEventType")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("StartTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Time")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("SpecialEventType");
|
||||||
|
|
||||||
|
b.HasIndex("StartTime");
|
||||||
|
|
||||||
|
b.ToTable("EventOccurrences");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -214,6 +298,31 @@ namespace Data.Migrations
|
|||||||
b.ToTable("TeamStudents", (string)null);
|
b.ToTable("TeamStudents", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CareerEventDefinition", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.Career", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RelatedCareersId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.EventOccurrence", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventDefinitionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.Navigation("EventDefinition");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ public class EventOccurrenceParser_Tests
|
|||||||
{
|
{
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void ParseTest()
|
public void ParseNationalsTest()
|
||||||
{
|
{
|
||||||
var events = TestEntityHandler.GetEvents();
|
var events = TestEntityHandler.GetEvents();
|
||||||
var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceFileInfo(), events);
|
var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceNationalsFileInfo(), events);
|
||||||
var dictionary = parser.Parse();
|
var dictionary = parser.Parse();
|
||||||
Console.WriteLine($"Occurrence, Month, Date, Time, Location");
|
Console.WriteLine($"Occurrence, Month, Date, Time, Location");
|
||||||
foreach (var @event in events)
|
foreach (var @event in events)
|
||||||
@@ -30,15 +30,102 @@ public class EventOccurrenceParser_Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("General Schedule");
|
Console.WriteLine("General Schedule");
|
||||||
|
if (dictionary.ContainsKey(EventDefinition.GeneralSchedule))
|
||||||
|
{
|
||||||
foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime))
|
foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dictionary.ContainsKey(EventDefinition.VotingDelegates))
|
Console.WriteLine("Meet the Candidates");
|
||||||
foreach (var eo in dictionary[EventDefinition.VotingDelegates])
|
if (dictionary.ContainsKey(EventDefinition.MeetTheCandidates))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{eo.Name} {eo.StartTime}, {eo.Location}");
|
foreach (var eo in dictionary[EventDefinition.MeetTheCandidates])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Chapter Officer Meeting");
|
||||||
|
if (dictionary.ContainsKey(EventDefinition.ChapterOfficerMeeting))
|
||||||
|
{
|
||||||
|
foreach (var eo in dictionary[EventDefinition.ChapterOfficerMeeting])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Voting Delegate Meeting");
|
||||||
|
if (dictionary.ContainsKey(EventDefinition.VotingDelegateMeeting))
|
||||||
|
{
|
||||||
|
foreach (var eo in dictionary[EventDefinition.VotingDelegateMeeting])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ParseStatesTest()
|
||||||
|
{
|
||||||
|
var events = TestEntityHandler.GetEvents();
|
||||||
|
var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceStateFileInfo(), events);
|
||||||
|
var dictionary = parser.Parse();
|
||||||
|
Console.WriteLine($"Occurrence, Month, Date, Time, Location");
|
||||||
|
foreach (var @event in events)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{@event.Name}");
|
||||||
|
|
||||||
|
if (!dictionary.ContainsKey(@event))
|
||||||
|
{
|
||||||
|
Console.WriteLine("!!! eventDefinition not found " + @event.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var eventOccurrences = dictionary[@event];
|
||||||
|
foreach (var eo in eventOccurrences)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("General Schedule");
|
||||||
|
if (dictionary.ContainsKey(EventDefinition.GeneralSchedule))
|
||||||
|
{
|
||||||
|
foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Meet the Candidates");
|
||||||
|
if (dictionary.ContainsKey(EventDefinition.MeetTheCandidates))
|
||||||
|
{
|
||||||
|
foreach (var eo in dictionary[EventDefinition.MeetTheCandidates])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Chapter Officer Meeting");
|
||||||
|
if (dictionary.ContainsKey(EventDefinition.ChapterOfficerMeeting))
|
||||||
|
{
|
||||||
|
foreach (var eo in dictionary[EventDefinition.ChapterOfficerMeeting])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Voting Delegate Meeting");
|
||||||
|
if (dictionary.ContainsKey(EventDefinition.VotingDelegateMeeting))
|
||||||
|
{
|
||||||
|
foreach (var eo in dictionary[EventDefinition.VotingDelegateMeeting])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Pass();
|
Assert.Pass();
|
||||||
|
|||||||
@@ -16,11 +16,17 @@ public static class TestEntityHandler
|
|||||||
return eventRankingsParser.Parse();
|
return eventRankingsParser.Parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileInfo GetEventOccurrenceFileInfo()
|
public static FileInfo GetEventOccurrenceNationalsFileInfo()
|
||||||
{
|
{
|
||||||
return FileUtility.GetContentFile(ContentDirectory, "2025 TSA Nationals Competition Event Times.txt");
|
return FileUtility.GetContentFile(ContentDirectory, "2025 TSA Nationals Competition Event Times.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static FileInfo GetEventOccurrenceStateFileInfo()
|
||||||
|
{
|
||||||
|
return FileUtility.GetContentFile(ContentDirectory, "2025 TN TSA State Competition Event Times.txt");
|
||||||
|
}
|
||||||
|
|
||||||
public static Student[] GetStudents(IList<EventDefinition> events)
|
public static Student[] GetStudents(IList<EventDefinition> events)
|
||||||
{
|
{
|
||||||
//var studentEventRankingsCsv = "Student Event Rankings.csv";
|
//var studentEventRankingsCsv = "Student Event Rankings.csv";
|
||||||
|
|||||||
@@ -1,366 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Core.Calculation;
|
|
||||||
using Core.Entities;
|
|
||||||
using Core.Parsers;
|
|
||||||
using Core.Utility;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Web.Models;
|
|
||||||
|
|
||||||
namespace Web.Controllers
|
|
||||||
{
|
|
||||||
public partial class HomeController : Controller
|
|
||||||
{
|
|
||||||
private const string ContentDirectory = @"C:\Users\james\source\TSA Chapter Organizer\Tests\Parsers\TestInput\";
|
|
||||||
|
|
||||||
private EventDefinition[] GetEvents()
|
|
||||||
{
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - EventDefinition Definitions.csv");
|
|
||||||
var eventRankingsParser = new EventDefinitionParser(fileInfo);
|
|
||||||
return eventRankingsParser.Parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Student[] GetStudents(IList<EventDefinition> events)
|
|
||||||
{
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - Nationals Student EventDefinition Rankings.csv");
|
|
||||||
var eventRankingsParser = new StudentParser(fileInfo);
|
|
||||||
return eventRankingsParser.Parse(events);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StudentEventRanking[] GetStudentEventRankings(Student[] students, EventDefinition[] events)
|
|
||||||
{
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA - Student Event Rankings.csv");
|
|
||||||
|
|
||||||
var rankingParser = new StudentEventRankingParser(fileInfo);
|
|
||||||
return rankingParser.Parse(students, events);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Team[] GetTeams(IList<EventDefinition> competitiveEvents, IList<Student> students)
|
|
||||||
{
|
|
||||||
var studentEventRankingsCsv = "2024-25 RMS TSA student & eventDefinition - Nationals Teams.csv";
|
|
||||||
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
|
|
||||||
var eventRankingsParser = new TeamParser(fileInfo);
|
|
||||||
var teams = eventRankingsParser.Parse(competitiveEvents, students);
|
|
||||||
|
|
||||||
foreach (var student in students)
|
|
||||||
{
|
|
||||||
student.Teams = teams.Where(t => t.Students.Contains(student)).ToList();
|
|
||||||
}
|
|
||||||
return teams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssignmentAssumption[] GetAssignmentAssumptions(IList<EventDefinition> competitiveEvents, IList<Student> students)
|
|
||||||
{
|
|
||||||
var assumptionsCsv = "2024-25 RMS TSA student & eventDefinition - assumptions.csv";
|
|
||||||
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, assumptionsCsv);
|
|
||||||
var assumptionParser = new AssignmentAssumptionParser(fileInfo);
|
|
||||||
var assumptions = assumptionParser.Parse(competitiveEvents, students);
|
|
||||||
|
|
||||||
return assumptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDictionary<EventDefinition, List<EventOccurrence>> GetStateEventOccurrences(IList<EventDefinition> competitiveEvents)
|
|
||||||
{
|
|
||||||
var eventTimesFilename = "2025 TN TSA State Competition EventDefinition Times.txt";
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, eventTimesFilename);
|
|
||||||
var parser = new EventOccurrenceParser(fileInfo, competitiveEvents);
|
|
||||||
return parser.Parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDictionary<EventDefinition, List<EventOccurrence>> GetNationalEventOccurrences(IList<EventDefinition> competitiveEvents)
|
|
||||||
{
|
|
||||||
var eventTimesFilename = "2025 TSA Nationals Competition EventDefinition Times.txt";
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, eventTimesFilename);
|
|
||||||
var parser = new EventOccurrenceParser(fileInfo, competitiveEvents);
|
|
||||||
var nationalEventOccurrences = parser.Parse();
|
|
||||||
|
|
||||||
var locationPrefixes = new[] { "Cheekwood", "Ryman", "Lincoln", "Canal", "Online", "Magnolia", "Tennessee", "Bayou", "Hermitage", "Belmont", "Davidson", "Washington", "Belle Meade"};
|
|
||||||
var oredLocations = string.Join("|", locationPrefixes);
|
|
||||||
var regex = new Regex($"^(.*)((?:{oredLocations}).*)");
|
|
||||||
|
|
||||||
foreach (var occurrence in nationalEventOccurrences)
|
|
||||||
{
|
|
||||||
foreach (var eventOccurrence in occurrence.Value)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(eventOccurrence.Location))
|
|
||||||
continue;
|
|
||||||
var match = regex.Match(eventOccurrence.Name);
|
|
||||||
if (!match.Success)
|
|
||||||
continue;
|
|
||||||
eventOccurrence.Name = match.Groups[1].Value.Trim();
|
|
||||||
eventOccurrence.Location = match.Groups[2].Value.Trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nationalEventOccurrences;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ILogger<HomeController> _logger;
|
|
||||||
|
|
||||||
public HomeController(ILogger<HomeController> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Index()
|
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Events()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents().Where(e => e.Name != "Chapter Team").ToArray();
|
|
||||||
return View(competitiveEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult StudentEventHandout()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents();
|
|
||||||
var students = GetStudents(competitiveEvents);
|
|
||||||
var teams = GetTeams(competitiveEvents, students);
|
|
||||||
|
|
||||||
return View(Tuple.Create(students));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult StudentEvents()
|
|
||||||
{
|
|
||||||
var events = GetEvents();
|
|
||||||
var students = GetStudents(events);
|
|
||||||
var rankings = GetStudentEventRankings(students, events);
|
|
||||||
|
|
||||||
|
|
||||||
var eventStudentPicksArray = StudentEventRanking.GetEventStudentRankings(rankings);
|
|
||||||
var assignmentParameters = new AssignmentParameters
|
|
||||||
{
|
|
||||||
EffortUpperBound = 9,
|
|
||||||
RequireOnSite = true,
|
|
||||||
RequireRegional = true,
|
|
||||||
TeamSizeLimit = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
var eventAssignment = new EventAssigner(events, students, assignmentParameters);
|
|
||||||
|
|
||||||
var assignmentAssumptions = GetAssignmentAssumptions(events, students);
|
|
||||||
|
|
||||||
foreach (var assumption in assignmentAssumptions)
|
|
||||||
{
|
|
||||||
switch (assumption.Assumption)
|
|
||||||
{
|
|
||||||
case Assumption.Exclude:
|
|
||||||
eventAssignment.ExcludeFromEvent(assumption.EventAssignment);
|
|
||||||
break;
|
|
||||||
case Assumption.Include:
|
|
||||||
eventAssignment.AssignToEvent(assumption.EventAssignment);
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventAssignment.RemoveEvent(new List<EventDefinition>
|
|
||||||
{
|
|
||||||
events.First(e => e.Name == "Chapter Team")
|
|
||||||
});
|
|
||||||
|
|
||||||
eventAssignment.IncludedEvents(new List<EventDefinition>
|
|
||||||
{
|
|
||||||
//competitiveEvents.First(e => e.Name == "System Control Technology")
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var eventAssignmentsList = eventAssignment.Solve();
|
|
||||||
return View(Tuple.Create(events, students, eventStudentPicksArray, eventAssignmentsList, assignmentParameters));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Teams()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents();
|
|
||||||
var students = GetStudents(competitiveEvents);
|
|
||||||
var teams = GetTeams(competitiveEvents, students);
|
|
||||||
|
|
||||||
//teams = teams.Where(t => t.EventDefinition.RegionalEvent).ToArray();
|
|
||||||
|
|
||||||
return View(Tuple.Create(teams));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Regionals()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents();
|
|
||||||
var students = GetStudents(competitiveEvents);
|
|
||||||
var teams = GetTeams(competitiveEvents, students);
|
|
||||||
|
|
||||||
teams = teams.Where(t => t.EventDefinition.RegionalEvent).ToArray();
|
|
||||||
var enumerable = students.Where(s => !teams.SelectMany(ts => ts.Students).Contains(s)).ToArray();
|
|
||||||
|
|
||||||
return View(Tuple.Create(teams, enumerable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult State()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents();
|
|
||||||
var students = GetStudents(competitiveEvents);
|
|
||||||
var teams = GetTeams(competitiveEvents, students);
|
|
||||||
var eventOccurrences = GetStateEventOccurrences(competitiveEvents);
|
|
||||||
|
|
||||||
// Filter out pre-conference
|
|
||||||
eventOccurrences =
|
|
||||||
(from kv in eventOccurrences
|
|
||||||
let newV = kv.Value.Where(eo => !eo.Name.Contains("Pre-Conference"))
|
|
||||||
select Tuple.Create(kv.Key, newV))
|
|
||||||
.ToDictionary(s => s.Item1, s => s.Item2.ToList());
|
|
||||||
|
|
||||||
return View(Tuple.Create(teams, students, eventOccurrences));
|
|
||||||
}
|
|
||||||
public IActionResult Nationals()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents();
|
|
||||||
var students = GetStudents(competitiveEvents);
|
|
||||||
var teams = GetTeams(competitiveEvents, students);
|
|
||||||
var eventOccurrences = GetNationalEventOccurrences(competitiveEvents);
|
|
||||||
|
|
||||||
// Filter out pre-conference
|
|
||||||
eventOccurrences =
|
|
||||||
(from kv in eventOccurrences
|
|
||||||
let newV = kv.Value.Where(eo => !eo.Name.Contains("Pre-Conference"))
|
|
||||||
select Tuple.Create(kv.Key, newV))
|
|
||||||
.ToDictionary(s => s.Item1, s => s.Item2.ToList());
|
|
||||||
|
|
||||||
return View(Tuple.Create(teams, students, eventOccurrences));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult TeamGrid()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents();
|
|
||||||
var students = GetStudents(competitiveEvents);
|
|
||||||
var teams = GetTeams(competitiveEvents, students);
|
|
||||||
|
|
||||||
return View(Tuple.Create(teams, students));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Students()
|
|
||||||
{
|
|
||||||
var competitiveEvents = GetEvents();
|
|
||||||
var students = GetStudents(competitiveEvents);
|
|
||||||
GetTeams(competitiveEvents, students);
|
|
||||||
|
|
||||||
return View(Tuple.Create(students));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Func<T, bool> And<T>(params Func<T, bool>[] predicates)
|
|
||||||
{
|
|
||||||
return t => predicates.All(predicate => predicate(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Schedule()
|
|
||||||
{
|
|
||||||
var scheduleOptions =
|
|
||||||
new ScheduleOptions(
|
|
||||||
timeSlots: 3,
|
|
||||||
mustIncludeEvents:new []
|
|
||||||
{
|
|
||||||
"Medical Technology", "Electrical Applications" //, "RegionalTeam",
|
|
||||||
,"Dragster", "Flight"
|
|
||||||
},
|
|
||||||
extended: new string[]
|
|
||||||
{
|
|
||||||
"Invention", "Construction Challenge", "Mechanical", "Mass", "Micro"
|
|
||||||
//"STEM"
|
|
||||||
//"Community", "Vlogging"// "Microcontroller"
|
|
||||||
},
|
|
||||||
omittedEvents: new string[]
|
|
||||||
{
|
|
||||||
"Vlogging", "Junior", "Community Service Video", "Digital Photography",
|
|
||||||
"STEM"
|
|
||||||
|
|
||||||
//"Leadership",// "Electrical", //"Construction"
|
|
||||||
// "Forensic",
|
|
||||||
//"CAD"
|
|
||||||
//"I&I Team 1", "I&I Team 2"//, "Website Design",
|
|
||||||
},
|
|
||||||
absentStudents: new string[]
|
|
||||||
{
|
|
||||||
//"Eliam"
|
|
||||||
},
|
|
||||||
reverse: true,
|
|
||||||
slotPush:0);
|
|
||||||
|
|
||||||
var events = GetEvents();
|
|
||||||
var students = GetStudents(events);
|
|
||||||
|
|
||||||
var allTeams = GetTeams(events, students);
|
|
||||||
|
|
||||||
var omittedEvents = allTeams.Where(t => scheduleOptions.OmittedEvents?.Any(s => t.Name.Contains(s)) == true).ToArray();
|
|
||||||
allTeams = allTeams.Where(t => !omittedEvents.Contains(t)).ToArray();
|
|
||||||
|
|
||||||
bool RegionalPredicate(Team t) => t.EventDefinition.RegionalEvent;
|
|
||||||
bool TeamPredicate(Team t) => t.EventDefinition.EventFormat is EventFormat.Team;
|
|
||||||
bool RegionalTeamPredicate(Team t) => RegionalPredicate(t) && TeamPredicate(t);
|
|
||||||
bool HighEffort(Team t) => t.EventDefinition.LevelOfEffort == 3;
|
|
||||||
bool LowEffort(Team t) => t.EventDefinition.LevelOfEffort == 1;
|
|
||||||
|
|
||||||
bool RegionalTeamSomeEffortPredicate(Team t) => RegionalTeamPredicate(t) && !LowEffort(t);
|
|
||||||
|
|
||||||
bool IndividualPredicate(Team t) => t.EventDefinition.EventFormat is EventFormat.Individual;
|
|
||||||
bool negativePredicate(Team t) => false;
|
|
||||||
|
|
||||||
var mustIncludeTeams =
|
|
||||||
from e in events
|
|
||||||
from t in allTeams
|
|
||||||
where t.EventDefinition == e &&
|
|
||||||
(scheduleOptions.MustIncludeEvents?.Any(s => s == "RegionalTeam") == true && RegionalTeamSomeEffortPredicate(t)
|
|
||||||
|| scheduleOptions.MustIncludeEvents?.Any( t.EventDefinition.Name.Contains) == true
|
|
||||||
|| HighEffort(t))
|
|
||||||
select t;
|
|
||||||
|
|
||||||
Debug.WriteLine("Must Include: " + string.Join(", ", mustIncludeTeams.Select(t => t.ToStringWithIndividualAndRegional())));
|
|
||||||
Debug.WriteLine("Omitted: " + string.Join(", ", omittedEvents.Select(t => t.ToStringWithIndividualAndRegional())));
|
|
||||||
|
|
||||||
var teamScheduler = new TeamScheduler(mustIncludeTeams.ToArray(), scheduleOptions.TimeSlots);
|
|
||||||
var schedule = teamScheduler.Solve();
|
|
||||||
|
|
||||||
//schedule = schedule.OrderByDescending(ts => new Random(schedule.GetHashCode() + 1).Next()).ToArray();
|
|
||||||
|
|
||||||
if (scheduleOptions.SlotPush != 0)
|
|
||||||
{
|
|
||||||
schedule = schedule.Skip(scheduleOptions.SlotPush).Concat(schedule.Take(scheduleOptions.SlotPush))
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
if (scheduleOptions.Reverse)
|
|
||||||
schedule = schedule.Reverse().ToArray();
|
|
||||||
|
|
||||||
////// extend schedules
|
|
||||||
var extendedTeams =
|
|
||||||
from t in allTeams
|
|
||||||
where
|
|
||||||
scheduleOptions.MustIncludeEvents?.Any(s => scheduleOptions.ExtendedTeams?.Any(et => t.Name.Contains(et)) == true) == true
|
|
||||||
select t;
|
|
||||||
|
|
||||||
foreach (var extendedTeam in extendedTeams)
|
|
||||||
{
|
|
||||||
schedule = new UnassignedStudentScheduler(allTeams, schedule).AddAdditionalTimeSlot(extendedTeam);
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.LevelOfEffort);
|
|
||||||
schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.BiggestGroup);
|
|
||||||
schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.IndividualEvents);
|
|
||||||
//schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.AnyNotMeetingAlready);
|
|
||||||
|
|
||||||
var unassignedStudents = UnassignedStudentScheduler.UnassignedStudents(students, schedule).ToArray();
|
|
||||||
|
|
||||||
return View(Tuple.Create(schedule, unassignedStudents, scheduleOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Privacy()
|
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
|
||||||
public IActionResult Error()
|
|
||||||
{
|
|
||||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
namespace Web.Controllers;
|
|
||||||
|
|
||||||
public partial class HomeController
|
|
||||||
{
|
|
||||||
public class ScheduleOptions(
|
|
||||||
int timeSlots = 3,
|
|
||||||
string[]? absentStudents = null,
|
|
||||||
string[]? extended = null,
|
|
||||||
string[]? omittedEvents = null,
|
|
||||||
string[]? mustIncludeEvents = null,
|
|
||||||
bool reverse = false,
|
|
||||||
DateTime date = new(),
|
|
||||||
int slotPush = 0
|
|
||||||
)
|
|
||||||
{
|
|
||||||
public int TimeSlots = timeSlots;
|
|
||||||
public string[]? AbsentStudents = absentStudents;
|
|
||||||
public string[]? ExtendedTeams = extended;
|
|
||||||
public string[]? OmittedEvents = omittedEvents;
|
|
||||||
public string[]? MustIncludeEvents = mustIncludeEvents;
|
|
||||||
public bool Reverse { get; } = reverse;
|
|
||||||
public int SlotPush { get; } = slotPush;
|
|
||||||
public DateTime Date { get; } = date == new DateTime() ? DateTime.Today : date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace Web
|
|
||||||
{
|
|
||||||
public class LabelHelper
|
|
||||||
{
|
|
||||||
public static string GetOrderClass(int pick)
|
|
||||||
{
|
|
||||||
switch (pick)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
return "first-pick";
|
|
||||||
case 2:
|
|
||||||
return "second-pick";
|
|
||||||
case 3:
|
|
||||||
return "third-pick";
|
|
||||||
case 4:
|
|
||||||
return "fourth-pick";
|
|
||||||
case 5:
|
|
||||||
return "fifth-pick";
|
|
||||||
case 6:
|
|
||||||
return "sixth-pick";
|
|
||||||
default:
|
|
||||||
return "non-pick";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace Web.Models
|
|
||||||
{
|
|
||||||
public class ErrorViewModel
|
|
||||||
{
|
|
||||||
public string? RequestId { get; set; }
|
|
||||||
|
|
||||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
// Add services to the container.
|
|
||||||
builder.Services.AddControllersWithViews();
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (!app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Home/Error");
|
|
||||||
}
|
|
||||||
app.UseStaticFiles();
|
|
||||||
|
|
||||||
app.UseRouting();
|
|
||||||
|
|
||||||
app.UseAuthorization();
|
|
||||||
|
|
||||||
app.MapControllerRoute(
|
|
||||||
name: "default",
|
|
||||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:28852",
|
|
||||||
"sslPort": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"Web": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "http://localhost:5016",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
@model int?
|
|
||||||
@{
|
|
||||||
switch (Model)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
<text>★☆☆</text>
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
<text>★★☆</text>
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
<text>★★★</text>
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
@model int?
|
|
||||||
@{
|
|
||||||
switch (Model)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
<text>☆</text>
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
<text>✯</text>
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
<text>★</text>
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
@using Core.Utility
|
|
||||||
@model EventDefinition[]
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Events Page";
|
|
||||||
}
|
|
||||||
<div>
|
|
||||||
@foreach (var evt in Model.OrderBy(e => e.Name))
|
|
||||||
{
|
|
||||||
<div class="container nobrk">
|
|
||||||
@if (evt.RegionalEvent)
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<i>Regional EventDefinition</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div div class="row">
|
|
||||||
<div class="col-4">
|
|
||||||
<h5>@evt.Name</h5>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
@if (evt.EventFormat is EventFormat.Team)
|
|
||||||
{
|
|
||||||
<html><strong>@evt.EventFormat</strong><br/>Size: <strong>@evt.TeamSize</strong></html>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<html>
|
|
||||||
<strong>@evt.EventFormat</strong>
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
Eligibility: @evt.Eligibility
|
|
||||||
</div>
|
|
||||||
<div class="col-1">
|
|
||||||
@Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
@evt.SemifinalistActivity
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div div class="row mt-3">
|
|
||||||
<div class="col">@evt.Description</div></div>
|
|
||||||
@if (!string.IsNullOrEmpty(evt.Theme))
|
|
||||||
{
|
|
||||||
<div div class="row mt-2">
|
|
||||||
<div class="col-3 text-center"><i>Theme for 2024-25:</i></div>
|
|
||||||
<div class="col">@evt.Theme</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(evt.Documentation))
|
|
||||||
{
|
|
||||||
<div div class="row mt-2">
|
|
||||||
<div class="col-3 text-center"><i>Materials:</i></div>
|
|
||||||
<div class="col">@evt.Documentation</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
@using Core.Utility
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Home Page";
|
|
||||||
}
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
@using System.Text.RegularExpressions
|
|
||||||
@using Core.Entities
|
|
||||||
@model Tuple<Team[], Student[], IDictionary<EventDefinition, List<EventOccurrence>>>
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Teams";
|
|
||||||
var eventOccurrences = Model.Item3;
|
|
||||||
}
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>Nationals EventDefinitions</h2>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Team</td>
|
|
||||||
<td>Team Members</td>
|
|
||||||
<td>Dates</td>
|
|
||||||
<td>Materials</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var team in Model.Item1)
|
|
||||||
{
|
|
||||||
var students = team.Students;
|
|
||||||
@if (true @* team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Team *@)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@* <strong class="@{ GetTeamClass(team);}">@team.Name</strong> *@
|
|
||||||
@team.Name @* #@team.TeamNumber
|
|
||||||
*@ </td>
|
|
||||||
<td>
|
|
||||||
@{
|
|
||||||
var first = true;
|
|
||||||
}
|
|
||||||
@foreach (var student in students.OrderByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
|
||||||
{
|
|
||||||
@if (!first)
|
|
||||||
{
|
|
||||||
<text>, </text>
|
|
||||||
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
@student.FirstNameLastName @if (team.Captain == student)
|
|
||||||
{
|
|
||||||
<text> (Cpt. @team.Captain.NationalID)</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@* @if (team.EventDefinition.StatePresubmission)
|
|
||||||
{
|
|
||||||
<text>Pre-submission due Friday, March 14</text>
|
|
||||||
}
|
|
||||||
@if (team.EventDefinition.StatePretesting)
|
|
||||||
{
|
|
||||||
<text>Pre-testing Wednesday, April 2nd</text>
|
|
||||||
}
|
|
||||||
@if (team.EventDefinition.StatePreliminaryRound)
|
|
||||||
{
|
|
||||||
<text>Preliminary and Semifinalist Rounds</text>
|
|
||||||
} *@
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@team.EventDefinition.Documentation
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
@* else if (team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Individual)
|
|
||||||
{
|
|
||||||
foreach (var student in students)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@team.EventDefinition.Name - @student.FirstNameLastName (@student.RegionalID)
|
|
||||||
</td>
|
|
||||||
<td>@team.RegionalTimeSlot</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
} *@
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@{
|
|
||||||
var s = Model.Item2.OrderBy(s => s.LastNameFirstName);
|
|
||||||
|
|
||||||
@foreach (var student in s)
|
|
||||||
{
|
|
||||||
<div class="container nobrk pt-5" style="page-break-before: always;">
|
|
||||||
|
|
||||||
<h3>@student.FirstNameLastName - @student.NationalID</h3>
|
|
||||||
<h4>TSA 2025 Nationals Schedule</h4>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col col-2">EventDefinitions</div>
|
|
||||||
<div class="col">
|
|
||||||
@foreach (var ev in student.Teams)
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col col-2">
|
|
||||||
@if (ev.EventDefinition.EventFormat is EventFormat.Team)
|
|
||||||
{
|
|
||||||
@ev.TeamNumber
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@ev.Captain.NationalID
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<text> @ev.Name
|
|
||||||
@if (ev.Captain == student)
|
|
||||||
{
|
|
||||||
<strong>(Cpt.)</strong>
|
|
||||||
}
|
|
||||||
</text>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
@ev.EventDefinition.SemifinalistActivity
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Time</td>
|
|
||||||
<td>EventDefinition</td>
|
|
||||||
<td></td>
|
|
||||||
<td>Location</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
@foreach (var date in
|
|
||||||
eventOccurrences
|
|
||||||
.Where(eo =>
|
|
||||||
student.Teams.Select(t => t.EventDefinition).Any(a => a == eo.Key)
|
|
||||||
|| eo.Key == EventDefinition.GeneralSchedule
|
|
||||||
|| (eo.Key == EventDefinition.VotingDelegates && student.VotingDelegate))
|
|
||||||
.SelectMany(eo => eo.Value.Select(v => Tuple.Create(v, eo.Key)))
|
|
||||||
.GroupBy(de => de.Item1.StartTime.Date)
|
|
||||||
.OrderBy(d => d.Key)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var eventsForThisDay =
|
|
||||||
date
|
|
||||||
.Where(de => de.Item1.Name != "Judging")
|
|
||||||
.Where(de => de.Item1.StartTime > new DateTime(2024, 3, 1))
|
|
||||||
// filter out occurrences where non-captain
|
|
||||||
.Where(de =>
|
|
||||||
!de.Item1.SignupSubmitPickup
|
|
||||||
|| de.Item2.EventFormat is EventFormat.Individual
|
|
||||||
|| student.Teams.Any(t => t.Captain == student && t.EventDefinition == de.Item2)
|
|
||||||
)
|
|
||||||
.OrderBy(de => de.Item1.StartTime);
|
|
||||||
|
|
||||||
@if (!eventsForThisDay.Any())
|
|
||||||
continue;
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td colspan="4" class="align-content-center text-center fw-bold">@date.Key.ToString("MMMM d")</td>
|
|
||||||
</tr>
|
|
||||||
@foreach (var eventOccurrence in eventsForThisDay.OrderBy(de => de.Item1.StartTime))
|
|
||||||
{
|
|
||||||
|
|
||||||
string hlClass = null;
|
|
||||||
@if (!eventOccurrence.Item2.Name.Contains("General"))
|
|
||||||
{
|
|
||||||
hlClass = "fw-bold";
|
|
||||||
}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="@hlClass" style="white-space:nowrap;">@eventOccurrence.Item1.Time</td>
|
|
||||||
<td class="@hlClass">@eventOccurrence.Item2.Name</td>
|
|
||||||
<td class="@hlClass">
|
|
||||||
@eventOccurrence.Item1.Name
|
|
||||||
@if (eventOccurrence.Item1.Name.Contains("Pick") && eventOccurrence.Item2.EventFormat is EventFormat.Team)
|
|
||||||
{
|
|
||||||
<br/>
|
|
||||||
<text>or coordinate with a teammate</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>@eventOccurrence.Item1.Location
|
|
||||||
@if (eventOccurrence.Item1.Location == "Online" && eventOccurrence.Item1.Name.Contains("Sign-up") ) { <text>by Advisor</text>}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@{
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>Combined Schedule</h2>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Time</td>
|
|
||||||
<td>Team</td>
|
|
||||||
<td></td>
|
|
||||||
<td>Location</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var eventsForDate
|
|
||||||
in Model.Item3.SelectMany(eo => eo.Value.Select(e => Tuple.Create(e, eo.Key)))
|
|
||||||
.Where(de => de.Item1.Name != "Judging")
|
|
||||||
.GroupBy(eo => eo.Item1.StartTime.Date)
|
|
||||||
.OrderBy(eo => eo.Key)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
<tr><td colspan="4"><strong>@eventsForDate.Key.ToString("MMMM d") </strong> </td></tr>
|
|
||||||
@foreach (var occurrence in eventsForDate.OrderBy(o => o.Item1.StartTime))
|
|
||||||
{
|
|
||||||
var teams = Model.Item1.Where(t => t.EventDefinition == occurrence.Item2);
|
|
||||||
if (occurrence.Item2 != EventDefinition.GeneralSchedule && occurrence.Item2 != EventDefinition.VotingDelegates && !teams.Any())
|
|
||||||
continue;
|
|
||||||
<tr>
|
|
||||||
<td style="white-space:nowrap;">@occurrence.Item1.Time</td>
|
|
||||||
<td>
|
|
||||||
@if (occurrence.Item2 == EventDefinition.GeneralSchedule)
|
|
||||||
{
|
|
||||||
<text>Everyone</text>
|
|
||||||
}
|
|
||||||
else if (occurrence.Item2 == EventDefinition.VotingDelegates)
|
|
||||||
{
|
|
||||||
<text>Voting Delegates - @string.Join(", ", Model.Item2.Where(stu => stu.VotingDelegate).Select(stu => stu.FirstName))</text>
|
|
||||||
}
|
|
||||||
@foreach (var team in teams)
|
|
||||||
{
|
|
||||||
<text>@team</text>
|
|
||||||
|
|
||||||
|
|
||||||
<text> - @string.Join(", ", team.Students.Select(stu => stu.FirstName))</text>
|
|
||||||
}
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@occurrence.Item1.Name
|
|
||||||
|
|
||||||
@if (occurrence.Item1.SignupSubmitPickup)
|
|
||||||
{
|
|
||||||
<br/>
|
|
||||||
<text>1 Team Member</text>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (occurrence.Item1.Name.Contains("Semifinalist") && (occurrence.Item1.Name.Contains("Interview") || occurrence.Item1.Name.Contains("Presentation")))
|
|
||||||
{
|
|
||||||
<br/>
|
|
||||||
<text>@Regex.Match(@occurrence.Item2.SemifinalistActivity, @"(?<=\().*?(?=\))").Value</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>@occurrence.Item1.Location</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>Students</h2>
|
|
||||||
<table class="table-primary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td>ID</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var student in Model.Item2.OrderByDescending(s => s.Officer.Contains("President")).ThenBy(s => s.LastNameFirstName))
|
|
||||||
{
|
|
||||||
var assignments
|
|
||||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
|
||||||
|
|
||||||
.Distinct()
|
|
||||||
.OrderBy(e => e.Name).ToList();
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<strong>@student.FirstNameLastName</strong> @if (!string.IsNullOrEmpty(student.Officer))
|
|
||||||
{
|
|
||||||
<text>(@student.Officer)</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>@student.NationalID</td>
|
|
||||||
|
|
||||||
@foreach (var t in assignments)
|
|
||||||
{
|
|
||||||
<td>
|
|
||||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
|
||||||
{
|
|
||||||
@t.Name
|
|
||||||
@if (t.Captain == student)
|
|
||||||
{
|
|
||||||
<text> (Captain)</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@t.EventDefinition.Name
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>Students</h2>
|
|
||||||
@foreach (var student in Model.Item2.OrderByDescending(s => s.Officer.Contains("President")).ThenBy(s => s.LastNameFirstName))
|
|
||||||
{
|
|
||||||
var assignments
|
|
||||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
|
||||||
|
|
||||||
.Distinct()
|
|
||||||
.OrderBy(e => e.Name).ToList();
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<strong>@student.FirstNameLastName</strong>
|
|
||||||
@if (!string.IsNullOrEmpty(student.Officer))
|
|
||||||
{
|
|
||||||
<text>(@student.Officer)</text>
|
|
||||||
}
|
|
||||||
<text>@student.NationalID</text>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
@foreach (var t in assignments)
|
|
||||||
{
|
|
||||||
<p>
|
|
||||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
|
||||||
{
|
|
||||||
@t.Name
|
|
||||||
@if (t.Captain == student)
|
|
||||||
{
|
|
||||||
<text> (Captain)</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@t.EventDefinition.Name
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
@{
|
|
||||||
ViewData["Title"] = "Privacy Policy";
|
|
||||||
}
|
|
||||||
<h1>@ViewData["Title"]</h1>
|
|
||||||
|
|
||||||
<p>Use this page to detail your site's privacy policy.</p>
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
@using Core.Utility
|
|
||||||
@model Tuple<Team[], Student[]>
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Teams";
|
|
||||||
}
|
|
||||||
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Team</td>
|
|
||||||
@*<td>Time Slot</td>*@
|
|
||||||
<td>Notes</td>
|
|
||||||
<td>Team Members</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var team in Model.Item1/*.OrderBy(t => t.RegionalTimeSlotObj)*/)
|
|
||||||
{
|
|
||||||
var students = team.Students;
|
|
||||||
@if (team.EventDefinition.EventFormat == EventFormat.Team)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@* <strong class="@{ GetTeamClass(team);}">@team.Name</strong> *@
|
|
||||||
@team.Name #@team.TeamNumber
|
|
||||||
</td>
|
|
||||||
@*<td>
|
|
||||||
@team.RegionalTimeSlot
|
|
||||||
</td>*@
|
|
||||||
<td>
|
|
||||||
@team.EventDefinition.Notes
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@{
|
|
||||||
var first = true;
|
|
||||||
}
|
|
||||||
@foreach (var student in students.OrderByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
|
||||||
{
|
|
||||||
@if (!first)
|
|
||||||
{
|
|
||||||
<text>, </text>
|
|
||||||
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
@student.FirstNameLastName @if (team.Captain == student)
|
|
||||||
{
|
|
||||||
<text> (Cpt.) (@student.RegionalID)</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
else if (team.EventDefinition.EventFormat == EventFormat.Individual)
|
|
||||||
{
|
|
||||||
foreach (var student in students)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@team.EventDefinition.Name #@student.RegionalID
|
|
||||||
</td>
|
|
||||||
<td>@team.EventDefinition.Notes</td>
|
|
||||||
<td>@student.FirstNameLastName</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
@{
|
|
||||||
var s = Model.Item1.SelectMany(t => t.Students).Distinct().OrderBy(s => s.LastNameFirstName);
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td>Regional ID</td>
|
|
||||||
<td>Team</td>
|
|
||||||
<td>Time Slot</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var student in s)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@student.FirstNameLastName</td>
|
|
||||||
<td>@student.RegionalID</td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
var teams = Model.Item1.Where(t => t.Students.Contains(student));
|
|
||||||
foreach (var team in teams/*.OrderBy(t => t.RegionalTimeSlotObj)*/)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td>@team.Name
|
|
||||||
@if (team.Captain == student)
|
|
||||||
{
|
|
||||||
<text>(Cpt.)</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>@team.EventDefinition.Notes @*@team.RegionalTimeSlot*@</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
<p>No regional eventDefinitions: @string.Join(", ", @Model.Item2.Select(s=> s.FirstName))</p>
|
|
||||||
|
|
||||||
@functions
|
|
||||||
{
|
|
||||||
public void GetOrderClass(int pick)
|
|
||||||
{
|
|
||||||
@Html.Raw(LabelHelper.GetOrderClass(pick))
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetTeamClass(Team team)
|
|
||||||
{
|
|
||||||
if (team.EventDefinition.RegionalEvent)
|
|
||||||
{
|
|
||||||
@Html.Raw("regional")
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
|
|
||||||
@model Tuple<IList<Team>[], IEnumerable<Student>[], Web.Controllers.HomeController.ScheduleOptions>
|
|
||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Schedule";
|
|
||||||
var slot = 0;
|
|
||||||
|
|
||||||
var schedule = Model.Item1;
|
|
||||||
var unassignedStudents = Model.Item2;
|
|
||||||
var allStudents = schedule.SelectMany(t => t).SelectMany(t => t.Students).Distinct().ToList();
|
|
||||||
var allMeetingTeams = schedule.SelectMany(t => t).Distinct();
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="bluewhite p-5">
|
|
||||||
@{
|
|
||||||
List<(Student student, IEnumerable<Team> teams)> overlaps;
|
|
||||||
}
|
|
||||||
@foreach (var timeslot in schedule)
|
|
||||||
{
|
|
||||||
overlaps = TeamSchedulerSolution.GetStudentTeamOverlaps(timeslot).ToList();
|
|
||||||
var partialTeams = timeslot.Where(t => t is PartialTeam && t.EventDefinition.EventFormat is not EventFormat.Individual);
|
|
||||||
var fullTeams = timeslot.Where(t => !partialTeams.Contains(t));
|
|
||||||
|
|
||||||
<h3>Time Slot @(slot + 1)</h3>
|
|
||||||
<p>@Model.Item3.Date.ToShortDateString()</p>
|
|
||||||
|
|
||||||
<h4>Teams</h4>
|
|
||||||
<table class="table schedule">
|
|
||||||
<thead>
|
|
||||||
<th class="col-2"></th>
|
|
||||||
<th class="col-3"></th>
|
|
||||||
<th class="col-5"></th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var team in fullTeams.Where(t => t.EventDefinition.EventFormat is EventFormat.Team).OrderBy(t => t.Name))
|
|
||||||
{
|
|
||||||
@await Html.PartialAsync("ScheduleTeamPartial", Tuple.Create(team, overlaps, Model.Item3.AbsentStudents))
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
if (partialTeams.Any())
|
|
||||||
{
|
|
||||||
<table class="table schedule">
|
|
||||||
<thead>
|
|
||||||
<th class="col-2"></th>
|
|
||||||
<th class="col-3"></th>
|
|
||||||
<th class="col-5"></th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var team in partialTeams.OrderBy(t => t.Name))
|
|
||||||
{
|
|
||||||
@await Html.PartialAsync("ScheduleTeamPartial", Tuple.Create(team, overlaps, Model.Item3.AbsentStudents))
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h4>Individual</h4>
|
|
||||||
<p><i>Use time for individual eventDefinition or to <strong>work on a team eventDefinition</strong> </i></p>
|
|
||||||
<table class="table schedule">
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
@foreach (var team in fullTeams.Where(t => t.EventDefinition.EventFormat is EventFormat.Individual).OrderBy(t => t.Name))
|
|
||||||
{
|
|
||||||
<td>
|
|
||||||
<strong>@team.Captain?.FirstName</strong> (@team.EventDefinition.Name@if(team.EventDefinition.RegionalEvent){ @* <text> (<i>Regional</i>)</text> *@})
|
|
||||||
</td>
|
|
||||||
@* @await Html.PartialAsync("ScheduleTeamPartial", Tuple.Create(team, overlaps)) *@
|
|
||||||
}
|
|
||||||
|
|
||||||
@foreach (var student in unassignedStudents[slot])
|
|
||||||
{
|
|
||||||
<td>
|
|
||||||
<strong>@student.FirstName</strong>
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
slot++;
|
|
||||||
}
|
|
||||||
|
|
||||||
<h3>Missed team eventDefinitions for today</h3>
|
|
||||||
<table class="table schedule">
|
|
||||||
@foreach (var student in allStudents.OrderBy(s => s.FirstName))
|
|
||||||
{
|
|
||||||
var studentMeetings = student.Teams.Where(t => allMeetingTeams.Any(mt => mt.Name == t.Name && mt.Students.Contains(student)));
|
|
||||||
var studentMissed = student.Teams.Where(t => studentMeetings.All(mt => mt.Name != t.Name)).ToList();
|
|
||||||
var studentMissedTeams = studentMissed.Where(t => t.EventDefinition.EventFormat is EventFormat.Team);
|
|
||||||
var studentMissedIndividual = studentMissed.Where(t => t.EventDefinition.EventFormat is EventFormat.Individual);
|
|
||||||
if (studentMissedTeams.Any())
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td class="col-4">@student.FirstName</td>
|
|
||||||
<td>
|
|
||||||
@string.Join(", ", studentMissedTeams.Select(t => t.ToStringWithIndividualAndRegional()))
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
if (studentMissedIndividual.Any())
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td class="col-4">@student.FirstName</td>
|
|
||||||
<td>
|
|
||||||
@string.Join(", ", studentMissedIndividual.Select(t => t.ToStringWithIndividualAndRegional()))
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
@functions
|
|
||||||
{
|
|
||||||
private void GetTeamClass(Team team)
|
|
||||||
{
|
|
||||||
if (team.EventDefinition.RegionalEvent)
|
|
||||||
{
|
|
||||||
@Html.Raw("regional")
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
@model Tuple<Core.Entities.Team, List<(Student student, IEnumerable<Team> teams)>, string[]?>
|
|
||||||
|
|
||||||
@{
|
|
||||||
var team = Model.Item1;
|
|
||||||
var overlaps = Model.Item2;
|
|
||||||
}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="@{ GetTeamClass(team); } col-6" >
|
|
||||||
<strong>@team</strong>
|
|
||||||
@if (!string.IsNullOrEmpty(team.EventDefinition.EventAttributes()))
|
|
||||||
{
|
|
||||||
<i>(@team.EventDefinition.EventAttributes())</i>
|
|
||||||
}
|
|
||||||
<small><i>@team.EventDefinition.SemifinalistActivity</i></small>
|
|
||||||
@* @if (team.EventDefinition.StatePresubmission)
|
|
||||||
{
|
|
||||||
<small>(pre-submission)</small>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (team.EventDefinition.StatePretesting)
|
|
||||||
{
|
|
||||||
<small>(pre-testing)</small>
|
|
||||||
} *@
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
@{ var first = true; }
|
|
||||||
@foreach (var student in team.Students.OrderByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)))
|
|
||||||
{
|
|
||||||
if (!first)
|
|
||||||
{
|
|
||||||
<text>, </text>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
@if (overlaps.Any(t => t.student == student))
|
|
||||||
{
|
|
||||||
<span style="color: #F66">@student.FirstName</span>
|
|
||||||
}
|
|
||||||
else if(Model.Item3?.Any(s => student.FirstNameLastName.Contains(s))== true)
|
|
||||||
{
|
|
||||||
<span style="color: lightgray">@student.FirstName</span>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@student.FirstName
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (team.Captain == student)
|
|
||||||
{
|
|
||||||
<span class="text-warning small">•</span>
|
|
||||||
@* <i class="bi bi-chevron-double-up text-warning small"></i> *@
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@team.EventDefinition.Documentation
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
@functions
|
|
||||||
{
|
|
||||||
private void GetTeamClass(Team team)
|
|
||||||
{
|
|
||||||
// if (team.EventDefinition.RegionalEvent)
|
|
||||||
// {
|
|
||||||
// @Html.Raw("regional")
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,358 +0,0 @@
|
|||||||
@using System.Text.RegularExpressions
|
|
||||||
@using Core.Entities
|
|
||||||
@using Core.Utility
|
|
||||||
@model Tuple<Team[], Student[], IDictionary<EventDefinition, List<EventOccurrence>>>
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Teams";
|
|
||||||
var eventOccurrences = Model.Item3;
|
|
||||||
}
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>State EventDefinitions</h2>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Team</td>
|
|
||||||
<td>Team Members</td>
|
|
||||||
<td>Dates</td>
|
|
||||||
<td>Materials</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var team in Model.Item1)
|
|
||||||
{
|
|
||||||
var students = team.Students;
|
|
||||||
@if (true @* team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Team *@)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@* <strong class="@{ GetTeamClass(team);}">@team.Name</strong> *@
|
|
||||||
@team.Name @* #@team.TeamNumber
|
|
||||||
*@ </td>
|
|
||||||
<td>
|
|
||||||
@{
|
|
||||||
var first = true;
|
|
||||||
}
|
|
||||||
@foreach (var student in students.OrderByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
|
||||||
{
|
|
||||||
@if (!first)
|
|
||||||
{
|
|
||||||
<text>, </text>
|
|
||||||
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
@student.FirstNameLastName @if (team.Captain == student)
|
|
||||||
{
|
|
||||||
<text> (Cpt. @team.Captain.StateID)</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@if (team.EventDefinition.StatePresubmission)
|
|
||||||
{
|
|
||||||
<text>Pre-submission due Friday, March 14</text>
|
|
||||||
}
|
|
||||||
@if (team.EventDefinition.StatePretesting)
|
|
||||||
{
|
|
||||||
<text>Pre-testing Wednesday, April 2nd</text>
|
|
||||||
}
|
|
||||||
@if (team.EventDefinition.StatePreliminaryRound)
|
|
||||||
{
|
|
||||||
<text>Preliminary and Semifinalist Rounds</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@team.EventDefinition.Documentation
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
@* else if (team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Individual)
|
|
||||||
{
|
|
||||||
foreach (var student in students)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@team.EventDefinition.Name - @student.FirstNameLastName (@student.RegionalID)
|
|
||||||
</td>
|
|
||||||
<td>@team.RegionalTimeSlot</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
} *@
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@{
|
|
||||||
var s = Model.Item2.OrderBy(s => s.LastNameFirstName);
|
|
||||||
|
|
||||||
@foreach (var student in s)
|
|
||||||
{
|
|
||||||
<div class="container nobrk pt-5" style="page-break-before: always;">
|
|
||||||
|
|
||||||
<h3>@student.FirstNameLastName - @student.StateID</h3>
|
|
||||||
<h4>TSA 2025 TN State Schedule</h4>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col col-2">EventDefinitions</div>
|
|
||||||
<div class="col">
|
|
||||||
@foreach (var ev in student.Teams)
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col col-2">
|
|
||||||
@if (ev.EventDefinition.EventFormat is EventFormat.Team)
|
|
||||||
{
|
|
||||||
@ev.TeamNumber
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@ev.Captain.StateID
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<text> @ev.Name
|
|
||||||
@if (ev.Captain == student)
|
|
||||||
{
|
|
||||||
<strong>(Cpt.)</strong>
|
|
||||||
}
|
|
||||||
</text>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
@ev.EventDefinition.SemifinalistActivity
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Time</td>
|
|
||||||
<td>EventDefinition</td>
|
|
||||||
<td></td>
|
|
||||||
<td>Location</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
@foreach (var date in
|
|
||||||
eventOccurrences
|
|
||||||
.Where(eo =>
|
|
||||||
student.Teams.Select(t => t.EventDefinition).Any(a => a == eo.Key)
|
|
||||||
|| eo.Key == EventDefinition.GeneralSchedule
|
|
||||||
|| (eo.Key == EventDefinition.VotingDelegates && student.VotingDelegate))
|
|
||||||
.SelectMany(eo => eo.Value.Select(v => Tuple.Create(v, eo.Key)))
|
|
||||||
.GroupBy(de => de.Item1.Date + ", " + de.Item1.StartTime.DayOfWeek)
|
|
||||||
.OrderBy(d => d.Key)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var eventsForThisDay =
|
|
||||||
date
|
|
||||||
.Where(de => de.Item1.Name != "Judging")
|
|
||||||
.Where(de => de.Item1.StartTime > new DateTime(2024, 3, 1))
|
|
||||||
// filter out occurrences where non-captain
|
|
||||||
.Where(de =>
|
|
||||||
!de.Item1.SignupSubmitPickup
|
|
||||||
|| de.Item2.EventFormat is EventFormat.Individual
|
|
||||||
|| student.Teams.Any(t => t.Captain == student && t.EventDefinition == de.Item2)
|
|
||||||
)
|
|
||||||
.OrderBy(de => de.Item1.StartTime);
|
|
||||||
|
|
||||||
@if (!eventsForThisDay.Any())
|
|
||||||
continue;
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td colspan="4"><strong>@date.Key</strong></td>
|
|
||||||
</tr>
|
|
||||||
@foreach (var eventOccurrence in eventsForThisDay.OrderBy(de => de.Item1.StartTime))
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@eventOccurrence.Item1.Time</td>
|
|
||||||
<td>@eventOccurrence.Item2.Name</td>
|
|
||||||
<td>@eventOccurrence.Item1.Name
|
|
||||||
@if (eventOccurrence.Item1.Name.Contains("Pick") && eventOccurrence.Item2.EventFormat is EventFormat.Team)
|
|
||||||
{
|
|
||||||
<br/>
|
|
||||||
<text>or coordinate with a teammate</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>@eventOccurrence.Item1.Location
|
|
||||||
@if (eventOccurrence.Item1.Location == "Online" && eventOccurrence.Item1.Name.Contains("Sign-up") ) { <text>by Advisor</text>}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@{
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>Combined Schedule</h2>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Time</td>
|
|
||||||
<td>Team</td>
|
|
||||||
<td></td>
|
|
||||||
<td>Location</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var eventsForDate
|
|
||||||
in Model.Item3.SelectMany(eo => eo.Value.Select(e => Tuple.Create(e, eo.Key)))
|
|
||||||
.Where(de => de.Item1.Name != "Judging")
|
|
||||||
.GroupBy(eo => eo.Item1.StartTime.Date)
|
|
||||||
.OrderBy(eo => eo.Key)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
<tr><td colspan="4"><strong>@eventsForDate.Key.ToString("MMMM d") </strong> </td></tr>
|
|
||||||
@foreach (var occurrence in eventsForDate.OrderBy(o => o.Item1.StartTime))
|
|
||||||
{
|
|
||||||
var teams = Model.Item1.Where(t => t.EventDefinition == occurrence.Item2);
|
|
||||||
if (occurrence.Item2 != EventDefinition.GeneralSchedule && occurrence.Item2 != EventDefinition.VotingDelegates && !teams.Any())
|
|
||||||
continue;
|
|
||||||
<tr>
|
|
||||||
<td>@occurrence.Item1.Time</td>
|
|
||||||
<td>
|
|
||||||
@if (occurrence.Item2 == EventDefinition.GeneralSchedule)
|
|
||||||
{
|
|
||||||
<text>Everyone</text>
|
|
||||||
}
|
|
||||||
else if (occurrence.Item2 == EventDefinition.VotingDelegates)
|
|
||||||
{
|
|
||||||
<text>Voting Delegates - @string.Join(", ", Model.Item2.Where(stu => stu.VotingDelegate).Select(stu => stu.FirstName))</text>
|
|
||||||
}
|
|
||||||
@foreach (var team in teams)
|
|
||||||
{
|
|
||||||
<text>@team</text>
|
|
||||||
|
|
||||||
|
|
||||||
<text> - @string.Join(", ", team.Students.Select(stu => stu.FirstName))</text>
|
|
||||||
}
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@occurrence.Item1.Name
|
|
||||||
|
|
||||||
@if (occurrence.Item1.SignupSubmitPickup)
|
|
||||||
{
|
|
||||||
<br/>
|
|
||||||
<text>1 Team Member</text>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (occurrence.Item1.Name.Contains("Semifinalist") && (occurrence.Item1.Name.Contains("Interview") || occurrence.Item1.Name.Contains("Presentation")))
|
|
||||||
{
|
|
||||||
<br/>
|
|
||||||
<text>@Regex.Match(@occurrence.Item2.SemifinalistActivity, @"(?<=\().*?(?=\))").Value</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>@occurrence.Item1.Location</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>Students</h2>
|
|
||||||
<table class="table-primary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td>ID</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var student in Model.Item2.OrderByDescending(s => s.Officer.Contains("President")).ThenBy(s => s.LastNameFirstName))
|
|
||||||
{
|
|
||||||
var assignments
|
|
||||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
|
||||||
|
|
||||||
.Distinct()
|
|
||||||
.OrderBy(e => e.Name).ToList();
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<strong>@student.FirstNameLastName</strong> @if (!string.IsNullOrEmpty(student.Officer))
|
|
||||||
{
|
|
||||||
<text>(@student.Officer)</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>@student.StateID</td>
|
|
||||||
|
|
||||||
@foreach (var t in assignments)
|
|
||||||
{
|
|
||||||
<td>
|
|
||||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
|
||||||
{
|
|
||||||
@t.Name
|
|
||||||
@if (t.Captain == student)
|
|
||||||
{
|
|
||||||
<text> (Captain)</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@t.EventDefinition.Name
|
|
||||||
}
|
|
||||||
@{
|
|
||||||
if (t.EventDefinition.EventFormat == EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<sup class="activity">(ind)</sup>
|
|
||||||
}
|
|
||||||
if (t.EventDefinition.OnSiteActivity)
|
|
||||||
{
|
|
||||||
<sup class="activity">(act)</sup>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<h2>Students</h2>
|
|
||||||
@foreach (var student in Model.Item2.OrderByDescending(s => s.Officer.Contains("President")).ThenBy(s => s.LastNameFirstName))
|
|
||||||
{
|
|
||||||
var assignments
|
|
||||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
|
||||||
|
|
||||||
.Distinct()
|
|
||||||
.OrderBy(e => e.Name).ToList();
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<strong>@student.FirstNameLastName</strong>
|
|
||||||
@if (!string.IsNullOrEmpty(student.Officer))
|
|
||||||
{
|
|
||||||
<text>(@student.Officer)</text>
|
|
||||||
}
|
|
||||||
<text>@student.StateID</text>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
@foreach (var t in assignments)
|
|
||||||
{
|
|
||||||
<p>
|
|
||||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
|
||||||
{
|
|
||||||
@t.Name
|
|
||||||
@if (t.Captain == student)
|
|
||||||
{
|
|
||||||
<text> (Captain)</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@t.EventDefinition.Name
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
@model Tuple<Student[]>
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Student Handout Page";
|
|
||||||
}
|
|
||||||
|
|
||||||
@foreach (var s in Model.Item1.OrderBy(n => n.FirstName))
|
|
||||||
{
|
|
||||||
<div class="container nobrk pt-5">
|
|
||||||
<p><i>@DateTime.Today.ToShortDateString()</i></p>
|
|
||||||
<h2><i>TSA teams and eventDefinitions:</i> @s.FirstNameLastName </h2>
|
|
||||||
|
|
||||||
@foreach (var team in
|
|
||||||
s.Teams.OrderByDescending(t => t.EventDefinition.EventFormat == EventFormat.Team)
|
|
||||||
.ThenByDescending(t => t.EventDefinition.LevelOfEffort)
|
|
||||||
.ThenByDescending(t => t.Name))
|
|
||||||
{
|
|
||||||
var evt = team.EventDefinition;
|
|
||||||
<div>
|
|
||||||
@if (evt.RegionalEvent)
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<i>Regional EventDefinition</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<h5>@evt.Name @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)</h5>
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
@if (evt.EventFormat is EventFormat.Team)
|
|
||||||
{
|
|
||||||
<html>
|
|
||||||
<strong>Teammates</strong><br/>
|
|
||||||
@string.Join(", ", team.Students.OrderByDescending(s => s.Grade + s.TsaYear).ThenBy(s => s.FirstName).Where(tm => tm != s).Select(tm => tm.FirstName))
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<html>
|
|
||||||
<strong>@evt.EventFormat</strong>
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
@evt.SemifinalistActivity
|
|
||||||
@if (evt.StatePresubmission)
|
|
||||||
{<text>, State Presubmission <strong>due March 14th</strong></text>}
|
|
||||||
@if (evt.StatePretesting)
|
|
||||||
{<text>, State Pre-testing <strong>April 2nd</strong></text>}
|
|
||||||
@if (evt.StatePreliminaryRound)
|
|
||||||
{<text>, State Preliminary and Semifinalist Rounds</text>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div div class="row mt-3">
|
|
||||||
<div class="col">@evt.Description</div></div>
|
|
||||||
@if (!string.IsNullOrEmpty(evt.Theme))
|
|
||||||
{
|
|
||||||
<div div class="row mt-2">
|
|
||||||
<div class="col-3 text-center"><i>Theme for 2024-25:</i></div>
|
|
||||||
<div class="col">@evt.Theme</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(evt.Documentation))
|
|
||||||
{
|
|
||||||
<div div class="row mt-2">
|
|
||||||
<div class="col-3 text-center"><i>Materials:</i></div>
|
|
||||||
<div class="col">@evt.Documentation</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
@using Core.Utility
|
|
||||||
@model Tuple<EventDefinition[], Student[], EventStudentRankings[], Team[], AssignmentParameters>
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Home Page";
|
|
||||||
var maxStudentPicks = Model.Item3.MaxBy(picks => picks.StudentRankings.Count).StudentRankings.Count;
|
|
||||||
var parameters = Model.Item5;
|
|
||||||
var unassignedEvents = Model.Item1.Where(e => Model.Item4.All(t => t.EventDefinition != e));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
<table class="table-primary">
|
|
||||||
<tr>
|
|
||||||
<td colspan="8">
|
|
||||||
Effort Limit:<strong>@parameters.EffortUpperBound</strong>
|
|
||||||
Require Regionals:<strong>@parameters.RequireRegional</strong>
|
|
||||||
Require On-Site Activity:<strong>@parameters.RequireOnSite</strong>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<br/>
|
|
||||||
<table class="table-primary">
|
|
||||||
<thead>
|
|
||||||
<tr><td>Student</td><td>Level of Effort Total</td></tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var student in Model.Item2.OrderBy(s => s.FirstName))
|
|
||||||
{
|
|
||||||
var assignments
|
|
||||||
= Model.Item4.Where(ea => ea.Students.Contains(student))
|
|
||||||
.Select(ea => ea.EventDefinition)
|
|
||||||
.Distinct()
|
|
||||||
@* .OrderBy(e =>
|
|
||||||
{
|
|
||||||
|
|
||||||
var r = student.RankedEventPicks.IndexOf(e);
|
|
||||||
r = r >= 0 ? r : 10;
|
|
||||||
//r = r * (4 - e.LevelOfEffort.Value);
|
|
||||||
return r;
|
|
||||||
}) *@
|
|
||||||
;
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td><strong>@student.FirstName</strong></td>
|
|
||||||
<td>@assignments.Sum(a => a.LevelOfEffort)</td>
|
|
||||||
|
|
||||||
@foreach (var evt in assignments)
|
|
||||||
{
|
|
||||||
@* var h = student.RankedEventPicks.IndexOf(evt) + 1; *@
|
|
||||||
@* @{ class="GetOrderClass(h);" } *@
|
|
||||||
<td class="">
|
|
||||||
@evt.ShortName @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
|
||||||
@{
|
|
||||||
if (evt.EventFormat == EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<sup class="activity">(ind)</sup>
|
|
||||||
}
|
|
||||||
if (evt.RegionalEvent)
|
|
||||||
{
|
|
||||||
<sup class="activity">(reg)</sup>
|
|
||||||
}
|
|
||||||
if (evt.OnSiteActivity)
|
|
||||||
{
|
|
||||||
<sup class="activity">(act)</sup>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<table><tr><td> </td></tr></table>
|
|
||||||
|
|
||||||
<table class="table-primary">
|
|
||||||
<thead>
|
|
||||||
<tr><td>Teams</td></tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var evt in Model.Item1.OrderByDescending(e => e.EventFormat is EventFormat.Team))
|
|
||||||
{
|
|
||||||
var assignments = Model.Item4.FirstOrDefault(i => i.EventDefinition == evt);
|
|
||||||
@if (assignments == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
<tr class="table-primary">
|
|
||||||
<td class="table-primary"><strong>@evt.Name</strong></td>
|
|
||||||
<td colspan="1">
|
|
||||||
@Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
|
||||||
@if (evt.EventFormat is EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<text>(ind)</text>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (evt.RegionalEvent)
|
|
||||||
{
|
|
||||||
<text>(reg)</text>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (evt.OnSiteActivity)
|
|
||||||
{
|
|
||||||
<text>(act)</text>
|
|
||||||
}@evt.TeamSize</td>
|
|
||||||
@*<td style="nowrap">@evt.MaxTeamCountState</td>*@
|
|
||||||
@if (assignments != null)
|
|
||||||
{
|
|
||||||
foreach (var student in assignments.Students)
|
|
||||||
{
|
|
||||||
var h = student.RankedEventPicks.IndexOf(evt) + 1;
|
|
||||||
<td class="@{ GetOrderClass(h); }">@student.FirstName</td>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<td></td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
@{
|
|
||||||
var unassigned = string.Join(", ", unassignedEvents.Select(e => e.Name));
|
|
||||||
}
|
|
||||||
<td colspan="8">Unassigned EventDefinitions: @unassigned</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<table><tr><td></td></tr></table>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Grade</th>
|
|
||||||
<th>TSA Year</th>
|
|
||||||
<th>1</th>
|
|
||||||
<th>2</th>
|
|
||||||
<th>3</th>
|
|
||||||
<th>4</th>
|
|
||||||
<th>5</th>
|
|
||||||
</thead>
|
|
||||||
@foreach (var s in Model.Item2.OrderBy(n => n.FirstName))
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@s.FirstName</td>
|
|
||||||
<td>@s.Grade.Ordinal()</td>
|
|
||||||
<td>@s.TsaYear.Ordinal()</td>
|
|
||||||
@for (var i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
var h = i + 1;
|
|
||||||
var evt = s.RankedEventPicks.Skip(i).FirstOrDefault();
|
|
||||||
if (evt == null)
|
|
||||||
continue;
|
|
||||||
<td class="@{ GetOrderClass(h); }">
|
|
||||||
@evt.ShortName @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
|
||||||
@if (evt.EventFormat == EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<sup class="activity">(ind)</sup>
|
|
||||||
}
|
|
||||||
@if (evt.RegionalEvent)
|
|
||||||
{
|
|
||||||
<sup class="activity">(reg)</sup>
|
|
||||||
}
|
|
||||||
@if (evt.OnSiteActivity)
|
|
||||||
{
|
|
||||||
<sup class="activity">(act)</sup>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><td>EventDefinition</td><td>Level of Effort</td><td>Individual</td><td>Regional</td><td>On-site Activity</td><td>Team Size</td><td>Max Team Count</td></tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var evt in Model.Item1)
|
|
||||||
{
|
|
||||||
var esp = Model.Item3.FirstOrDefault(i => i.EventDefinition == evt);
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@evt.Name
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>@Html.Partial("EffortStarsPartial", evt.LevelOfEffort)</td>
|
|
||||||
<td>@if (evt.EventFormat is EventFormat.Individual) { <text>ind</text> }</td>
|
|
||||||
<td>@if (evt.RegionalEvent) { <text>reg</text> }</td>
|
|
||||||
<td>@if (evt.OnSiteActivity) { <text>act</text> }</td>
|
|
||||||
<td style="nowrap">@evt.TeamSize</td>
|
|
||||||
<td style="nowrap">@evt.MaxTeamCountState</td>
|
|
||||||
|
|
||||||
@for (var i = 0; i < maxStudentPicks; i++)
|
|
||||||
{
|
|
||||||
var d = esp?.StudentRankings.Skip(i).FirstOrDefault();
|
|
||||||
<td class="@{GetOrderClass(d?.Item2 ?? int.MaxValue);}">@d?.Item1.FirstName</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
@functions
|
|
||||||
{
|
|
||||||
public void GetOrderClass(int pick)
|
|
||||||
{
|
|
||||||
@Html.Raw(LabelHelper.GetOrderClass(pick))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
|
|
||||||
@model Tuple<Student[]>
|
|
||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Student Teams";
|
|
||||||
}
|
|
||||||
|
|
||||||
<table class="table-primary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td><td>Level of Effort Total</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var student in Model.Item1.OrderBy(s => s.FirstName))
|
|
||||||
{
|
|
||||||
var assignments
|
|
||||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
|
||||||
.Select(ea => ea.EventDefinition)
|
|
||||||
.Distinct()
|
|
||||||
@* .OrderBy(e =>
|
|
||||||
{
|
|
||||||
var r = student.RankedEventPicks.IndexOf(e);
|
|
||||||
r = r >= 0 ? r : 10;
|
|
||||||
//r = r * (4 - e.LevelOfEffort.Value);
|
|
||||||
return r;
|
|
||||||
}) *@
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<strong>@student.FirstNameLastName</strong> @if(!string.IsNullOrEmpty(student.Officer )) {<text>(@student.Officer)</text>}
|
|
||||||
</td>
|
|
||||||
<td>@assignments.Sum(a => a.LevelOfEffort)</td>
|
|
||||||
|
|
||||||
@foreach (var evt in assignments)
|
|
||||||
{
|
|
||||||
@* var h = student.RankedEventPicks.IndexOf(evt) + 1; *@
|
|
||||||
@* @{ GetOrderClass(h); } *@
|
|
||||||
<td class="">
|
|
||||||
@evt.ShortName @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
|
||||||
@{
|
|
||||||
if (evt.EventFormat == EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<sup class="activity">(ind)</sup>
|
|
||||||
}
|
|
||||||
if (evt.RegionalEvent)
|
|
||||||
{
|
|
||||||
<sup class="activity">(reg)</sup>
|
|
||||||
}
|
|
||||||
if (evt.OnSiteActivity)
|
|
||||||
{
|
|
||||||
<sup class="activity">(act)</sup>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
@*
|
|
||||||
<table class="table">
|
|
||||||
<tr>
|
|
||||||
<th>Student</th>
|
|
||||||
<th>Team</th>
|
|
||||||
<th>Teammates</th>
|
|
||||||
</tr>
|
|
||||||
@foreach (var student in Model.Item1.RankBy(s => s.Name))
|
|
||||||
{
|
|
||||||
var teams = student.Teams;
|
|
||||||
|
|
||||||
<tr class="table-primary">
|
|
||||||
<td class="table-primary">
|
|
||||||
<strong>@student.FirstNameLastName</strong>
|
|
||||||
@if (!string.IsNullOrEmpty(student.Officer))
|
|
||||||
{
|
|
||||||
<text>(</text> @student.Officer <text>)</text>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@foreach (var team in student.Teams)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td class="@{ GetTeamClass(team); }">@team.Name
|
|
||||||
@{
|
|
||||||
var ind = new List<string>();
|
|
||||||
if (team.Captain == student)
|
|
||||||
{
|
|
||||||
<span>(Cpt .)</span>
|
|
||||||
}
|
|
||||||
if (!team.EventDefinition.InterviewOrPresentation)
|
|
||||||
{
|
|
||||||
ind.Add("a");
|
|
||||||
}
|
|
||||||
if (team.EventDefinition.EventDefinitionFormat is EventDefinitionEventDefinitionFormat.Individual)
|
|
||||||
{
|
|
||||||
ind.Add("i");
|
|
||||||
}
|
|
||||||
if (ind.Count > 0)
|
|
||||||
{
|
|
||||||
<span class="activity">(@string.Join(",", ind))</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@string.Join(", ", team.Students.Where(s => s != student).RankByDescending(s => s.TsaYear + s.Grade).Select(s => s.FirstName))
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
<p>
|
|
||||||
(a) denotes an eventDefinition that has activity other than interview or presentation at state
|
|
||||||
<br />
|
|
||||||
(i) denotes an individual eventDefinition
|
|
||||||
</p> *@
|
|
||||||
|
|
||||||
|
|
||||||
<table class="table-primary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var student in Model.Item1.OrderBy(s => s.LastNameFirstName))
|
|
||||||
{
|
|
||||||
var assignments
|
|
||||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
|
||||||
.Select(ea => ea.EventDefinition)
|
|
||||||
.Distinct()
|
|
||||||
@* .OrderBy(e =>
|
|
||||||
{
|
|
||||||
var r = student.RankedEventPicks.IndexOf(e);
|
|
||||||
r = r >= 0 ? r : 10;
|
|
||||||
//r = r * (4 - e.LevelOfEffort.Value);
|
|
||||||
return r;
|
|
||||||
}) *@
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<strong>@student.FirstName</strong>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
@foreach (var evt in assignments)
|
|
||||||
{
|
|
||||||
<td>
|
|
||||||
@evt.Name
|
|
||||||
@{
|
|
||||||
if (evt.EventFormat == EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<sup class="activity">(individual)</sup>
|
|
||||||
}
|
|
||||||
if (evt.RegionalEvent)
|
|
||||||
{
|
|
||||||
<sup class="activity">(regional)</sup>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
@functions
|
|
||||||
{
|
|
||||||
public void GetOrderClass(int pick)
|
|
||||||
{
|
|
||||||
@Html.Raw(LabelHelper.GetOrderClass(pick))
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetTeamClass(Team team)
|
|
||||||
{
|
|
||||||
// if (team.EventDefinition.RegionalEvent)
|
|
||||||
// {
|
|
||||||
// @Html.Raw("regional");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
@model System.Tuple<Core.Entities.Team[],Core.Entities.Student[]>
|
|
||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Team Grid";
|
|
||||||
}
|
|
||||||
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr> <th></th>
|
|
||||||
@foreach (var team in Model.Item1)
|
|
||||||
{
|
|
||||||
if (team.Name == team.EventDefinition.Name)
|
|
||||||
{
|
|
||||||
<td style="writing-mode: vertical-rl;text-orientation:sideways">
|
|
||||||
@team.EventDefinition.ShortName
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<td style="writing-mode: vertical-rl;text-orientation:sideways">
|
|
||||||
@team.Name
|
|
||||||
</td>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var student in Model.Item2.OrderBy(s => s.FirstName))
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@student.FirstName</td>
|
|
||||||
@foreach (var team in Model.Item1)
|
|
||||||
{
|
|
||||||
@if (team.Students.Contains(student))
|
|
||||||
{
|
|
||||||
<td><strong> X</strong></td>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<td></td>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
@using Core.Entities
|
|
||||||
@model Tuple<Team[]>
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Teams";
|
|
||||||
}
|
|
||||||
|
|
||||||
<table>
|
|
||||||
@foreach (var team in Model.Item1.Where(t => t.EventDefinition.Documentation.Contains("Port")))
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@team.Name @if (team.EventDefinition.EventFormat is EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<text>(ind)</text>
|
|
||||||
}
|
|
||||||
</td><td>@team.EventDefinition.Documentation</td>
|
|
||||||
@foreach(var student in @team.Students.OrderByDescending(s => s.TsaYear + s.Grade))
|
|
||||||
{
|
|
||||||
<td>@student.FirstName</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
@foreach (var student in Model.Item1.SelectMany(t => t.Students).Distinct().Where(s => s.Teams.Any(t => t.EventDefinition.Name.Contains("Port"))))
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@student.FirstName</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<table class="table-primary">
|
|
||||||
<thead>
|
|
||||||
<tr><td>Teams</td></tr>
|
|
||||||
</thead>
|
|
||||||
@{
|
|
||||||
var ind = false;
|
|
||||||
}
|
|
||||||
@foreach (var team in Model.Item1.OrderByDescending(t => t.EventDefinition.EventFormat is EventFormat.Team))
|
|
||||||
{
|
|
||||||
@if (!ind && team.EventDefinition.EventFormat is EventFormat.Individual)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td><hr/></td><td>Individual</td><td><hr/></td>
|
|
||||||
</tr>
|
|
||||||
ind = true;
|
|
||||||
}
|
|
||||||
<tr class="table-primary">
|
|
||||||
<td class="table-primary"><strong>@team.Name</strong></td>
|
|
||||||
<td colspan="1">
|
|
||||||
@* @Html.Partial("EffortStarsPartial", team.EventDefinition.LevelOfEffort) *@
|
|
||||||
@* @if (team.EventDefinition.RegionalEventDefinition)
|
|
||||||
{
|
|
||||||
<text>(reg)</text>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (team.EventDefinition.OnSiteActivity)
|
|
||||||
{
|
|
||||||
<text>(act)</text>
|
|
||||||
} *@
|
|
||||||
@team.EventDefinition.TeamSize
|
|
||||||
</td>
|
|
||||||
@*<td style="nowrap">@evt.MaxTeamCountState</td>*@
|
|
||||||
@foreach (var student in team.Students.OrderByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
|
||||||
{
|
|
||||||
<td>@student.FirstName @if (team.Captain == student) { <text>(Cpt.)</text>}</td>
|
|
||||||
}
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
@*
|
|
||||||
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>EventDefinition</td>
|
|
||||||
<td>Pre-submission</td>
|
|
||||||
<td>Notes</td>
|
|
||||||
<td>Team Members</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
@foreach (var team in Model.Item1)
|
|
||||||
{
|
|
||||||
var students = team.Students;
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@team.Name
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
@foreach (var student in students.RankByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
|
||||||
{
|
|
||||||
<td>@student.FirstNameLastName @if (team.Captain == student) { <text>(Cpt.)</text>}</td>
|
|
||||||
}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="eventDefinition-desc" colspan="4">Team Size: @team.EventDefinition.TeamSize, Max Teams: @team.EventDefinition.MaxTeamCountState
|
|
||||||
@{
|
|
||||||
if (!team.EventDefinition.InterviewOrPresentation)
|
|
||||||
{
|
|
||||||
<span class="activity"> (a)</span>
|
|
||||||
}
|
|
||||||
if (team.EventDefinition.EventDefinitionFormat is EventDefinitionEventDefinitionFormat.Individual)
|
|
||||||
{
|
|
||||||
<span class="activity"> (i)</span>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
<p>
|
|
||||||
(a) denotes an eventDefinition that has activity other than interview or presentation at state
|
|
||||||
<br />
|
|
||||||
(i) denotes an individual eventDefinition
|
|
||||||
</p>
|
|
||||||
*@
|
|
||||||
|
|
||||||
@functions
|
|
||||||
{
|
|
||||||
public void GetOrderClass(int pick)
|
|
||||||
{
|
|
||||||
@Html.Raw(LabelHelper.GetOrderClass(pick))
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetTeamClass(Team team)
|
|
||||||
{
|
|
||||||
// if (team.EventDefinition.RegionalEvent)
|
|
||||||
// {
|
|
||||||
// @Html.Raw("regional");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
@model ErrorViewModel
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Error";
|
|
||||||
}
|
|
||||||
|
|
||||||
<h1 class="text-danger">Error.</h1>
|
|
||||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
|
||||||
|
|
||||||
@if (Model.ShowRequestId)
|
|
||||||
{
|
|
||||||
<p>
|
|
||||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h3>Development Mode</h3>
|
|
||||||
<p>
|
|
||||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
|
||||||
It can result in displaying sensitive information from exceptions to end users.
|
|
||||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
|
||||||
and restarting the app.
|
|
||||||
</p>
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>@ViewData["Title"] - Web</title>
|
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css">
|
|
||||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
|
||||||
<link rel="stylesheet" href="~/Web.styles.css" asp-append-version="true" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Web</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
|
||||||
aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
|
||||||
<ul class="navbar-nav flex-grow-1">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Events">EventDefinitions</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="StudentEvents">Student EventDefinition Picks</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="StudentEventHandout">Student EventDefinition Handout</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Teams">Teams</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="TeamGrid">Team Grid</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Students">Students</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Schedule">Schedule</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Regionals">Regionals</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="State">State</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Nationals">Nationals</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<div class="container">
|
|
||||||
<main role="main" class="pb-3">
|
|
||||||
@RenderBody()
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="border-top footer text-muted">
|
|
||||||
<div class="container">
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
|
||||||
for details on configuring this project to bundle and minify static web assets. */
|
|
||||||
|
|
||||||
a.navbar-brand {
|
|
||||||
white-space: normal;
|
|
||||||
text-align: center;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #0077cc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #1b6ec2;
|
|
||||||
border-color: #1861ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #1b6ec2;
|
|
||||||
border-color: #1861ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
.border-top {
|
|
||||||
border-top: 1px solid #e5e5e5;
|
|
||||||
}
|
|
||||||
.border-bottom {
|
|
||||||
border-bottom: 1px solid #e5e5e5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-shadow {
|
|
||||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
|
||||||
}
|
|
||||||
|
|
||||||
button.accept-policy {
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
white-space: nowrap;
|
|
||||||
line-height: 60px;
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
|
||||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
@using Web
|
|
||||||
@using Web.Models
|
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
@{
|
|
||||||
Layout = "_Layout";
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="bootstrap" Version="5.3.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Core\Core.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<UpToDateCheckInput Remove="Views\Home\Teams.cshtml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<_ContentIncludedByDefault Remove="Views\Home\TeamGrid.cshtml" />
|
|
||||||
<_ContentIncludedByDefault Remove="Views\Home\Teams.cshtml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<UpToDateCheckInput Remove="Views\Home\TeamGrid.cshtml" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
html {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
html {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
body .container {
|
|
||||||
max-width:1200px;
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
font-size: 11px;
|
|
||||||
margin: 30pt;
|
|
||||||
color: #000;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
body header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.nobrk {
|
|
||||||
break-inside: avoid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
position: relative;
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin-bottom: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.bluewhite, table.schedule {
|
|
||||||
background-color: #4285F4;
|
|
||||||
color: white !IMPORTANT;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first-pick { background-color: #dd7e6b; }
|
|
||||||
.second-pick { background-color: #ea9999; }
|
|
||||||
.third-pick { background-color: #f9cb9c; }
|
|
||||||
.fourth-pick { background-color: #ffe599; }
|
|
||||||
.fifth-pick { background-color: #fff2cc; }
|
|
||||||
.sixth-pick { background-color: #fffaea; }
|
|
||||||
.seventh-pick { background-color: #fffff0; }
|
|
||||||
.non-pick { background-color: #f0f2f4; }
|
|
||||||
.event-desc { padding-left: 40px; font-size:small; }
|
|
||||||
.activity { font:xx-small; color:grey;}
|
|
||||||
.regional { color: #f1c232; }
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.3 KiB |
@@ -1,4 +0,0 @@
|
|||||||
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
|
||||||
// for details on configuring this project to bundle and minify static web assets.
|
|
||||||
|
|
||||||
// Write your JavaScript code.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2011-2021 Twitter, Inc.
|
|
||||||
Copyright (c) 2011-2021 The Bootstrap Authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,427 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
|
||||||
*/
|
|
||||||
*,
|
|
||||||
*::before,
|
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
:root {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: var(--bs-body-font-family);
|
|
||||||
font-size: var(--bs-body-font-size);
|
|
||||||
font-weight: var(--bs-body-font-weight);
|
|
||||||
line-height: var(--bs-body-line-height);
|
|
||||||
color: var(--bs-body-color);
|
|
||||||
text-align: var(--bs-body-text-align);
|
|
||||||
background-color: var(--bs-body-bg);
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
margin: 1rem 0;
|
|
||||||
color: inherit;
|
|
||||||
background-color: currentColor;
|
|
||||||
border: 0;
|
|
||||||
opacity: 0.25;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr:not([size]) {
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6, h5, h4, h3, h2, h1 {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: calc(1.375rem + 1.5vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: calc(1.325rem + 0.9vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h2 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: calc(1.3rem + 0.6vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h3 {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: calc(1.275rem + 0.3vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h4 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
abbr[title],
|
|
||||||
abbr[data-bs-original-title] {
|
|
||||||
-webkit-text-decoration: underline dotted;
|
|
||||||
text-decoration: underline dotted;
|
|
||||||
cursor: help;
|
|
||||||
-webkit-text-decoration-skip-ink: none;
|
|
||||||
text-decoration-skip-ink: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
address {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-style: normal;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul {
|
|
||||||
padding-left: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul,
|
|
||||||
dl {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol ol,
|
|
||||||
ul ul,
|
|
||||||
ol ul,
|
|
||||||
ul ol {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
b,
|
|
||||||
strong {
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-size: 0.875em;
|
|
||||||
}
|
|
||||||
|
|
||||||
mark {
|
|
||||||
padding: 0.2em;
|
|
||||||
background-color: #fcf8e3;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub,
|
|
||||||
sup {
|
|
||||||
position: relative;
|
|
||||||
font-size: 0.75em;
|
|
||||||
line-height: 0;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub {
|
|
||||||
bottom: -0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
top: -0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #0d6efd;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #0a58ca;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre,
|
|
||||||
code,
|
|
||||||
kbd,
|
|
||||||
samp {
|
|
||||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
||||||
font-size: 1em;
|
|
||||||
direction: ltr /* rtl:ignore */;
|
|
||||||
unicode-bidi: bidi-override;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
display: block;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 0.875em;
|
|
||||||
}
|
|
||||||
pre code {
|
|
||||||
font-size: inherit;
|
|
||||||
color: inherit;
|
|
||||||
word-break: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-size: 0.875em;
|
|
||||||
color: #d63384;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
a > code {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
kbd {
|
|
||||||
padding: 0.2rem 0.4rem;
|
|
||||||
font-size: 0.875em;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #212529;
|
|
||||||
border-radius: 0.2rem;
|
|
||||||
}
|
|
||||||
kbd kbd {
|
|
||||||
padding: 0;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
img,
|
|
||||||
svg {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
caption-side: bottom;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
caption {
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
color: #6c757d;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
text-align: inherit;
|
|
||||||
text-align: -webkit-match-parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead,
|
|
||||||
tbody,
|
|
||||||
tfoot,
|
|
||||||
tr,
|
|
||||||
td,
|
|
||||||
th {
|
|
||||||
border-color: inherit;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus:not(:focus-visible) {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
|
||||||
button,
|
|
||||||
select,
|
|
||||||
optgroup,
|
|
||||||
textarea {
|
|
||||||
margin: 0;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
select {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[role=button] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
word-wrap: normal;
|
|
||||||
}
|
|
||||||
select:disabled {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[list]::-webkit-calendar-picker-indicator {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
[type=button],
|
|
||||||
[type=reset],
|
|
||||||
[type=submit] {
|
|
||||||
-webkit-appearance: button;
|
|
||||||
}
|
|
||||||
button:not(:disabled),
|
|
||||||
[type=button]:not(:disabled),
|
|
||||||
[type=reset]:not(:disabled),
|
|
||||||
[type=submit]:not(:disabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-moz-focus-inner {
|
|
||||||
padding: 0;
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
min-width: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend {
|
|
||||||
float: left;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-size: calc(1.275rem + 0.3vw);
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
legend {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
legend + * {
|
|
||||||
clear: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-datetime-edit-fields-wrapper,
|
|
||||||
::-webkit-datetime-edit-text,
|
|
||||||
::-webkit-datetime-edit-minute,
|
|
||||||
::-webkit-datetime-edit-hour-field,
|
|
||||||
::-webkit-datetime-edit-day-field,
|
|
||||||
::-webkit-datetime-edit-month-field,
|
|
||||||
::-webkit-datetime-edit-year-field {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-inner-spin-button {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=search] {
|
|
||||||
outline-offset: -2px;
|
|
||||||
-webkit-appearance: textfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rtl:raw:
|
|
||||||
[type="tel"],
|
|
||||||
[type="url"],
|
|
||||||
[type="email"],
|
|
||||||
[type="number"] {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
::-webkit-search-decoration {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-color-swatch-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::file-selector-button {
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-file-upload-button {
|
|
||||||
font: inherit;
|
|
||||||
-webkit-appearance: button;
|
|
||||||
}
|
|
||||||
|
|
||||||
output {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
display: list-item;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
progress {
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
[hidden] {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
|
||||||
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
|
|
||||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,424 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
|
||||||
*/
|
|
||||||
*,
|
|
||||||
*::before,
|
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
:root {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: var(--bs-body-font-family);
|
|
||||||
font-size: var(--bs-body-font-size);
|
|
||||||
font-weight: var(--bs-body-font-weight);
|
|
||||||
line-height: var(--bs-body-line-height);
|
|
||||||
color: var(--bs-body-color);
|
|
||||||
text-align: var(--bs-body-text-align);
|
|
||||||
background-color: var(--bs-body-bg);
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
margin: 1rem 0;
|
|
||||||
color: inherit;
|
|
||||||
background-color: currentColor;
|
|
||||||
border: 0;
|
|
||||||
opacity: 0.25;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr:not([size]) {
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6, h5, h4, h3, h2, h1 {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: calc(1.375rem + 1.5vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: calc(1.325rem + 0.9vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h2 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: calc(1.3rem + 0.6vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h3 {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: calc(1.275rem + 0.3vw);
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
h4 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
abbr[title],
|
|
||||||
abbr[data-bs-original-title] {
|
|
||||||
-webkit-text-decoration: underline dotted;
|
|
||||||
text-decoration: underline dotted;
|
|
||||||
cursor: help;
|
|
||||||
-webkit-text-decoration-skip-ink: none;
|
|
||||||
text-decoration-skip-ink: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
address {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-style: normal;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul {
|
|
||||||
padding-right: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul,
|
|
||||||
dl {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol ol,
|
|
||||||
ul ul,
|
|
||||||
ol ul,
|
|
||||||
ul ol {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
b,
|
|
||||||
strong {
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-size: 0.875em;
|
|
||||||
}
|
|
||||||
|
|
||||||
mark {
|
|
||||||
padding: 0.2em;
|
|
||||||
background-color: #fcf8e3;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub,
|
|
||||||
sup {
|
|
||||||
position: relative;
|
|
||||||
font-size: 0.75em;
|
|
||||||
line-height: 0;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub {
|
|
||||||
bottom: -0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
top: -0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #0d6efd;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #0a58ca;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre,
|
|
||||||
code,
|
|
||||||
kbd,
|
|
||||||
samp {
|
|
||||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
||||||
font-size: 1em;
|
|
||||||
direction: ltr ;
|
|
||||||
unicode-bidi: bidi-override;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
display: block;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 0.875em;
|
|
||||||
}
|
|
||||||
pre code {
|
|
||||||
font-size: inherit;
|
|
||||||
color: inherit;
|
|
||||||
word-break: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-size: 0.875em;
|
|
||||||
color: #d63384;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
a > code {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
kbd {
|
|
||||||
padding: 0.2rem 0.4rem;
|
|
||||||
font-size: 0.875em;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #212529;
|
|
||||||
border-radius: 0.2rem;
|
|
||||||
}
|
|
||||||
kbd kbd {
|
|
||||||
padding: 0;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
img,
|
|
||||||
svg {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
caption-side: bottom;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
caption {
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
color: #6c757d;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
text-align: inherit;
|
|
||||||
text-align: -webkit-match-parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead,
|
|
||||||
tbody,
|
|
||||||
tfoot,
|
|
||||||
tr,
|
|
||||||
td,
|
|
||||||
th {
|
|
||||||
border-color: inherit;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus:not(:focus-visible) {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
|
||||||
button,
|
|
||||||
select,
|
|
||||||
optgroup,
|
|
||||||
textarea {
|
|
||||||
margin: 0;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
select {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[role=button] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
word-wrap: normal;
|
|
||||||
}
|
|
||||||
select:disabled {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[list]::-webkit-calendar-picker-indicator {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
[type=button],
|
|
||||||
[type=reset],
|
|
||||||
[type=submit] {
|
|
||||||
-webkit-appearance: button;
|
|
||||||
}
|
|
||||||
button:not(:disabled),
|
|
||||||
[type=button]:not(:disabled),
|
|
||||||
[type=reset]:not(:disabled),
|
|
||||||
[type=submit]:not(:disabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-moz-focus-inner {
|
|
||||||
padding: 0;
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
min-width: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend {
|
|
||||||
float: right;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-size: calc(1.275rem + 0.3vw);
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
legend {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
legend + * {
|
|
||||||
clear: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-datetime-edit-fields-wrapper,
|
|
||||||
::-webkit-datetime-edit-text,
|
|
||||||
::-webkit-datetime-edit-minute,
|
|
||||||
::-webkit-datetime-edit-hour-field,
|
|
||||||
::-webkit-datetime-edit-day-field,
|
|
||||||
::-webkit-datetime-edit-month-field,
|
|
||||||
::-webkit-datetime-edit-year-field {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-inner-spin-button {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=search] {
|
|
||||||
outline-offset: -2px;
|
|
||||||
-webkit-appearance: textfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
[type="tel"],
|
|
||||||
[type="url"],
|
|
||||||
[type="email"],
|
|
||||||
[type="number"] {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
::-webkit-search-decoration {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-color-swatch-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::file-selector-button {
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-file-upload-button {
|
|
||||||
font: inherit;
|
|
||||||
-webkit-appearance: button;
|
|
||||||
}
|
|
||||||
|
|
||||||
output {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
display: list-item;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
progress {
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
[hidden] {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
|
||||||
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
|
|
||||||
/*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
-11221
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-5026
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user