ASP.NET API #1

Open
Zagrebin wants to merge 7 commits from develop into main
83 changed files with 3659 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.idea/
.vs/
.vscode/
[Bb]in/
[Oo]bj/

View File

@ -0,0 +1,38 @@
using domain.UseCase;
using domain.Request;
using Microsoft.AspNetCore.Mvc;
namespace Presence.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
private readonly IAdminUseCase _adminUseCase;
public AdminController(IAdminUseCase adminUseCase)
{
_adminUseCase = adminUseCase;
}
[HttpPost("/group/{groupId}/students")]
public IActionResult AddStudentsToGroup(int groupId, [FromBody] AddStudentsToGroupRequest request)
{
if (request?.Students == null)
return BadRequest();
_adminUseCase.AddStudentsToGroup(groupId, request);
return NoContent();
}
[HttpPost("/group/{groupId}/subjects")]
public IActionResult AddSubjectsToGroup(int groupId, [FromBody] AddSubjectsToGroupRequest request)
{
if (request?.Subjects == null)
return BadRequest();
_adminUseCase.AddSubjectsToGroup(groupId, request);
return NoContent();
}
}
}

View File

@ -0,0 +1,98 @@
using domain.Request;
using domain.UseCase;
using Microsoft.AspNetCore.Mvc;
using Presence.API.Response;
namespace Presence.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class GroupController : ControllerBase
{
private readonly IGroupUseCase _groupService;
public GroupController(IGroupUseCase groupService)
{
_groupService = groupService;
}
[HttpGet("/group")]
public ActionResult<GroupResponse> GetAllGroups()
{
var result = _groupService
.GetGroupsWithStudents()
.Select(group => new GroupResponse
{
Id = group.Id,
Name = group.Name,
Users = group.Users?.Select(
user => new UserResponse
{
Id = user.Id,
LastName = user.LastName,
FirstName = user.FirstName,
Patronymic = user.Patronymic
}).ToList()
}).ToList();
return Ok(result);
}
[HttpDelete("/group/{id}")]
public IActionResult DeleteGroup(int id)
{
RemoveGroupRequest removeGroupRequest = new() { GroupId = id };
bool isDeleted = _groupService.RemoveGroup(removeGroupRequest);
if (!isDeleted) return NotFound();
else return NoContent();
}
[HttpPost("/group")]
public ActionResult<GroupResponse> PostGroup(AddGroupRequest addGroupRequest)
{
if (addGroupRequest is null)
return BadRequest(new ArgumentNullException());
var groupWithStudentsRequest = new AddGroupWithStudentRequest
{
AddGroupRequest = new AddGroupRequest { Name = addGroupRequest.Name },
AddStudentRequests = addGroupRequest.Students
};
_groupService.AddGroupWithStudents(groupWithStudentsRequest);
var createdGroup = _groupService.GetGroupsWithStudents()
.FirstOrDefault(g => g.Name == addGroupRequest.Name);
return CreatedAtAction(nameof(GetGroupById), new { id = createdGroup.Id }, createdGroup);
}
[HttpGet("/group/{id:int}")]
public ActionResult<GroupResponse> GetGroupById(int id)
{
var group = _groupService.GetGroupsWithStudents().FirstOrDefault(g => g.Id == id);
if (group == null)
return NotFound();
var response = new GroupResponse
{
Id = group.Id,
Name = group.Name,
Users = group.Users?.Select(
user => new UserResponse
{
Id = user.Id,
LastName = user.LastName,
FirstName = user.FirstName,
Patronymic = user.Patronymic
}).ToList()
};
return Ok(response);
}
[HttpGet("/group/{id}/subjects")]
public ActionResult<GroupSubjectResponse> GetGroupSubject(int id)
{
return Ok(_groupService.GetGroupSubject(id));
}
}
}

View File

@ -0,0 +1,101 @@
using domain.UseCase;
using Microsoft.AspNetCore.Mvc;
using Presence.API.Response;
using domain.Request;
using System;
using System.Linq;
namespace Presence.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class PresenceController : ControllerBase
{
private readonly IPresenceUseCase _presenceUseCase;
public PresenceController(IPresenceUseCase presenceUseCase)
{
_presenceUseCase = presenceUseCase;
}
[HttpGet("/presence/{groupId}")]
public ActionResult<IEnumerable<PresenceResponse>> GetPresence(
int groupId,
[FromQuery] int? subject,
[FromQuery] DateOnly? date,
[FromQuery] int? student)
{
var presences = _presenceUseCase.GetPresence(groupId, subject, date, student)
.Select(p => new PresenceResponse
{
Id = p.Id,
StudentId = p.StudentId,
StudentName = $"{p.Student.LastName} {p.Student.FirstName} {p.Student.Patronymic}",
SubjectId = p.SubjectId,
SubjectName = p.Subject.Name,
TrafficId = p.TrafficId,
TrafficName = p.Traffic.Name,
LessonNumber = p.LessonNumber,
Date = p.Date
})
.ToList();
return Ok(presences);
}
[HttpPost("/presence")]
public IActionResult AddPresenceRecords([FromBody] AddPresenceRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
_presenceUseCase.AddPresenceRecords(request);
return Ok();
}
catch (ArgumentException ex)
{
return BadRequest(ex.Message);
}
}
[HttpPut("/presence")]
public IActionResult UpdatePresenceRecords([FromBody] UpdatePresenceRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
_presenceUseCase.UpdatePresenceRecords(request);
return Ok();
}
catch (ArgumentException ex)
{
return BadRequest(ex.Message);
}
}
[HttpDelete("/presence")]
public IActionResult DeleteAllPresence()
{
_presenceUseCase.DeleteAllPresence();
return NoContent();
}
[HttpDelete("/presence/group")]
public IActionResult DeleteGroupPresence([FromQuery] int? group)
{
if (!group.HasValue)
{
_presenceUseCase.DeleteAllPresence();
}
else
{
_presenceUseCase.DeleteGroupPresence(group.Value);
}
return NoContent();
}
}
}

View File

