diff --git a/UlstuGosApi.sln b/UlstuGosApi.sln new file mode 100644 index 0000000..11be519 --- /dev/null +++ b/UlstuGosApi.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34728.123 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UlstuGosApi", "UlstuGosApi\UlstuGosApi.csproj", "{664D9DC0-1E75-406D-A3BA-DDFD958BD729}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {664D9DC0-1E75-406D-A3BA-DDFD958BD729}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {664D9DC0-1E75-406D-A3BA-DDFD958BD729}.Debug|Any CPU.Build.0 = Debug|Any CPU + {664D9DC0-1E75-406D-A3BA-DDFD958BD729}.Release|Any CPU.ActiveCfg = Release|Any CPU + {664D9DC0-1E75-406D-A3BA-DDFD958BD729}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1D74895A-13EC-44F0-9764-2FDECE58D9E9} + EndGlobalSection +EndGlobal diff --git a/UlstuGosApi/.config/dotnet-tools.json b/UlstuGosApi/.config/dotnet-tools.json new file mode 100644 index 0000000..93ec8eb --- /dev/null +++ b/UlstuGosApi/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "8.0.5", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/UlstuGosApi/Program.cs b/UlstuGosApi/Program.cs new file mode 100644 index 0000000..4c58b31 --- /dev/null +++ b/UlstuGosApi/Program.cs @@ -0,0 +1,40 @@ +using System.Reflection; +using UlstuGosApi; + +var builder = WebApplication.CreateBuilder(args); + +builder.Host.UseSystemd(); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(options => +{ + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); +}); +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy( + policy => + { + policy + .AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod(); + }); +}); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(); + +app.UseCors(); + +app.MapGet("/", () => Results.LocalRedirect("/swagger")) + .ExcludeFromDescription(); + +app.MapGroup("/timetable") + .AddTimeTable() + .WithTags("TimeTable"); + +app.Run(); diff --git a/UlstuGosApi/Properties/launchSettings.json b/UlstuGosApi/Properties/launchSettings.json new file mode 100644 index 0000000..5ef4aa7 --- /dev/null +++ b/UlstuGosApi/Properties/launchSettings.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:26590", + "sslPort": 44388 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5189", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7254;http://localhost:5189", + "executablePath": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/UlstuGosApi/TimeTableRouteGroup.cs b/UlstuGosApi/TimeTableRouteGroup.cs new file mode 100644 index 0000000..1ce7ffc --- /dev/null +++ b/UlstuGosApi/TimeTableRouteGroup.cs @@ -0,0 +1,216 @@ +using System.Net; + +namespace UlstuGosApi; + +public static class TimeTableRouteGroup +{ + public static RouteGroupBuilder AddTimeTable(this RouteGroupBuilder group) + { + group.MapGet("/groups", () => TypedResults.Ok(groups)) + .WithSummary("Список групп"); + group.MapGet("/rooms", () => TypedResults.Ok(rooms)) + .WithSummary("Список аудиторий"); + group.MapGet("/teachers", () => TypedResults.Ok(teachers)) + .WithSummary("Список преподавателей"); + + group.MapGet("/by-group/{groupName}", (string groupName) => + { + var group = groups.FirstOrDefault(m => m.Name == groupName); + if (group == default) + { + return Results.NotFound(); + } + return Results.Ok(GenerateByGroup(group)); + }) + .Produces() + .WithSummary("Расписание по учебной группе"); + + group.MapGet("/by-room/{roomName}", (string roomName) => + { + var room = rooms.FirstOrDefault(m => m.Name == WebUtility.UrlDecode(roomName)); + if (room == default) + { + return Results.NotFound(); + } + return Results.Ok(GenerateByRoom(room)); + + }) + .Produces() + .WithSummary("Расписание по аудитории"); + + group.MapGet("/by-teacher/{teacherFullName}", (string teacherFullName) => + { + var teacher = teachers.FirstOrDefault(m => m.FullName == teacherFullName); + if (teacher == default) + { + return Results.NotFound(); + } + return Results.Ok(GenerateByTeacher(teacher)); + }) + .Produces() + .WithSummary("Расписание по преподавателю"); + + return group; + } + + private static TimetableGroup[] groups = [ + new TimetableGroup("ПИбд-41"), + new TimetableGroup("ПИбд-42"), + new TimetableGroup("ПИбд-43"), + ]; + + private static TimetableRoom[] rooms = [ + new TimetableRoom("3/420"), + new TimetableRoom("3/424"), + new TimetableRoom("3/429"), + new TimetableRoom("3/431"), + ]; + + private static TimetableTeacher[] teachers = [ + new TimetableTeacher("Иванов И.И."), + new TimetableTeacher("Петров П.П."), + new TimetableTeacher("Сидорова С.С."), + ]; + + private static TimetableSubject[] subjects = [ + new TimetableSubject(teachers[0], "Основы программирования", "лек"), + new TimetableSubject(teachers[0], "Базы данных", "лек"), + new TimetableSubject(teachers[0], "Схемотехника", "лек"), + new TimetableSubject(teachers[1], "Основы программирования", "л/р"), + new TimetableSubject(teachers[1], "Базы данных", "л/р"), + new TimetableSubject(teachers[2], "Философия", "лек"), + new TimetableSubject(teachers[2], "Философия", "сем"), + ]; + + private const int LessonsMaxCount = 2 * 6 * 8; + private static List lessons = new(); + private static object lessonsLockObj = new object(); + public static IReadOnlyList Lessons + { + get + { + lock (lessonsLockObj) + { + if (lessons.Count == 0) + { + GenerateLessons(); + } + } + return lessons; + } + } + private static void GenerateLessons() + { + foreach (var idx in Enumerable.Range(0, LessonsMaxCount)) + { + lessons.Add( + new TimetableLesson( + groups[Random.Shared.Next(groups.Length)], rooms[Random.Shared.Next(rooms.Length)], subjects[Random.Shared.Next(subjects.Length)] + )); + } + } + + private static TimetableWeek[] GenerateByGroup(TimetableGroup group) => GenerateWithLessons(Lessons.Where(i => i.Group == group)); + private static TimetableWeek[] GenerateByRoom(TimetableRoom room) => GenerateWithLessons(Lessons.Where(i => i.Room == room)); + private static TimetableWeek[] GenerateByTeacher(TimetableTeacher teacher) => GenerateWithLessons(Lessons.Where(i => i.Subject.Teacher == teacher)); + private static TimetableWeek[] GenerateWithLessons(IEnumerable filteredLessons) + { + var weeks = new List(); + var lessonsEnumerator = filteredLessons.GetEnumerator(); + weeks.Add(new TimetableWeek(GenerateDays(lessonsEnumerator), true)); + weeks.Add(new TimetableWeek(GenerateDays(lessonsEnumerator), false)); + return weeks.ToArray(); + } + private static TimetableDay[] GenerateDays(IEnumerator filteredLessons) + { + var result = new List(); + foreach (var _ in Enumerable.Range(1, 6)) + { + result.Add(GenerateDay(filteredLessons)); + } + return result.ToArray(); + } + private static TimetableDay GenerateDay(IEnumerator filteredLessons) + { + var result = new List(); + foreach (var _ in Enumerable.Range(1, 8)) + { + if (Random.Shared.NextDouble() < 0.25) + { + filteredLessons.MoveNext(); + result.Add(filteredLessons.Current); + } + else + { + result.Add(null); + } + } + return new TimetableDay(result.ToArray()); + } +} + +/// +/// Неделя расписания. +/// +/// Массив дней недели: ПН, ВТ, СР и до субботы. +/// Флаг нечётности. +public sealed record TimetableWeek( + TimetableDay[] Days, + bool IsOdd + ); + +/// +/// День расписания. +/// +/// Массив пар от 1й до 8й. Если пары в "слоте" нет, возвращается null. +public sealed record TimetableDay( + TimetableLesson?[] Lessons + ); + +/// +/// "Слот" расписания, непосредственно пара. +/// +/// Группа. +/// Аудитория. +/// Дисциплина. +public sealed record TimetableLesson( + TimetableGroup Group, + TimetableRoom Room, + TimetableSubject Subject + ); + +/// +/// Группа. +/// +/// Название. +public sealed record TimetableGroup( + string Name + ); + +/// +/// Аудитория. +/// +/// Номер. +public sealed record TimetableRoom( + string Name + ); + +/// +/// Дисциплина. +/// +/// Преподаватель. +/// Имя. +/// Тип занятия. +public sealed record TimetableSubject( + TimetableTeacher Teacher, + string Name, + string Type + ); + +/// +/// Преподаватель +/// +/// ФИО. +public sealed record TimetableTeacher( + string FullName + ); diff --git a/UlstuGosApi/UlstuGosApi.csproj b/UlstuGosApi/UlstuGosApi.csproj new file mode 100644 index 0000000..58be965 --- /dev/null +++ b/UlstuGosApi/UlstuGosApi.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + true + $(NoWarn);1591 + + + diff --git a/UlstuGosApi/appsettings.Development.json b/UlstuGosApi/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/UlstuGosApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/UlstuGosApi/appsettings.json b/UlstuGosApi/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/UlstuGosApi/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +}