From b84da3b29a48a3fd68f57e251b277c5728da2c33 Mon Sep 17 00:00:00 2001 From: 1eG0ist Date: Thu, 12 Dec 2024 21:16:01 +0300 Subject: [PATCH] right-version --- Presence.Desktop/App.axaml.cs | 6 +- Presence.Desktop/Models/GroupPresenter.cs | 15 +- Presence.Desktop/Models/StudentPresenter.cs | 19 ++ Presence.Desktop/Models/SubjectPresenter.cs | 18 + Presence.Desktop/Models/UserPresenter.cs | 15 - .../Presence - Backup.Desktop.csproj | 37 ++ Presence.Desktop/ViewLocator.cs | 23 +- Presence.Desktop/ViewModels/GroupViewModel.cs | 310 +++++++++++++---- .../ViewModels/MainWindowViewModel.cs | 16 - Presence.Desktop/Views/GroupView.axaml | 73 ++-- Presence.Desktop/Views/GroupView.axaml.cs | 23 +- Presence.Desktop/Views/MainWindow.axaml | 19 -- Presence.Desktop/Views/MainWindow.axaml.cs | 18 - ...0241212071056_InitialCreate123.Designer.cs | 320 ------------------ .../20241212071056_InitialCreate123.cs | 243 ------------- .../20241212071851_InitialCreate2.Designer.cs | 320 ------------------ .../20241212071851_InitialCreate2.cs | 243 ------------- ...> 20241212072957_WorkWithData.Designer.cs} | 4 +- ...ate2.cs => 20241212072957_WorkWithData.cs} | 2 +- data/data.csproj | 5 - domain/Entity/GroupEntity.cs | 9 +- domain/Entity/StudentEntity.cs | 19 ++ domain/Entity/SubjectEntity.cs | 18 + domain/Entity/UserEntity.cs | 15 - domain/Request/AddStudentRequest.cs | 4 +- domain/Request/AddSubjectRequest.cs | 13 + domain/Request/AttendanceRequest.cs | 17 + domain/Request/AttendanceUpdateRequest.cs | 17 + domain/Service/GroupService.cs | 224 +++++++++--- domain/UseCase/IGroupUseCase.cs | 47 ++- semesterWork.sln | 2 +- 31 files changed, 729 insertions(+), 1385 deletions(-) create mode 100644 Presence.Desktop/Models/StudentPresenter.cs create mode 100644 Presence.Desktop/Models/SubjectPresenter.cs delete mode 100644 Presence.Desktop/Models/UserPresenter.cs create mode 100644 Presence.Desktop/Presence - Backup.Desktop.csproj delete mode 100644 Presence.Desktop/ViewModels/MainWindowViewModel.cs delete mode 100644 Presence.Desktop/Views/MainWindow.axaml delete mode 100644 Presence.Desktop/Views/MainWindow.axaml.cs delete mode 100644 data/Migrations/20241212071056_InitialCreate123.Designer.cs delete mode 100644 data/Migrations/20241212071056_InitialCreate123.cs delete mode 100644 data/Migrations/20241212071851_InitialCreate2.Designer.cs delete mode 100644 data/Migrations/20241212071851_InitialCreate2.cs rename data/Migrations/{20241212072022_InitialCreate2.Designer.cs => 20241212072957_WorkWithData.Designer.cs} (99%) rename data/Migrations/{20241212072022_InitialCreate2.cs => 20241212072957_WorkWithData.cs} (99%) create mode 100644 domain/Entity/StudentEntity.cs create mode 100644 domain/Entity/SubjectEntity.cs delete mode 100644 domain/Entity/UserEntity.cs create mode 100644 domain/Request/AddSubjectRequest.cs create mode 100644 domain/Request/AttendanceRequest.cs create mode 100644 domain/Request/AttendanceUpdateRequest.cs diff --git a/Presence.Desktop/App.axaml.cs b/Presence.Desktop/App.axaml.cs index 023b7c1..2cdb41d 100644 --- a/Presence.Desktop/App.axaml.cs +++ b/Presence.Desktop/App.axaml.cs @@ -20,13 +20,13 @@ namespace Presence.Desktop var serviceCollection = new ServiceCollection(); serviceCollection.AddCommonService(); var services = serviceCollection.BuildServiceProvider(); - var mainViewModel = services.GetRequiredService(); + var maimViewModel = services.GetRequiredService(); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow() + desktop.MainWindow = new GroupView() { - DataContext = new MainWindowViewModel(services), + DataContext = maimViewModel, }; } diff --git a/Presence.Desktop/Models/GroupPresenter.cs b/Presence.Desktop/Models/GroupPresenter.cs index eb8c3be..4b0513d 100644 --- a/Presence.Desktop/Models/GroupPresenter.cs +++ b/Presence.Desktop/Models/GroupPresenter.cs @@ -1,17 +1,16 @@ -using Avalonia.Controls; -using ReactiveUI; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Presence.Desktop.Models +namespace Presense.Desktop.Models { public class GroupPresenter { - public int Id { get; set; } - public string Name { get; set; } - public IEnumerable? users { get; set; } = null; + public int GroupId { get; set; } + public string GroupName { get; set; } = string.Empty; + public List? Students { get; set; } = null; + public List Subjects { get; set; } = new(); } -} \ No newline at end of file +} diff --git a/Presence.Desktop/Models/StudentPresenter.cs b/Presence.Desktop/Models/StudentPresenter.cs new file mode 100644 index 0000000..7ca273a --- /dev/null +++ b/Presence.Desktop/Models/StudentPresenter.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Presense.Desktop.Models +{ + public class StudentPresenter + { + public int Id { get; set; } + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string Patronymic { get; set; } = string.Empty; + + // Ссылка на группу, к которой относится студент + public GroupPresenter? Group { get; set; } + } +} diff --git a/Presence.Desktop/Models/SubjectPresenter.cs b/Presence.Desktop/Models/SubjectPresenter.cs new file mode 100644 index 0000000..32aed5e --- /dev/null +++ b/Presence.Desktop/Models/SubjectPresenter.cs @@ -0,0 +1,18 @@ +using domain.Entity; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Presense.Desktop.Models +{ + public class SubjectPresenter + { + public int SubjectId { get; set; } + public string SubjectName { get; set; } + + public int GroupId { get; set; } + public GroupEntity Group { get; set; } + } +} diff --git a/Presence.Desktop/Models/UserPresenter.cs b/Presence.Desktop/Models/UserPresenter.cs deleted file mode 100644 index 3a8b4a8..0000000 --- a/Presence.Desktop/Models/UserPresenter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Presence.Desktop.Models -{ - public class UserPresenter - { - public Guid Guid { get; set; } - public string Name { get; set; } - public GroupPresenter Group { get; set; } - } -} \ No newline at end of file diff --git a/Presence.Desktop/Presence - Backup.Desktop.csproj b/Presence.Desktop/Presence - Backup.Desktop.csproj new file mode 100644 index 0000000..a85ddd0 --- /dev/null +++ b/Presence.Desktop/Presence - Backup.Desktop.csproj @@ -0,0 +1,37 @@ + + + WinExe + net8.0 + enable + true + app.manifest + true + + + + + + + + + + + + + + None + All + + + + + + + + + + + + + + diff --git a/Presence.Desktop/ViewLocator.cs b/Presence.Desktop/ViewLocator.cs index 2da0eb6..ace58ef 100644 --- a/Presence.Desktop/ViewLocator.cs +++ b/Presence.Desktop/ViewLocator.cs @@ -7,13 +7,24 @@ using ReactiveUI; namespace Presence.Desktop { - public class ViewLocator : IViewLocator + public class ViewLocator : IDataTemplate { - public IViewFor? ResolveView(T? viewModel, string? contract = null) => viewModel switch + public Control Build(object data) { - GroupViewModel groupViewModel => new GroupView { DataContext = groupViewModel }, - PresenceViewModel presenceViewModel => new PresenceView { DataContext = presenceViewModel }, - _ => throw new ArgumentOutOfRangeException(nameof(viewModel)) - }; + var name = data.GetType().FullName!.Replace("ViewModel", "View"); + var type = Type.GetType(name); + + if (type != null) + { + return (Control)Activator.CreateInstance(type)!; + } + + return new TextBlock { Text = "Not Found: " + name }; + } + + public bool Match(object data) + { + return data is ViewModelBase; + } } } diff --git a/Presence.Desktop/ViewModels/GroupViewModel.cs b/Presence.Desktop/ViewModels/GroupViewModel.cs index 3522b81..cd337e6 100644 --- a/Presence.Desktop/ViewModels/GroupViewModel.cs +++ b/Presence.Desktop/ViewModels/GroupViewModel.cs @@ -1,100 +1,292 @@ -using domain.UseCase; -using Presence.Desktop.Models; +using Avalonia.Controls; +using domain.Request; +using domain.UseCase; +using Presense.Desktop.Models; using ReactiveUI; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Linq; +using System.Reactive; using System.Reactive.Linq; +using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace Presence.Desktop.ViewModels { - public class GroupViewModel : ViewModelBase, IRoutableViewModel + public class GroupViewModel : ViewModelBase { - public ICommand OpenFileDialog { get; } - public Interaction SelectFileInteraction => _SelectFileInteraction; - public readonly Interaction _SelectFileInteraction; - private string? _selectedFile; - public string? SelectedFile - { - get => _selectedFile; - set => this.RaiseAndSetIfChanged(ref _selectedFile, value); - } + private readonly IGroupUseCase _groupService; - private readonly List _groupPresentersDataSource = new List(); + private readonly List _groupPresentersDataSource = new(); private ObservableCollection _groups; public ObservableCollection Groups => _groups; + private GroupPresenter? _selectedGroupItem; public GroupPresenter? SelectedGroupItem { get => _selectedGroupItem; - set => this.RaiseAndSetIfChanged(ref _selectedGroupItem, value); + set + { + if (_selectedGroupItem != value) + { + _selectedGroupItem = value; + this.RaisePropertyChanged(); + UpdateStudents(); + } + } } - private GroupPresenter? _selectedGroupItem; + private ObservableCollection _students; + public ObservableCollection Students => _students; - - private IGroupUseCase _groupUseCase; - public ObservableCollection Users { get => _users; } - public ObservableCollection _users; + public GroupViewModel() : this(null!) { } public GroupViewModel(IGroupUseCase groupUseCase) { - _groupUseCase = groupUseCase; - _SelectFileInteraction = new Interaction(); - OpenFileDialog = ReactiveCommand.CreateFromTask(SelectFile); - _users = new ObservableCollection(); - RefreshGroups(); - this.WhenAnyValue(vm => vm.SelectedGroupItem) - .Subscribe(_ => - { - RefreshGroups(); - SetUsers(); - }); + _groupService = groupUseCase; + + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + ImportStudentsCommand = ReactiveCommand.CreateFromTask(ImportStudentsAsync); + + LoadGroupsAndStudents(); + _students = new ObservableCollection(); + RemoveAllStudentsCommand = ReactiveCommand.Create(RemoveAllStudents); + + RemoveSelectedStudentsCommand = ReactiveCommand.Create(RemoveSelectedStudents); } - private void SetUsers() + private void LoadGroupsAndStudents() { - if (SelectedGroupItem == null) return; - Users.Clear(); - var group = _groups.First(it => it.Id == SelectedGroupItem.Id); - if (group.users == null) return; - foreach (var item in group.users) + foreach (var groupEntity in _groupService.GetGroupsWithStudents()) { - Users.Add(item); - } - } - - private void RefreshGroups() - { - _groupPresentersDataSource.Clear(); - foreach (var item in _groupUseCase.GetGroupsWithStudents()) - { - GroupPresenter groupPresenter = new GroupPresenter + var groupPresenter = new GroupPresenter { - Id = item.Id, - Name = item.Name, - users = item.Users?.Select(user => new UserPresenter + GroupId = groupEntity.GroupId, + GroupName = groupEntity.GroupName, + Students = groupEntity.Students?.Select(student => new StudentPresenter { - Name = user.Name, - Guid = user.Guid, - Group = new GroupPresenter { Id = item.Id, Name = item.Name } - } - ).ToList() + Id = student.Id, + FirstName = student.FirstName, + LastName = student.LastName, + Patronymic = student.Patronymic, + + Group = null + }).ToList(), + Subjects = groupEntity.Subjects.Select(subject => new SubjectPresenter + { + SubjectId = subject.SubjectId, + SubjectName = subject.SubjectName + }).ToList() }; + + foreach (var student in groupPresenter.Students ?? Enumerable.Empty()) + { + student.Group = groupPresenter; + } + _groupPresentersDataSource.Add(groupPresenter); } + _groups = new ObservableCollection(_groupPresentersDataSource); } - private async Task SelectFile() + private void UpdateStudents() { - Console.WriteLine("clock"); - SelectedFile = await _SelectFileInteraction.Handle("Chose csv file"); + if (SelectedGroupItem?.Students == null) return; + + Students.Clear(); + + foreach (var student in SelectedGroupItem.Students) + { + Students.Add(student); + } + } + + private readonly string[] _sortingOptions = new[] + { + "Без сортировки", + "Фамилии (по возрастанию)", + "Фамилии (по убыванию)" + }; + + public string[] SortingOptions => _sortingOptions; + + private string? _selectedSortingOption; + public string? SelectedSortingOption + { + get => _selectedSortingOption; + set + { + this.RaiseAndSetIfChanged(ref _selectedSortingOption, value); + ApplySorting(); + } + } + + private void ApplySorting() + { + if (SelectedGroupItem?.Students == null) return; + + IEnumerable sortedStudents = SelectedSortingOption switch + { + "Фамилии (по возрастанию)" => SelectedGroupItem.Students.OrderBy(s => s.LastName), + "Фамилии (по убыванию)" => SelectedGroupItem.Students.OrderByDescending(s => s.LastName), + _ => SelectedGroupItem.Students + }; + + Students.Clear(); + foreach (var student in sortedStudents) + { + Students.Add(student); + } + } + + public ReactiveCommand RemoveAllStudentsCommand { get; } + + private void RemoveAllStudents() + { + if (SelectedGroupItem == null || SelectedGroupItem.Students == null) return; + + _groupService.RemoveStudentsFromGroup(SelectedGroupItem.GroupId); + + SelectedGroupItem.Students.Clear(); + UpdateStudents(); + LoadGroupsAndStudents(); + } + + public ReactiveCommand ImportStudentsCommand { get; } + + public async Task ImportStudentsAsync() + { + var dialog = new OpenFileDialog + { + Title = "Выберите файл CSV:", + Filters = new List + { + new FileDialogFilter { Name = "CSV Files", Extensions = { "csv" } } + } + }; + + var result = await dialog.ShowAsync(new Window()); + + if (result != null && result.Any()) + { + var filePath = result.First(); + + if (SelectedGroupItem == null) + { + Console.WriteLine("Группа не выбрана."); + return; + } + + try + { + var csvLines = File.ReadAllLines(filePath, Encoding.GetEncoding("windows-1251")); + + var studentsToAdd = new List(); + + foreach (var line in csvLines) + { + var data = line.Split(';'); + if (data.Length != 3) + { + Console.WriteLine($"Либо разделитель либо хз: {line}"); + continue; + } + + studentsToAdd.Add(new AddStudentRequest + { + FirstName = data[0], + LastName = data[1], + Patronymic = data[2] + }); + } + + if (studentsToAdd.Any()) + { + _groupService.AddStudentsToGroup(SelectedGroupItem.GroupId, studentsToAdd); + + foreach (var studentRequest in studentsToAdd) + { + var studentPresenter = new StudentPresenter + { + FirstName = studentRequest.FirstName, + LastName = studentRequest.LastName, + Patronymic = studentRequest.Patronymic, + Group = SelectedGroupItem + }; + + SelectedGroupItem.Students.Add(studentPresenter); + Students.Add(studentPresenter); + } + + UpdateStudents(); + LoadGroupsAndStudents(); + } + else + { + Console.WriteLine("Проблема с данными."); + } + } + catch (Exception ex) + { + Console.WriteLine($"...: {ex.Message}"); + } + } + } + + + private ObservableCollection _selectedStudents = new(); + public ObservableCollection SelectedStudents + { + get => _selectedStudents; + set + { + this.RaiseAndSetIfChanged(ref _selectedStudents, value); + UpdateSelectionStates(); + } + } + + private bool _isSingleSelection; + public bool IsSingleSelection + { + get => _isSingleSelection; + set => this.RaiseAndSetIfChanged(ref _isSingleSelection, value); + } + + private bool _isMultipleSelection; + public bool IsMultipleSelection + { + get => _isMultipleSelection; + set => this.RaiseAndSetIfChanged(ref _isMultipleSelection, value); + } + + public void UpdateSelectionStates() + { + IsSingleSelection = SelectedStudents.Count == 1; + IsMultipleSelection = SelectedStudents.Count > 1; + } + + public ReactiveCommand RemoveSelectedStudentsCommand { get; } + + private void RemoveSelectedStudents() + { + if (SelectedStudents == null || SelectedStudents.Count == 0) return; + + var studentIds = SelectedStudents.Select(s => s.Id).ToList(); + _groupService.RemoveStudentsFromGroupByIds(SelectedGroupItem.GroupId, studentIds); + + foreach (var student in SelectedStudents.ToList()) + { + SelectedGroupItem.Students.Remove(student); + Students.Remove(student); + } + + UpdateStudents(); + LoadGroupsAndStudents(); } - public string? UrlPathSegment { get; } - public IScreen HostScreen { get; } } } diff --git a/Presence.Desktop/ViewModels/MainWindowViewModel.cs b/Presence.Desktop/ViewModels/MainWindowViewModel.cs deleted file mode 100644 index 8449f0d..0000000 --- a/Presence.Desktop/ViewModels/MainWindowViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using ReactiveUI; - -namespace Presence.Desktop.ViewModels; - -public class MainWindowViewModel : ViewModelBase, IScreen -{ - public RoutingState Router { get; } = new RoutingState(); - - public MainWindowViewModel(IServiceProvider serviceProvider) - { - var groupViewModel = serviceProvider.GetRequiredService(); - Router.Navigate.Execute(groupViewModel); - } -} \ No newline at end of file diff --git a/Presence.Desktop/Views/GroupView.axaml b/Presence.Desktop/Views/GroupView.axaml index d1aed66..c0590e9 100644 --- a/Presence.Desktop/Views/GroupView.axaml +++ b/Presence.Desktop/Views/GroupView.axaml @@ -1,4 +1,4 @@ - + - - - - - - - + + + + + + + - + -