@ -0,0 +1,32 @@
using data;
using data.Repository;
using domain.Service;
using domain.UseCase;
using Presence.API.Controllers;
namespace Presence.API.Extensions
{
public static class ServiceCollectionExtension
{
public static void AddCommonServices(this IServiceCollection services)
{
services
.AddDbContext<DatabaseContext>()
.AddScoped<IGroupRepository, SQLGroupRepository>()
.AddScoped<IGroupUseCase, GroupService>()
.AddScoped<GroupController>();
services
.AddDbContext<DatabaseContext>()
.AddScoped<IAdminRepository, SQLAdminRepository>()
.AddScoped<IAdminUseCase, AdminService>()
.AddScoped<AdminController>();
services
.AddDbContext<DatabaseContext>()
.AddScoped<IPresenceRepository, SQLPresenceRepository>()
.AddScoped<IPresenceUseCase, PresenceService>()
.AddScoped<PresenceController>();
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\data\data.csproj" />
<ProjectReference Include="..\domain\domain.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,6 @@
@Presence.API_HostAddress = http://localhost:5275
GET {{Presence.API_HostAddress}}/weatherforecast/
Accept: application/json
###

38
Presence.API/Program.cs Normal file
View File

@ -0,0 +1,38 @@
using Presence.API.Extensions;
namespace Presence.API
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddCommonServices();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
}

View File

@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56416",
"sslPort": 44345
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5275",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7226;http://localhost:5275",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,10 @@
namespace Presence.API.Response
{
public class GroupResponse
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<UserResponse>? Users { get; set; } = null;
}
}

View File

@ -0,0 +1,9 @@
namespace Presence.API.Response
{
public class GroupSubjectResponse
{
public string Name { get; set; }
public IEnumerable<SubjectResponse> Subjects { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace Presence.API.Response
{
public class PresenceResponse
{
public int Id { get; set; }
public int StudentId { get; set; }
public string StudentName { get; set; }
public int SubjectId { get; set; }
public string SubjectName { get; set; }
public int TrafficId { get; set; }
public string TrafficName { get; set; }
public int LessonNumber { get; set; }
public DateOnly Date { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace Presence.API.Response
{
public class SubjectResponse
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace Presence.API.Response
{
public class UserResponse
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Patronymic { get; set; }
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,15 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Presence.Desktop.App"
xmlns:local="using:Presence.Desktop"
RequestedThemeVariant="Light">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

View File

@ -0,0 +1,36 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection;
using Presence.Desktop.DI;
using Presence.Desktop.ViewModels;
using Presence.Desktop.Views;
namespace Presence.Desktop
{
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddComonoServices();
var services = serviceCollection.BuildServiceProvider();
MainWindowViewModel mainViewModel = services.GetRequiredService<MainWindowViewModel>();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = mainViewModel,
};
}
base.OnFrameworkInitializationCompleted();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@ -0,0 +1,21 @@
using data;
using data.Repository;
using domain.UseCase;
using domain.Service;
using Microsoft.Extensions.DependencyInjection;
using Presence.Desktop.ViewModels;
namespace Presence.Desktop.DI
{
public static class ServiceCollectionExtension
{
public static void AddComonoServices(this IServiceCollection collection)
{
collection
.AddDbContext<DatabaseContext>()
.AddSingleton<IGroupRepository, SQLGroupRepository>()
.AddTransient<IGroupUseCase, GroupService>()
.AddTransient<MainWindowViewModel>();
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using domain.Request;
using Presence.Desktop.Models;
namespace Presence.Desktop.Helpers
{
public static class CsvHelper
{
public static IEnumerable<AddStudentRequest> ReadCsvStudents(string path)
{
List<AddStudentRequest> students = new List<AddStudentRequest>();
var csvLines = File.ReadAllLines(path, Encoding.GetEncoding("utf-8"));
using (var reader = new StreamReader(path))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var values = line.Split(';');
if (values.Length != 3)
continue;
students.Add(new AddStudentRequest
{
LastName = values[0],
FirstName = values[1],
Patronymic = values[2]
});
}
}
return students;
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Presence.Desktop.Models
{
public class GroupPresenter
{
public int Id { get; set; }
public string Name { get; set; }
public List<StudentPresenter>? students = null;
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Presence.Desktop.Models
{
public class StudentPresenter
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Patronymic { get; set; }
public GroupPresenter Group { get; set; }
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.1.0" />
<PackageReference Include="Avalonia.Desktop" Version="11.1.0" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.0" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.1.0" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.0" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\data\data.csproj" />
<ProjectReference Include="..\domain\domain.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,24 @@
using Avalonia;
using Avalonia.ReactiveUI;
using System;
namespace Presence.Desktop
{
internal sealed class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
.UseReactiveUI();
}
}

View File

@ -0,0 +1,34 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Presence.Desktop.ViewModels;
using System;
namespace Presence.Desktop
{
public class ViewLocator : IDataTemplate
{
public Control? Build(object? data)
{
if (data is null)
return null;
var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null)
{
var control = (Control)Activator.CreateInstance(type)!;
control.DataContext = data;
return control;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
}
}

View File

@ -0,0 +1,6 @@
namespace Presence.Desktop.ViewModels;
public class EditWindowViewModel : ViewModelBase
{
}

View File

@ -0,0 +1,259 @@
using domain.UseCase;
using Presence.Desktop.Models;
using Presence.Desktop.Helpers;
using System;
using System.Reactive.Linq;
using System.Reactive;
using System.Linq;
using ReactiveUI;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Windows.Input;
using System.Threading.Tasks;
using Avalonia.Controls.Selection;
using domain.Request;
using System.Text;
namespace Presence.Desktop.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
private readonly IGroupUseCase _groupService;
private List<GroupPresenter> groupPresenters = new List<GroupPresenter>();
private ObservableCollection<GroupPresenter> _groups;
public ObservableCollection<GroupPresenter> Groups
{
get => _groups;
set => this.RaiseAndSetIfChanged(ref _groups, value);
}
private GroupPresenter? _selectedGroupItem;
public GroupPresenter? SelectedGroupItem
{
get => _selectedGroupItem;
set => this.RaiseAndSetIfChanged(ref _selectedGroupItem, value);
}
private ObservableCollection<StudentPresenter> _students;
public ObservableCollection<StudentPresenter> Students
{
get => _students;
set => this.RaiseAndSetIfChanged(ref _students, value);
}
public Interaction<Unit, string?> ShowOpenFileDialog;
private string? _selectedFile;
public string? SelectedFile
{
get => _selectedFile;
set => this.RaiseAndSetIfChanged(ref _selectedFile, value);
}
private bool _MultipleSelected = false;
public bool MultipleSelected { get => _MultipleSelected; set => this.RaiseAndSetIfChanged(ref _MultipleSelected, value); }
public SelectionModel<StudentPresenter> Selection { get; }
private string _selectedSort = "Релевантность";
public string SelectedSort
{
get => _selectedSort;
set => this.RaiseAndSetIfChanged(ref _selectedSort, value);
}
public List<string> Sorting { get; set; } =
[
"Релевантность",
"А-Я",
"Я-А"
];
public ICommand OpenFileDialog { get; }
public ICommand EditCommand { get; }
public ICommand DeleteCommand { get; }
public ICommand DeleteSelectedCommand { get; }
public ICommand DeleteAllCommand { get; }
public MainWindowViewModel()
{
_groupService = null;
_groups = new();
_students = new();
OpenFileDialog = ReactiveCommand.CreateFromTask(SelectFile);
}
public MainWindowViewModel(IGroupUseCase gService)
{
_groupService = gService;
ShowOpenFileDialog = new Interaction<Unit, string?>();
OpenFileDialog = ReactiveCommand.CreateFromTask(SelectFile);
_students = new();
this.WhenAnyValue(vm => vm.SelectedGroupItem,
x => x.SelectedSort)
.Subscribe(_ =>
{
RefreshGroups();
SetStudents(SelectedSort);
});
Selection = new SelectionModel<StudentPresenter>();
Selection.SingleSelect = false;
Selection.SelectionChanged += (sender, args) =>
{
MultipleSelected = Selection.SelectedItems.Count > 1;
};
DeleteCommand = ReactiveCommand.Create(DeleteStudent);
DeleteSelectedCommand = ReactiveCommand.Create(DeleteStudent);
DeleteAllCommand = ReactiveCommand.Create(DeleteAllStudents);
}
private void RefreshGroups()
{
groupPresenters.Clear();
foreach (var item in _groupService.GetGroupsWithStudents())
{
GroupPresenter groupPresenter = new GroupPresenter
{
Id = item.Id,
Name = item.Name,
students = item.Users?.Select(u => new StudentPresenter
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
Patronymic = u.Patronymic,
Group = new GroupPresenter
{
Id = item.Id,
Name = item.Name
}
}).ToList()
};
groupPresenters.Add(groupPresenter);
}
_groups = new(groupPresenters);
}
private void SetStudents(string selectedSort)
{
if (SelectedGroupItem == null) return;
if (SelectedGroupItem.students == null) return;
Students.Clear();
List<StudentPresenter> students = new(_groupService.GetGroupsWithStudents()
.Single(g => g.Id == SelectedGroupItem.Id)
.Users
.Select(u => new StudentPresenter
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
Patronymic = u.Patronymic,
Group = new GroupPresenter()
{
Id = SelectedGroupItem.Id,
Name = SelectedGroupItem.Name
}
})
);
students = selectedSort switch
{
"Релевантность" => students.OrderBy(s => s.Id).ToList(),
"А-Я" => students.OrderBy(s => s.LastName).ToList(),
"Я-А" => students.OrderByDescending(s => s.LastName).ToList(),
_ => students
};
foreach (var student in students)
Students.Add(student);
}
private async Task SelectFile()
{
try
{
SelectedFile = await ShowOpenFileDialog.Handle(Unit.Default);
}
catch (Exception)
{
return;
}
if (SelectedGroupItem == null) return;
try
{
var studentsToAdd = CsvHelper.ReadCsvStudents(SelectedFile);
if (studentsToAdd.Any())
{
_groupService.AddStudentsToGroup(SelectedGroupItem.Id, studentsToAdd);
foreach (var studentRequest in studentsToAdd)
{
var studentPresenter = new StudentPresenter
{
LastName = studentRequest.LastName,
FirstName = studentRequest.FirstName,
Patronymic = studentRequest.Patronymic,
Group = SelectedGroupItem
};
SelectedGroupItem.students.Add(studentPresenter);
Students.Add(studentPresenter);
}
RefreshGroups();
SetStudents(SelectedSort);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private void DeleteStudent()
{
if (Selection.SelectedItems == null || Selection.SelectedItems.Count == 0) return;
var studentIds = Selection.SelectedItems.Select(s => s.Id).ToList();
_groupService.RemoveStudentsFromGroupByIds(SelectedGroupItem.Id, studentIds);
foreach (var student in Selection.SelectedItems.ToList())
{
SelectedGroupItem.students.Remove(student);
Students.Remove(student);
}
SetStudents(SelectedSort);
RefreshGroups();
}
private void DeleteAllStudents()
{
if (Students.Count == 0 && SelectedGroupItem == null) return;
_groupService.RemoveStudentsFromGroup(SelectedGroupItem.Id);
SelectedGroupItem.students.Clear();
Students.Clear();
RefreshGroups();
SetStudents(SelectedSort);
}
}
}

View File

@ -0,0 +1,8 @@
using ReactiveUI;
namespace Presence.Desktop.ViewModels
{
public class ViewModelBase : ReactiveObject
{
}
}

View File

@ -0,0 +1,17 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:Presence.Desktop.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Presence.Desktop.EditWindow"
x:DataType="vm:EditWindowViewModel"
Title="Edit">
<Design.DataContext>
<vm:EditWindowViewModel/>
</Design.DataContext>
</Window>

View File

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Presence.Desktop;
public partial class EditWindow : Window
{
public EditWindow()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,70 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Presence.Desktop.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Presence.Desktop.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="Presence.Desktop">
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<DockPanel>
<StackPanel
Spacing="10"
HorizontalAlignment="Center"
DockPanel.Dock="Top"
Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Groups}"
SelectedValue="{Binding SelectedGroupItem}"
DisplayMemberBinding="{Binding Name}"
SelectedValueBinding="{Binding}"
/>
<Button Width="140" HorizontalContentAlignment="Center" Content="Внести студентов" Command="{Binding OpenFileDialog}"/>
<ComboBox ItemsSource="{Binding Sorting}" SelectedItem="{Binding SelectedSort}"/>
<Button HorizontalAlignment="Right" Content="Удалить всех студентов" Command="{Binding DeleteAllCommand}"/>
</StackPanel>
<Border>
<ListBox Name="StudentListBox" SelectionMode="Multiple" Selection="{Binding Selection}" ItemsSource="{Binding Students}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem IsVisible="{Binding !#StudentListBox.((vm:MainWindowViewModel)DataContext).MultipleSelected}"
Command="{Binding #StudentListBox.((vm:MainWindowViewModel)DataContext).EditCommand}"
Header="Изменить"/>
<MenuItem IsVisible="{Binding !#StudentListBox.((vm:MainWindowViewModel)DataContext).MultipleSelected}"
Command="{Binding #StudentListBox.((vm:MainWindowViewModel)DataContext).DeleteCommand}"
Header="Удалить"/>
<MenuItem IsVisible="{Binding #StudentListBox.((vm:MainWindowViewModel)DataContext).MultipleSelected}"
Command="{Binding #StudentListBox.((vm:MainWindowViewModel)DataContext).DeleteSelectedCommand}"
Header="Удалить выделенные"/>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1} {2}">
<Binding Path="LastName"/>
<Binding Path="FirstName"/>
<Binding Path="Patronymic"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</DockPanel>
</Window>

View File

@ -0,0 +1,51 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Platform.Storage;
using Avalonia.ReactiveUI;
using Presence.Desktop.ViewModels;
using ReactiveUI;
using System;
using System.Linq;
using System.Reactive;
using System.Threading.Tasks;
namespace Presence.Desktop.Views
{
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
{
public MainWindow()
{
if (Design.IsDesignMode)
ViewModel = new MainWindowViewModel();
InitializeComponent();
this.WhenActivated(action =>
{
action(ViewModel.ShowOpenFileDialog.RegisterHandler(ShowOpenFileDialogAsync));
});
}
public static FilePickerFileType FileCsv { get; } = new("Comma-Separated Values")
{
Patterns = ["*.csv"],
AppleUniformTypeIdentifiers = ["public.comma-separated-values-text"],
MimeTypes = ["application/vnd.ms-excel", "text/csv"]
};
private async Task ShowOpenFileDialogAsync(InteractionContext<Unit, string?> interaction)
{
var topLevel = GetTopLevel(this);
FilePickerOpenOptions fpOptions = new FilePickerOpenOptions
{
AllowMultiple = false,
Title = "students csv",
FileTypeFilter = [FileCsv]
};
var storageFile = await topLevel.StorageProvider.OpenFilePickerAsync(fpOptions);
interaction.SetOutput(storageFile.First().Path.LocalPath);
}
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="Presence.Desktop.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

36
console_ui/Program.cs Normal file
View File

@ -0,0 +1,36 @@
using console_ui.UI;
using data;
using data.Repository;
using domain.Service;
using domain.UseCase;
using Microsoft.Extensions.DependencyInjection;
namespace console_ui
{
class Program
{
static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
services
.AddDbContext<DatabaseContext>()
.AddSingleton<IGroupRepository, SQLGroupRepository>()
.AddSingleton<IGroupUseCase, GroupService>()
.AddSingleton<GroupUI>();
var serviceProvider = services.BuildServiceProvider();
var groupUI = serviceProvider.GetService<GroupUI>();
var repo = serviceProvider.GetService<IGroupRepository>();
printAllGroups(repo!);
}
static void printAllGroups(IGroupRepository groupRepository)
{
foreach (var item in groupRepository.GetAllGroups())
{
Console.WriteLine($"{item.Id} \t {item.Name}");
}
}
}
}

54
console_ui/UI/GroupUI.cs Normal file
View File

@ -0,0 +1,54 @@
using domain.Request;
using domain.Service;
using domain.UseCase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace console_ui.UI
{
public class GroupUI
{
private readonly IGroupUseCase _groupService;
public GroupUI(IGroupUseCase groupService)
{
_groupService = groupService;
}
public void AddGroup()
{
Console.Write("Введите имя группы: ");
_groupService.AddGroup(new AddGroupRequest { Name = Console.ReadLine() });
}
public void AddGroupWithStudents()
{
Console.Write("Введите имя группы: ");
AddGroupRequest addGroupRequest = new AddGroupRequest { Name = Console.ReadLine() };
List<AddStudentRequest> addStudentRequests = new List<AddStudentRequest>()
{
new AddStudentRequest {FirstName = "Имя", LastName = "Фамилия", Patronymic = "Отчество"},
new AddStudentRequest {FirstName = "Imya", LastName = "Familiya", Patronymic = "Otchestvo"},
new AddStudentRequest {FirstName = "FirstName", LastName = "LastName", Patronymic = "Patronymic"},
new AddStudentRequest {FirstName = "1", LastName = "2", Patronymic = "3"},
};
AddGroupWithStudentRequest addGroupWithStudentRequest = new()
{
AddGroupRequest = addGroupRequest,
AddStudentRequests = addStudentRequests
};
_groupService.AddGroupWithStudents(addGroupWithStudentRequest);
}
public void RemoveGroup()
{
Console.Write("Введите индетефикатор группы: ");
RemoveGroupRequest removeGroupRequest = new RemoveGroupRequest { GroupId = int.Parse(Console.ReadLine()) };
_groupService.RemoveGroup(removeGroupRequest);
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../data/data.csproj" />
<ProjectReference Include="..\domain\domain.csproj" />
</ItemGroup>
</Project>

32
data/DAO/Diary.cs Normal file
View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace data.DAO
{
public class Diary
{
[Key]
public int Id { get; set; }
public string? DiaryText { get; set; }
public int TrafficId { get; set; }
public int StudentGroupSubjectId { get; set; }
public int StudentId { get; set; }
public virtual Traffic Traffic { get; set; }
public virtual StudentGroupSubject StudentGroupSubject { get; set; }
public virtual Student Student { get; set; }
public int NumberSubject { get; set; }
public DateOnly Date { get; set; }
}
}

20
data/DAO/Group.cs Normal file
View File

@ -0,0 +1,20 @@

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace data.DAO
{
public class Group
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; } = new List<Student>();
public virtual ICollection<StudentGroupSubject> StudentGroupsSubjects { get; set; } = new List<StudentGroupSubject>();
}
}

27
data/DAO/Student.cs Normal file
View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace data.DAO
{
public class Student
{
[Key]
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Patronymic { get; set; }
public int GroupId { get; set; }
public virtual ICollection<Diary> Diaries { get; set; } = new List<Diary>();
public virtual Group Group { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace data.DAO
{
public class StudentGroupSubject
{
[Key]
public int Id { get; set; }
public virtual Group Group { get; set; }
public int SemesterStart { get; set; }
public int SemesterEnd { get; set; }
public virtual Subject Subject { get; set; }
public virtual ICollection<Diary> Diaries { get; set; } = new List<Diary>();
}
}

19
data/DAO/Subject.cs Normal file
View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace data.DAO
{
// Предметы
public class Subject
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentGroupSubject> StudentGroupsSubjects { get; set; } = new List<StudentGroupSubject>();
}
}

18
data/DAO/Traffic.cs Normal file
View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace data.DAO
{
public class Traffic
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Diary> Diaries { get; set; } = new List<Diary>();
}
}

47
data/DatabaseContext.cs Normal file
View File

@ -0,0 +1,47 @@
using data.DAO;
using Microsoft.EntityFrameworkCore;
namespace data;
public class DatabaseContext : DbContext
{
public DatabaseContext() { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql("Host=45.67.56.214;Port=5454;Username=user13;Password=keNBA0yK");
public DbSet<Student> Students { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Diary> Diaries { get; set; }
public DbSet<Subject> Subjects { get; set; }
public DbSet<Traffic> Traffics { get; set; }
public DbSet<StudentGroupSubject> StudentGroupsSubjects { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Property(it => it.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Group>().Property(it => it.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Traffic>().Property(it => it.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<StudentGroupSubject>().Property(it => it.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Diary>().Property(it => it.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Subject>().Property(it => it.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Student>()
.HasOne(it => it.Group);
modelBuilder.Entity<Diary>()
.HasOne(it => it.StudentGroupSubject);
modelBuilder.Entity<Diary>()
.HasOne(it => it.Traffic);
modelBuilder.Entity<Diary>()
.HasOne(it => it.Student);
modelBuilder.Entity<StudentGroupSubject>()
.HasOne(it => it.Group);
modelBuilder.Entity<StudentGroupSubject>()
.HasOne(it => it.Subject);
}
}

View File

@ -0,0 +1,222 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using data;
#nullable disable
namespace data.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20241120062915_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateOnly>("Date")
.HasColumnType("date");
b.Property<string>("DiaryText")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NumberSubject")
.HasColumnType("integer");
b.Property<int>("StudentGroupSubjectId")
.HasColumnType("integer");
b.Property<int>("TrafficId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StudentGroupSubjectId");
b.HasIndex("TrafficId");
b.ToTable("Diaries");
});
modelBuilder.Entity("data.DAO.Group", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Groups");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Patronymic")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("GroupId");
b.ToTable("Students");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<int>("SemesterEnd")
.HasColumnType("integer");
b.Property<int>("SemesterStart")
.HasColumnType("integer");
b.Property<int>("SubjectId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GroupId");
b.HasIndex("SubjectId");
b.ToTable("StudentGroupsSubjects");
});
modelBuilder.Entity("data.DAO.Subject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Subjects");
});
modelBuilder.Entity("data.DAO.Traffic", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Traffics");
});
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.HasOne("data.DAO.StudentGroupSubject", "StudentGroupSubject")
.WithMany()
.HasForeignKey("StudentGroupSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Traffic", "Traffic")
.WithMany()
.HasForeignKey("TrafficId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("StudentGroupSubject");
b.Navigation("Traffic");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany()
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany()
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Subject", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
b.Navigation("Subject");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,181 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace data.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Groups",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Groups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Subjects",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Subjects", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Traffics",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Traffics", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Students",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
LastName = table.Column<string>(type: "text", nullable: false),
FirstName = table.Column<string>(type: "text", nullable: false),
Patronymic = table.Column<string>(type: "text", nullable: false),
GroupId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Students", x => x.Id);
table.ForeignKey(
name: "FK_Students_Groups_GroupId",
column: x => x.GroupId,
principalTable: "Groups",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "StudentGroupsSubjects",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
GroupId = table.Column<int>(type: "integer", nullable: false),
SemesterStart = table.Column<int>(type: "integer", nullable: false),
SemesterEnd = table.Column<int>(type: "integer", nullable: false),
SubjectId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_StudentGroupsSubjects", x => x.Id);
table.ForeignKey(
name: "FK_StudentGroupsSubjects_Groups_GroupId",
column: x => x.GroupId,
principalTable: "Groups",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_StudentGroupsSubjects_Subjects_SubjectId",
column: x => x.SubjectId,
principalTable: "Subjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Diaries",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
DiaryText = table.Column<string>(type: "text", nullable: false),
TrafficId = table.Column<int>(type: "integer", nullable: false),
StudentGroupSubjectId = table.Column<int>(type: "integer", nullable: false),
NumberSubject = table.Column<int>(type: "integer", nullable: false),
Date = table.Column<DateOnly>(type: "date", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Diaries", x => x.Id);
table.ForeignKey(
name: "FK_Diaries_StudentGroupsSubjects_StudentGroupSubjectId",
column: x => x.StudentGroupSubjectId,
principalTable: "StudentGroupsSubjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Diaries_Traffics_TrafficId",
column: x => x.TrafficId,
principalTable: "Traffics",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Diaries_StudentGroupSubjectId",
table: "Diaries",
column: "StudentGroupSubjectId");
migrationBuilder.CreateIndex(
name: "IX_Diaries_TrafficId",
table: "Diaries",
column: "TrafficId");
migrationBuilder.CreateIndex(
name: "IX_StudentGroupsSubjects_GroupId",
table: "StudentGroupsSubjects",
column: "GroupId");
migrationBuilder.CreateIndex(
name: "IX_StudentGroupsSubjects_SubjectId",
table: "StudentGroupsSubjects",
column: "SubjectId");
migrationBuilder.CreateIndex(
name: "IX_Students_GroupId",
table: "Students",
column: "GroupId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Diaries");
migrationBuilder.DropTable(
name: "Students");
migrationBuilder.DropTable(
name: "StudentGroupsSubjects");
migrationBuilder.DropTable(
name: "Traffics");
migrationBuilder.DropTable(
name: "Groups");
migrationBuilder.DropTable(
name: "Subjects");
}
}
}

View File

@ -0,0 +1,240 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using data;
#nullable disable
namespace data.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20241122130535_Fix")]
partial class Fix
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateOnly>("Date")
.HasColumnType("date");
b.Property<string>("DiaryText")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NumberSubject")
.HasColumnType("integer");
b.Property<int>("StudentGroupSubjectId")
.HasColumnType("integer");
b.Property<int>("TrafficId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StudentGroupSubjectId");
b.HasIndex("TrafficId");
b.ToTable("Diaries");
});
modelBuilder.Entity("data.DAO.Group", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Groups");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Patronymic")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("GroupId");
b.ToTable("Students");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<int>("SemesterEnd")
.HasColumnType("integer");
b.Property<int>("SemesterStart")
.HasColumnType("integer");
b.Property<int>("StudentId")
.HasColumnType("integer");
b.Property<int>("SubjectId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GroupId");
b.HasIndex("StudentId");
b.HasIndex("SubjectId");
b.ToTable("StudentGroupsSubjects");
});
modelBuilder.Entity("data.DAO.Subject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Subjects");
});
modelBuilder.Entity("data.DAO.Traffic", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Traffics");
});
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.HasOne("data.DAO.StudentGroupSubject", "StudentGroupSubject")
.WithMany()
.HasForeignKey("StudentGroupSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Traffic", "Traffic")
.WithMany()
.HasForeignKey("TrafficId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("StudentGroupSubject");
b.Navigation("Traffic");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany("Students")
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany()
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Student", "Student")
.WithMany()
.HasForeignKey("StudentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Subject", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
b.Navigation("Student");
b.Navigation("Subject");
});
modelBuilder.Entity("data.DAO.Group", b =>
{
b.Navigation("Students");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace data.Migrations
{
/// <inheritdoc />
public partial class Fix : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "StudentId",
table: "StudentGroupsSubjects",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_StudentGroupsSubjects_StudentId",
table: "StudentGroupsSubjects",
column: "StudentId");
migrationBuilder.AddForeignKey(
name: "FK_StudentGroupsSubjects_Students_StudentId",
table: "StudentGroupsSubjects",
column: "StudentId",
principalTable: "Students",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_StudentGroupsSubjects_Students_StudentId",
table: "StudentGroupsSubjects");
migrationBuilder.DropIndex(
name: "IX_StudentGroupsSubjects_StudentId",
table: "StudentGroupsSubjects");
migrationBuilder.DropColumn(
name: "StudentId",
table: "StudentGroupsSubjects");
}
}
}

View File

@ -0,0 +1,240 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using data;
#nullable disable
namespace data.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20241122130744_FixIssueAgain")]
partial class FixIssueAgain
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateOnly>("Date")
.HasColumnType("date");
b.Property<string>("DiaryText")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NumberSubject")
.HasColumnType("integer");
b.Property<int>("StudentGroupSubjectId")
.HasColumnType("integer");
b.Property<int>("StudentId")
.HasColumnType("integer");
b.Property<int>("TrafficId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StudentGroupSubjectId");
b.HasIndex("StudentId");
b.HasIndex("TrafficId");
b.ToTable("Diaries");
});
modelBuilder.Entity("data.DAO.Group", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Groups");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Patronymic")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("GroupId");
b.ToTable("Students");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<int>("SemesterEnd")
.HasColumnType("integer");
b.Property<int>("SemesterStart")
.HasColumnType("integer");
b.Property<int>("SubjectId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GroupId");
b.HasIndex("SubjectId");
b.ToTable("StudentGroupsSubjects");
});
modelBuilder.Entity("data.DAO.Subject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Subjects");
});
modelBuilder.Entity("data.DAO.Traffic", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Traffics");
});
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.HasOne("data.DAO.StudentGroupSubject", "StudentGroupSubject")
.WithMany()
.HasForeignKey("StudentGroupSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Student", "Student")
.WithMany()
.HasForeignKey("StudentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Traffic", "Traffic")
.WithMany()
.HasForeignKey("TrafficId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Student");
b.Navigation("StudentGroupSubject");
b.Navigation("Traffic");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany("Students")
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany()
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Subject", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
b.Navigation("Subject");
});
modelBuilder.Entity("data.DAO.Group", b =>
{
b.Navigation("Students");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,82 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace data.Migrations
{
/// <inheritdoc />
public partial class FixIssueAgain : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_StudentGroupsSubjects_Students_StudentId",
table: "StudentGroupsSubjects");
migrationBuilder.DropIndex(
name: "IX_StudentGroupsSubjects_StudentId",
table: "StudentGroupsSubjects");
migrationBuilder.DropColumn(
name: "StudentId",
table: "StudentGroupsSubjects");
migrationBuilder.AddColumn<int>(
name: "StudentId",
table: "Diaries",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_Diaries_StudentId",
table: "Diaries",
column: "StudentId");
migrationBuilder.AddForeignKey(
name: "FK_Diaries_Students_StudentId",
table: "Diaries",
column: "StudentId",
principalTable: "Students",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Diaries_Students_StudentId",
table: "Diaries");
migrationBuilder.DropIndex(
name: "IX_Diaries_StudentId",
table: "Diaries");
migrationBuilder.DropColumn(
name: "StudentId",
table: "Diaries");
migrationBuilder.AddColumn<int>(
name: "StudentId",
table: "StudentGroupsSubjects",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_StudentGroupsSubjects_StudentId",
table: "StudentGroupsSubjects",
column: "StudentId");
migrationBuilder.AddForeignKey(
name: "FK_StudentGroupsSubjects_Students_StudentId",
table: "StudentGroupsSubjects",
column: "StudentId",
principalTable: "Students",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -0,0 +1,237 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using data;
#nullable disable
namespace data.Migrations
{
[DbContext(typeof(DatabaseContext))]
partial class DatabaseContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateOnly>("Date")
.HasColumnType("date");
b.Property<string>("DiaryText")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NumberSubject")
.HasColumnType("integer");
b.Property<int>("StudentGroupSubjectId")
.HasColumnType("integer");
b.Property<int>("StudentId")
.HasColumnType("integer");
b.Property<int>("TrafficId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StudentGroupSubjectId");
b.HasIndex("StudentId");
b.HasIndex("TrafficId");
b.ToTable("Diaries");
});
modelBuilder.Entity("data.DAO.Group", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Groups");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Patronymic")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("GroupId");
b.ToTable("Students");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("GroupId")
.HasColumnType("integer");
b.Property<int>("SemesterEnd")
.HasColumnType("integer");
b.Property<int>("SemesterStart")
.HasColumnType("integer");
b.Property<int>("SubjectId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GroupId");
b.HasIndex("SubjectId");
b.ToTable("StudentGroupsSubjects");
});
modelBuilder.Entity("data.DAO.Subject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Subjects");
});
modelBuilder.Entity("data.DAO.Traffic", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Traffics");
});
modelBuilder.Entity("data.DAO.Diary", b =>
{
b.HasOne("data.DAO.StudentGroupSubject", "StudentGroupSubject")
.WithMany()
.HasForeignKey("StudentGroupSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Student", "Student")
.WithMany()
.HasForeignKey("StudentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Traffic", "Traffic")
.WithMany()
.HasForeignKey("TrafficId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Student");
b.Navigation("StudentGroupSubject");
b.Navigation("Traffic");
});
modelBuilder.Entity("data.DAO.Student", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany("Students")
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
});
modelBuilder.Entity("data.DAO.StudentGroupSubject", b =>
{
b.HasOne("data.DAO.Group", "Group")
.WithMany()
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("data.DAO.Subject", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
b.Navigation("Subject");
});
modelBuilder.Entity("data.DAO.Group", b =>
{
b.Navigation("Students");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace data.Repository
{
public interface IAdminRepository
{
void AddStudentsToGroup(int groupId, IEnumerable<int> studentIds);
void AddSubjectsToGroup(int groupId, IEnumerable<int> subjectIds);
}
}

View File

@ -0,0 +1,67 @@
using data.DAO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace data.Repository
{
public interface IGroupRepository
{
/// <summary>
/// Получение списка групп
/// </summary>
public IEnumerable<Group> GetAllGroups();
/// <summary>
/// Создание группы
/// </summary>
/// <param name="group">группа</param>
public bool CreateGroup(Group group);
/// <summary>
/// Создание группы вместо со студентами
/// </summary>
/// <param name="group">группа</param>
/// <param name="students">список студентов</param>
public bool AddGroupWithStudents(Group group, IEnumerable<Student> students);
/// <summary>
/// TODO
/// </summary>
/// <param name="id"></param>
/// <param name="students"></param>
/// <returns></returns>
public bool AddStudentsToGroup(int id, IEnumerable<Student> students);
/// <summary>
/// Удаление группы по индетефикатору
/// </summary>
/// <param name="id">индетефикатор</param>
public bool DeleteGroup(int id);
/// <summary>
/// Обновление наименование группы
/// </summary>
/// <param name="id">индетефикатор</param>
/// <param name="name">наименованвие</param>
public bool UpdateGroup(int id, string name);
/// <summary>
/// Удаление всех студентов из группы
/// </summary>
/// <param name="id">индетефикатор</param>
/// <returns></returns>
public bool RemoveStudentsFromGroup(int id);
/// <summary>
/// Удаление студентов из группы по их индетефикатору
/// </summary>
/// <param name="id">индетефикатор группы</param>
/// <param name="studentIds">список индетефикаторов студентов</param>
/// <returns></returns>
public bool RemoveStudentsFromGroupByIds(int id, IEnumerable<int> studentIds);
}
}

View File

@ -0,0 +1,23 @@
using data.DAO;
using System;
using System.Collections.Generic;
namespace data.Repository
{
public interface IPresenceRepository
{
IEnumerable<Diary> GetPresence(
int groupId,
int? subjectId = null,
DateOnly? date = null,
int? studentId = null);
public void DeleteAllPresence();
public void DeleteGroupPresence(int groupId);
public void AddPresenceRecords(List<Diary> items);
public void UpdatePresenceRecords(List<Diary> items);
}
}

View File

@ -0,0 +1,58 @@
using data.Repository;
using data.DAO;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
namespace data.Repository
{
public class SQLAdminRepository : IAdminRepository
{
private readonly DatabaseContext _context;
public SQLAdminRepository(DatabaseContext context)
{
_context = context;
}
public void AddStudentsToGroup(int groupId, IEnumerable<int> studentIds)
{
var group = _context.Groups.Include(g => g.Students).FirstOrDefault(g => g.Id == groupId);
if (group == null) return;
var studentsToAdd = _context.Students
.Where(u => studentIds.Contains(u.Id))
.ToList();
foreach (var student in studentsToAdd)
{
if (!group.Students.Any(u => u.Id == student.Id))
{
group.Students.Add(student);
}
}
_context.SaveChanges();
}
public void AddSubjectsToGroup(int groupId, IEnumerable<int> subjectIds)
{
var group = _context.Groups.Include(g => g.StudentGroupsSubjects).ThenInclude(s => s.Subject).FirstOrDefault(g => g.Id == groupId);
if (group == null) return;
var subjectsToAdd = _context.Subjects
.Where(s => subjectIds.Contains(s.Id))
.ToList();
foreach (var subject in subjectsToAdd)
{
if (!group.StudentGroupsSubjects.Any(s => s.Subject.Id == subject.Id))
{
group.StudentGroupsSubjects.Add(new StudentGroupSubject { Subject = subject });
}
}
_context.SaveChanges();
}
}
}

View File

@ -0,0 +1,119 @@
using data.DAO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace data.Repository
{
public class SQLGroupRepository : IGroupRepository
{
private readonly DatabaseContext _dbContext;
public SQLGroupRepository(DatabaseContext dbContext)
{
_dbContext = dbContext;
}
public bool AddGroupWithStudents(Group group, IEnumerable<Student> students)
{
using var transaction = _dbContext.Database.BeginTransaction();
try
{
_dbContext.Groups.Add(group);
_dbContext.SaveChanges();
foreach (var item in students)
{
item.Group = group;
_dbContext.Students.Add(item);
}
_dbContext.SaveChanges();
transaction.Commit();
return true;
}
catch (Exception ex)
{
Debug.WriteLine("Во время добавления группы со студентами произошла ошибка: ", ex.Message);
transaction.Rollback();
}
return false;
}
public bool AddStudentsToGroup(int id, IEnumerable<Student> students)
{
using var transaction = _dbContext.Database.BeginTransaction();
try
{
Group group = _dbContext.Groups.Find(id);
foreach (var student in students)
{
student.Group = group;
student.GroupId = id;
_dbContext.Students.Add(student);
}
_dbContext.SaveChanges();
transaction.Commit();
return true;
}
catch (Exception ex)
{
Console.WriteLine("Во время добавления студентов в группу произошла ошибка: ", ex.Message);
transaction.Rollback();
}
return false;
}
public bool CreateGroup(Group group)
{
_dbContext.Groups.Add(group);
return _dbContext.SaveChanges() > 0;
}
public bool DeleteGroup(int id)
{
Group group = _dbContext.Groups.Single(g => g.Id == id);
_dbContext.Groups.Remove(group);
return _dbContext.SaveChanges() > 0;
}
public IEnumerable<Group> GetAllGroups()
=> _dbContext.Groups
.Include(g => g.Students)
.Include(g => g.StudentGroupsSubjects)
.ThenInclude(sgs => sgs.Subject)
.ToList();
public bool UpdateGroup(int id, string name)
{
Group group = _dbContext.Groups.Single(g => g.Id == id);
group.Name = name;
return _dbContext.SaveChanges() > 0;
}
public bool RemoveStudentsFromGroup(int id)
{
var students = _dbContext.Students.Where(s => s.GroupId == id).ToList();
_dbContext.Students.RemoveRange(students);
return _dbContext.SaveChanges() > 0;
}
public bool RemoveStudentsFromGroupByIds(int id, IEnumerable<int> studentIds)
{
var studentsToRemove = _dbContext.Students
.Where(s => s.GroupId == id && studentIds.Contains(s.Id))
.ToList();
if (studentsToRemove.Any())
{
_dbContext.Students.RemoveRange(studentsToRemove);
return _dbContext.SaveChanges() > 0;
}
return false;
}
}
}

View File

@ -0,0 +1,83 @@
using data.DAO;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
namespace data.Repository
{
public class SQLPresenceRepository : IPresenceRepository
{
private readonly DatabaseContext _context;
public SQLPresenceRepository(DatabaseContext context)
{
_context = context;
}
public IEnumerable<Diary> GetPresence(
int groupId,
int? subjectId = null,
DateOnly? date = null,
int? studentId = null)
{
var query = _context.Diaries
.Include(p => p.Student)
.Include(p => p.StudentGroupSubject)
.ThenInclude(p => p.Subject)
.Include(p => p.Traffic)
.Where(p => p.Student.GroupId == groupId);
if (subjectId.HasValue)
query = query.Where(p => p.StudentGroupSubject.Subject.Id == subjectId.Value);
if (date.HasValue)
query = query.Where(p => p.Date == date.Value);
if (studentId.HasValue)
query = query.Where(p => p.Student.Id == studentId.Value);
return query.ToList();
}
public void DeleteAllPresence()
{
_context.Diaries.RemoveRange(_context.Diaries);
_context.SaveChanges();
}
public void DeleteGroupPresence(int groupId)
{
var presencesToDelete = _context.Diaries
.Include(p => p.Student)
.Where(p => p.Student.GroupId == groupId);
_context.Diaries.RemoveRange(presencesToDelete);
_context.SaveChanges();
}
public void AddPresenceRecords(List<Diary> items)
{
_context.Diaries.AddRange(items);
_context.SaveChanges();
}
public void UpdatePresenceRecords(List<Diary> items)
{
foreach (var item in items)
{
var existingRecord = _context.Diaries
.FirstOrDefault(p =>
p.Date == item.Date &&
p.NumberSubject == item.NumberSubject &&
p.StudentId == item.StudentId);
if (existingRecord != null)
{
existingRecord.TrafficId = item.TrafficId;
}
}
_context.SaveChanges();
}
}
}

18
data/data.csproj Normal file
View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
</ItemGroup>
</Project>

7
domain/Class1.cs Normal file
View File

@ -0,0 +1,7 @@
namespace domain
{
public class Class1
{
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Entity
{
public class GroupEntity
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<UserEntity> Users { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Entity
{
public class GroupSubjectEntity
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<SubjectEntity> Subjects { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace domain.Entity
{
public class PresenceEntity
{
public int Id { get; set; }
public int StudentId { get; set; }
public UserEntity Student { get; set; }
public int SubjectId { get; set; }
public SubjectEntity Subject { get; set; }
public int TrafficId { get; set; }
public TrafficEntity Traffic { get; set; }
public int LessonNumber { get; set; }
public DateOnly Date { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Entity
{
public class SubjectEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Entity
{
public class TrafficEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Entity
{
public class UserEntity
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Patronymic { get; set; }
public GroupEntity Group { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Request
{
public class AddGroupRequest
{
public string Name { get; set; }
public List<AddStudentRequest> Students { get; set; } = new List<AddStudentRequest>();
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Request
{
public class AddGroupWithStudentRequest
{
public AddGroupRequest AddGroupRequest { get; set; }
public IEnumerable<AddStudentRequest> AddStudentRequests { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace domain.Request
{
public class AddPresenceRequest
{
public List<PresenceItem> Items { get; set; } = new List<PresenceItem>();
}
public class PresenceItem
{
public DateOnly Date { get; set; }
public int StudentGroupSubjectId { get; set; }
public int LessonNumber { get; set; }
public int StudentId { get; set; }
public int TrafficId { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Request
{
public class AddStudentRequest
{
public string LastName { get; set; }
public string FirstName { get; set; }
public string Patronymic { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace domain.Request
{
public class AddStudentsToGroupRequest
{
public List<int> Students { get; set; } = new List<int>();
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace domain.Request
{
public class AddSubjectsToGroupRequest
{
public List<int> Subjects { get; set; } = new List<int>();
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Request
{
public class EditGroupRequest
{
public int GroupId { get; set; }
public string GroupName { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Request
{
public class RemoveGroupRequest
{
public int GroupId { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
namespace domain.Request
{
public class UpdatePresenceRequest
{
public List<UpdatePresenceItem> Items { get; set; } = new List<UpdatePresenceItem>();
}
public class UpdatePresenceItem
{
public DateOnly Date { get; set; }
public int LessonNumber { get; set; }
public int StudentId { get; set; }
public int TrafficId { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using data.Repository;
using domain.Request;
using domain.UseCase;
namespace domain.Service
{
public class AdminService : IAdminUseCase
{
private readonly IAdminRepository _adminRepository;
public AdminService(IAdminRepository adminRepository)
{
_adminRepository = adminRepository;
}
public void AddStudentsToGroup(int groupId, AddStudentsToGroupRequest request)
{
_adminRepository.AddStudentsToGroup(groupId, request.Students);
}
public void AddSubjectsToGroup(int groupId, AddSubjectsToGroupRequest request)
{
_adminRepository.AddSubjectsToGroup(groupId, request.Subjects);
}
}
}

View File

@ -0,0 +1,107 @@
using data.DAO;
using data.Repository;
using domain.Entity;
using domain.Request;
using domain.UseCase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.Service
{
public class GroupService : IGroupUseCase
{
private readonly IGroupRepository _groupRepository;
public GroupService(IGroupRepository groupRepository)
{
_groupRepository = groupRepository;
}
public bool AddGroup(AddGroupRequest addGroupRequest)
=> _groupRepository.CreateGroup(new Group { Name = addGroupRequest.Name });
public void AddGroupWithStudents(AddGroupWithStudentRequest addGroupWithStudentRequest)
{
Group group = new Group { Name = addGroupWithStudentRequest.AddGroupRequest.Name };
List<Student> students = addGroupWithStudentRequest
.AddStudentRequests
.Select(it => new Student { FirstName = it.FirstName, LastName = it.LastName, Patronymic = it.Patronymic})
.ToList();
_groupRepository.AddGroupWithStudents(group, students);
}
public void AddStudentsToGroup(int id, IEnumerable<AddStudentRequest> students)
{
List<Student> studentList = students
.Select(it => new Student { FirstName = it.FirstName, LastName = it.LastName, Patronymic = it.Patronymic })
.ToList();
_groupRepository.AddStudentsToGroup(id, studentList);
}
public void EditGroup(EditGroupRequest editGroupRequest)
=> _groupRepository.UpdateGroup(editGroupRequest.GroupId, editGroupRequest.GroupName);
public IEnumerable<GroupEntity> GetGroupsWithStudents()
{
return _groupRepository.GetAllGroups().Select(
group => new GroupEntity
{
Id = group.Id,
Name = group.Name,
Users = group.Students.Select(
user => new UserEntity
{
Id = user.Id,
LastName = user.LastName,
FirstName = user.FirstName,
Patronymic = user.Patronymic,
Group = new GroupEntity
{
Id = group.Id,
Name = group.Name
}
})
});
}
public bool RemoveGroup(RemoveGroupRequest removeGroupRequest)
=> _groupRepository.DeleteGroup(removeGroupRequest.GroupId);
public GroupSubjectEntity GetGroupSubject(int id)
{
return _groupRepository.GetAllGroups().Select(
group => new GroupSubjectEntity
{
Id = group.Id,
Name = group.Name,
Subjects = group.StudentGroupsSubjects.Select(
subject => new SubjectEntity
{
Id = subject.Subject.Id,
Name = subject.Subject.Name,
})
}).Single(g => g.Id == id);
}
public void RemoveStudentsFromGroup(int groupId)
{
_groupRepository.RemoveStudentsFromGroup(groupId);
}
public void RemoveStudentsFromGroupByIds(int groupId, IEnumerable<int> studentIds)
{
var group = _groupRepository.GetAllGroups().FirstOrDefault(g => g.Id == groupId);
if (group == null)
{
throw new KeyNotFoundException($"Группа с ID {groupId} не найдена.");
}
_groupRepository.RemoveStudentsFromGroupByIds(groupId, studentIds);
}
}
}

View File

@ -0,0 +1,109 @@
using data.DAO;
using data.Repository;
using domain.Entity;
using domain.Request;
using domain.UseCase;
using System;
using System.Collections.Generic;
namespace domain.Service
{
public class PresenceService : IPresenceUseCase
{
private readonly IPresenceRepository _presenceRepository;
public PresenceService(IPresenceRepository presenceRepository)
{
_presenceRepository = presenceRepository;
}
public IEnumerable<PresenceEntity> GetPresence(
int groupId,
int? subjectId = null,
DateOnly? date = null,
int? studentId = null)
{
return _presenceRepository.GetPresence(groupId, subjectId, date, studentId).Select(p => new PresenceEntity
{
Id = p.Id,
StudentId = p.Student.Id,
Student = new UserEntity
{
Id = p.Student.Id,
FirstName = p.Student.FirstName,
LastName = p.Student.LastName,
Patronymic = p.Student.Patronymic,
Group = new GroupEntity()
},
SubjectId = p.StudentGroupSubject.Subject.Id,
Subject = new SubjectEntity
{
Id = p.StudentGroupSubject.Subject.Id,
Name = p.StudentGroupSubject.Subject.Name
},
TrafficId = p.TrafficId,
Traffic = new TrafficEntity
{
Id = p.TrafficId,
Name = p.Traffic.Name
},
LessonNumber = p.NumberSubject,
Date = p.Date
});
}
public void DeleteAllPresence()
{
_presenceRepository.DeleteAllPresence();
}
public void DeleteGroupPresence(int groupId)
{
_presenceRepository.DeleteGroupPresence(groupId);
}
public void AddPresenceRecords(AddPresenceRequest request)
{
if (request?.Items == null || request.Items.Count == 0)
throw new ArgumentException("No records");
List<Diary> diaries = new List<Diary>();
foreach (var item in request.Items)
{
diaries.Add(new Diary
{
Date = item.Date,
DiaryText = "",
NumberSubject = item.LessonNumber,
StudentId = item.StudentId,
TrafficId = item.TrafficId,
StudentGroupSubjectId = item.StudentGroupSubjectId
});
}
_presenceRepository.AddPresenceRecords(diaries);
}
public void UpdatePresenceRecords(UpdatePresenceRequest request)
{
if (request?.Items == null || request.Items.Count == 0)
throw new ArgumentException("No records");
List<Diary> diaries = new List<Diary>();
foreach (var item in request.Items)
{
diaries.Add(new Diary
{
Date = item.Date,
NumberSubject = item.LessonNumber,
StudentId = item.StudentId,
TrafficId = item.TrafficId,
});
}
_presenceRepository.UpdatePresenceRecords(diaries);
}
}
}

View File

@ -0,0 +1,10 @@
using domain.Request;
namespace domain.UseCase
{
public interface IAdminUseCase
{
void AddStudentsToGroup(int groupId, AddStudentsToGroupRequest request);
void AddSubjectsToGroup(int groupId, AddSubjectsToGroupRequest request);
}
}

View File

@ -0,0 +1,31 @@
using domain.Entity;
using domain.Request;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace domain.UseCase
{
public interface IGroupUseCase
{
public bool AddGroup(AddGroupRequest addGroupRequest);
public void AddGroupWithStudents(AddGroupWithStudentRequest addGroupWithStudentRequest);
public void AddStudentsToGroup(int id, IEnumerable<AddStudentRequest> students);
public bool RemoveGroup(RemoveGroupRequest removeGroupRequest);
public void EditGroup(EditGroupRequest editGroupRequest);
public IEnumerable<GroupEntity> GetGroupsWithStudents();
public GroupSubjectEntity GetGroupSubject(int id);
public void RemoveStudentsFromGroup(int groupId);
public void RemoveStudentsFromGroupByIds(int groupId, IEnumerable<int> studentIds);
}
}

View File

@ -0,0 +1,21 @@
using domain.Entity;
using domain.Request;
using System;
using System.Collections.Generic;
namespace domain.UseCase
{
public interface IPresenceUseCase
{
IEnumerable<PresenceEntity> GetPresence(
int groupId,
int? subjectId = null,
DateOnly? date = null,
int? studentId = null);
void DeleteAllPresence();
void DeleteGroupPresence(int groupId);
void AddPresenceRecords(AddPresenceRequest request);
void UpdatePresenceRecords(UpdatePresenceRequest request);
}
}

19
domain/domain.csproj Normal file
View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Response\**" />
<EmbeddedResource Remove="Response\**" />
<None Remove="Response\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\data\data.csproj" />
</ItemGroup>
</Project>

49
presence.sln Normal file
View File

@ -0,0 +1,49 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "data", "data\data.csproj", "{BB350D5F-DE0B-465C-909F-6A179AA03C3A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console_ui", "console_ui\console_ui.csproj", "{BD6F5F76-9186-4301-8506-EB78220FA095}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "domain", "domain\domain.csproj", "{4D39120E-C021-4D38-BB4D-2F58BDE5FE17}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Presence.API", "Presence.API\Presence.API.csproj", "{88AE15FA-7E2D-4D7C-A75B-7DAC9742B32A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Presence.Desktop", "Presence.Desktop\Presence.Desktop.csproj", "{03BA523C-7C5B-4FE1-BAC1-461E4B4F7FCD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BB350D5F-DE0B-465C-909F-6A179AA03C3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB350D5F-DE0B-465C-909F-6A179AA03C3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB350D5F-DE0B-465C-909F-6A179AA03C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB350D5F-DE0B-465C-909F-6A179AA03C3A}.Release|Any CPU.Build.0 = Release|Any CPU
{BD6F5F76-9186-4301-8506-EB78220FA095}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD6F5F76-9186-4301-8506-EB78220FA095}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD6F5F76-9186-4301-8506-EB78220FA095}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD6F5F76-9186-4301-8506-EB78220FA095}.Release|Any CPU.Build.0 = Release|Any CPU
{4D39120E-C021-4D38-BB4D-2F58BDE5FE17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D39120E-C021-4D38-BB4D-2F58BDE5FE17}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D39120E-C021-4D38-BB4D-2F58BDE5FE17}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D39120E-C021-4D38-BB4D-2F58BDE5FE17}.Release|Any CPU.Build.0 = Release|Any CPU
{88AE15FA-7E2D-4D7C-A75B-7DAC9742B32A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{88AE15FA-7E2D-4D7C-A75B-7DAC9742B32A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88AE15FA-7E2D-4D7C-A75B-7DAC9742B32A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88AE15FA-7E2D-4D7C-A75B-7DAC9742B32A}.Release|Any CPU.Build.0 = Release|Any CPU
{03BA523C-7C5B-4FE1-BAC1-461E4B4F7FCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{03BA523C-7C5B-4FE1-BAC1-461E4B4F7FCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03BA523C-7C5B-4FE1-BAC1-461E4B4F7FCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03BA523C-7C5B-4FE1-BAC1-461E4B4F7FCD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D71F226C-A42E-49C2-9E51-10BE305FFC48}
EndGlobalSection
EndGlobal