From 04b9f8b74deae88fdf7aa3e465458ed19941abd6 Mon Sep 17 00:00:00 2001 From: Userok Date: Thu, 14 Nov 2024 10:10:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D1=8C=D1=82?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Demo.sln | 25 ++ .../Data/Exceptions/GroupNotFoundException.cs | 10 + .../Exceptions/PresenceNotFoundException.cs | 11 + Demo/Data/Exceptions/RepositoryException.cs | 9 + Demo/Data/Exceptions/UserNotFoundException.cs | 10 + Demo/Data/LocalData/Entity/Group.cs | 17 ++ Demo/Data/LocalData/Entity/Presence.cs | 18 ++ Demo/Data/LocalData/Entity/User.cs | 25 ++ Demo/Data/LocalData/LocalStaticData.cs | 36 +++ .../RemoteDataBase/DAO/AttendanceRecord.cs | 19 ++ .../RemoteData/RemoteDataBase/DAO/Group.cs | 16 ++ .../DAO/GroupAttendanceStatistics.cs | 16 ++ .../RemoteData/RemoteDataBase/DAO/Presence.cs | 18 ++ .../RemoteData/RemoteDataBase/DAO/User.cs | 16 ++ .../RemoteDataBase/DAO/UserAttendance.cs | 16 ++ .../RemoteDataBase/RemoteDatabase.cs | 12 + .../RemoteDataBase/RemoteDatabaseContext.cs | 35 +++ Demo/Data/Repository/GroupRepositoryImpl.cs | 60 +++++ Demo/Data/Repository/IGroupRepository.cs | 18 ++ Demo/Data/Repository/IPresenceRepository.cs | 27 +++ Demo/Data/Repository/IUserRepository.cs | 14 ++ .../Data/Repository/PresenceRepositoryImpl.cs | 53 +++++ .../Data/Repository/SQLGroupRepositoryImpl.cs | 116 +++++++++ Demo/Data/Repository/SQLPresenceRepository.cs | 220 +++++++++++++++++ Demo/Data/Repository/SQLUserRepositoryImpl.cs | 61 +++++ Demo/Data/Repository/UserRepositoryImpl.cs | 37 +++ Demo/Demo.csproj | 27 +++ Demo/Domain/Models/Group.cs | 14 ++ Demo/Domain/Models/Presence.cs | 18 ++ Demo/Domain/Models/User.cs | 15 ++ Demo/Domain/UseCase/GroupUseCase.cs | 88 +++++++ .../Domain/UseCase/UseCaseGeneratePresence.cs | 192 +++++++++++++++ Demo/Domain/UseCase/UserUseCase.cs | 146 ++++++++++++ .../20241103105727_CreateDatabase.Designer.cs | 113 +++++++++ .../20241103105727_CreateDatabase.cs | 84 +++++++ .../RemoteDatabaseContextModelSnapshot.cs | 110 +++++++++ Demo/Program.cs | 28 +++ Demo/Reports/AttendanceReport.xlsx | Bin 0 -> 10544 bytes Demo/UI/GroupConsole.cs | 58 +++++ Demo/UI/MainMenu.cs | 224 ++++++++++++++++++ Demo/UI/PresenceConsole.cs | 188 +++++++++++++++ Demo/UI/UserConsole.cs | 81 +++++++ presence.sln | 22 ++ presence/Class1.cs | 7 + presence/presence.csproj | 9 + 45 files changed, 2339 insertions(+) create mode 100644 Demo.sln create mode 100644 Demo/Data/Exceptions/GroupNotFoundException.cs create mode 100644 Demo/Data/Exceptions/PresenceNotFoundException.cs create mode 100644 Demo/Data/Exceptions/RepositoryException.cs create mode 100644 Demo/Data/Exceptions/UserNotFoundException.cs create mode 100644 Demo/Data/LocalData/Entity/Group.cs create mode 100644 Demo/Data/LocalData/Entity/Presence.cs create mode 100644 Demo/Data/LocalData/Entity/User.cs create mode 100644 Demo/Data/LocalData/LocalStaticData.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/DAO/AttendanceRecord.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/DAO/Group.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/DAO/GroupAttendanceStatistics.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/DAO/Presence.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/DAO/User.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/DAO/UserAttendance.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/RemoteDatabase.cs create mode 100644 Demo/Data/RemoteData/RemoteDataBase/RemoteDatabaseContext.cs create mode 100644 Demo/Data/Repository/GroupRepositoryImpl.cs create mode 100644 Demo/Data/Repository/IGroupRepository.cs create mode 100644 Demo/Data/Repository/IPresenceRepository.cs create mode 100644 Demo/Data/Repository/IUserRepository.cs create mode 100644 Demo/Data/Repository/PresenceRepositoryImpl.cs create mode 100644 Demo/Data/Repository/SQLGroupRepositoryImpl.cs create mode 100644 Demo/Data/Repository/SQLPresenceRepository.cs create mode 100644 Demo/Data/Repository/SQLUserRepositoryImpl.cs create mode 100644 Demo/Data/Repository/UserRepositoryImpl.cs create mode 100644 Demo/Demo.csproj create mode 100644 Demo/Domain/Models/Group.cs create mode 100644 Demo/Domain/Models/Presence.cs create mode 100644 Demo/Domain/Models/User.cs create mode 100644 Demo/Domain/UseCase/GroupUseCase.cs create mode 100644 Demo/Domain/UseCase/UseCaseGeneratePresence.cs create mode 100644 Demo/Domain/UseCase/UserUseCase.cs create mode 100644 Demo/Migrations/20241103105727_CreateDatabase.Designer.cs create mode 100644 Demo/Migrations/20241103105727_CreateDatabase.cs create mode 100644 Demo/Migrations/RemoteDatabaseContextModelSnapshot.cs create mode 100644 Demo/Program.cs create mode 100644 Demo/Reports/AttendanceReport.xlsx create mode 100644 Demo/UI/GroupConsole.cs create mode 100644 Demo/UI/MainMenu.cs create mode 100644 Demo/UI/PresenceConsole.cs create mode 100644 Demo/UI/UserConsole.cs create mode 100644 presence.sln create mode 100644 presence/Class1.cs create mode 100644 presence/presence.csproj diff --git a/Demo.sln b/Demo.sln new file mode 100644 index 0000000..ce1f93c --- /dev/null +++ b/Demo.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35312.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{983820F6-FF31-4B3A-8593-831BC3904E80}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {983820F6-FF31-4B3A-8593-831BC3904E80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {983820F6-FF31-4B3A-8593-831BC3904E80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {983820F6-FF31-4B3A-8593-831BC3904E80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {983820F6-FF31-4B3A-8593-831BC3904E80}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4F43A963-447C-4FCB-BB78-8D315EC0F1D6} + EndGlobalSection +EndGlobal diff --git a/Demo/Data/Exceptions/GroupNotFoundException.cs b/Demo/Data/Exceptions/GroupNotFoundException.cs new file mode 100644 index 0000000..de2876b --- /dev/null +++ b/Demo/Data/Exceptions/GroupNotFoundException.cs @@ -0,0 +1,10 @@ +using System; + +namespace Demo.Data.Exceptions +{ + public class GroupNotFoundException : RepositoryException + { + public GroupNotFoundException(int userId) + : base($"Группа с ID {userId} не найдена.") { } + } +} \ No newline at end of file diff --git a/Demo/Data/Exceptions/PresenceNotFoundException.cs b/Demo/Data/Exceptions/PresenceNotFoundException.cs new file mode 100644 index 0000000..39ab821 --- /dev/null +++ b/Demo/Data/Exceptions/PresenceNotFoundException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Demo.Data.Exceptions +{ + public class PresenceNotFoundException : RepositoryException + { + public PresenceNotFoundException(int userId, DateOnly date, int firstLesson, int lastLesson) + : base($"Посещаемость для пользователя ID: {userId} на дату {date.ToShortDateString()}" + + $" с {firstLesson} по {lastLesson} уроки не найдена.") { } + } +} \ No newline at end of file diff --git a/Demo/Data/Exceptions/RepositoryException.cs b/Demo/Data/Exceptions/RepositoryException.cs new file mode 100644 index 0000000..5732bc4 --- /dev/null +++ b/Demo/Data/Exceptions/RepositoryException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Demo.Data.Exceptions +{ + public class RepositoryException : Exception + { + public RepositoryException(string message) : base(message) { } + } +} \ No newline at end of file diff --git a/Demo/Data/Exceptions/UserNotFoundException.cs b/Demo/Data/Exceptions/UserNotFoundException.cs new file mode 100644 index 0000000..c05fdea --- /dev/null +++ b/Demo/Data/Exceptions/UserNotFoundException.cs @@ -0,0 +1,10 @@ +using System; + +namespace Demo.Data.Exceptions +{ + public class UserNotFoundException : RepositoryException + { + public UserNotFoundException(int userId) + : base($"Пользователь с ID {userId} не найден.") { } + } +} \ No newline at end of file diff --git a/Demo/Data/LocalData/Entity/Group.cs b/Demo/Data/LocalData/Entity/Group.cs new file mode 100644 index 0000000..506833b --- /dev/null +++ b/Demo/Data/LocalData/Entity/Group.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.domain.Models +{ + public class GroupLocalEntity + { + public int Id { get; set; } + public required string Name { get; set; } + + + + } +} diff --git a/Demo/Data/LocalData/Entity/Presence.cs b/Demo/Data/LocalData/Entity/Presence.cs new file mode 100644 index 0000000..4c3a1c6 --- /dev/null +++ b/Demo/Data/LocalData/Entity/Presence.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.domain.Models +{ + public class PresenceLocalEntity + { + public required int UserId { get; set; } + public required int GroupId { get; set; } + public bool IsAttedance { get; set; } = true; + public required DateTime Date { get; set; } + + public required int LessonNumber { get; set; } + } +} diff --git a/Demo/Data/LocalData/Entity/User.cs b/Demo/Data/LocalData/Entity/User.cs new file mode 100644 index 0000000..ce8aebd --- /dev/null +++ b/Demo/Data/LocalData/Entity/User.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.domain.Models +{ + public class UserLocalEnity : IEquatable + { + + public required string FIO { get; set; } + public int ID { get; set; } + + public required int GroupID { get; set; } + + + + public bool Equals(UserLocalEnity? other) + { + if (other == null) return false; + return this.ID.Equals(other.ID); + } + } +} diff --git a/Demo/Data/LocalData/LocalStaticData.cs b/Demo/Data/LocalData/LocalStaticData.cs new file mode 100644 index 0000000..a1c741d --- /dev/null +++ b/Demo/Data/LocalData/LocalStaticData.cs @@ -0,0 +1,36 @@ +using Demo.domain.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.LocalData +{ + public static class LocalStaticData + { + public static List groups => new List + + { + new GroupLocalEntity{ Id = 1, Name = "ИП1-21" }, + new GroupLocalEntity{ Id = 2, Name = "ИП1-22" }, + new GroupLocalEntity{ Id = 3, Name = "ИП1-23" }, + }; + + public static List users => new List + { + new UserLocalEnity{ID = 1, FIO = "RandomFio", GroupID = 1 }, + new UserLocalEnity{ID = 2, FIO = "RandomFio1", GroupID = 2 }, + new UserLocalEnity{ID = 3, FIO = "RandomFio2", GroupID = 3 }, + new UserLocalEnity{ID = 4, FIO = "RandomFio3", GroupID = 1 }, + new UserLocalEnity{ID = 5, FIO = "RandomFio4", GroupID = 2 }, + new UserLocalEnity{ID = 6, FIO = "RandomFio5", GroupID = 3 }, + }; + + public static List presences => new List + { + + }; + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/DAO/AttendanceRecord.cs b/Demo/Data/RemoteData/RemoteDataBase/DAO/AttendanceRecord.cs new file mode 100644 index 0000000..76a10ee --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/DAO/AttendanceRecord.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase.DAO +{ + public class AttendanceRecord + { + public int UserId { get; set; } + public string UserName { get; set; } + public string FullName { get; set; } + public DateOnly Date { get; set; } + public bool IsAttedance { get; set; } + public int LessonNumber { get; set; } + public string GroupName { get; set; } + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/DAO/Group.cs b/Demo/Data/RemoteData/RemoteDataBase/DAO/Group.cs new file mode 100644 index 0000000..33b8206 --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/DAO/Group.cs @@ -0,0 +1,16 @@ +using Demo.domain.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase.DAO +{ + public class GroupDao + { + public int Id { get; set; } + public string Name { get; set; } + public virtual List Users { get; set; } + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/DAO/GroupAttendanceStatistics.cs b/Demo/Data/RemoteData/RemoteDataBase/DAO/GroupAttendanceStatistics.cs new file mode 100644 index 0000000..93e39ab --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/DAO/GroupAttendanceStatistics.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase.DAO +{ + public class GroupAttendanceStatistics + { + public int UserCount { get; set; } + public int TotalLessons { get; set; } + public double AttendancePercentage { get; set; } + public List UserAttendanceDetails { get; set; } = new List(); + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/DAO/Presence.cs b/Demo/Data/RemoteData/RemoteDataBase/DAO/Presence.cs new file mode 100644 index 0000000..fbb977f --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/DAO/Presence.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase.DAO +{ + public class PresenceDao + { + public int PresenceId { get; set; } + public int UserId { get; set; } + public bool IsAttedance { get; set; } = true; + public DateOnly Date { get; set; } + public int LessonNumber { get; set; } + public int GroupId { get; set; } + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/DAO/User.cs b/Demo/Data/RemoteData/RemoteDataBase/DAO/User.cs new file mode 100644 index 0000000..bdf3616 --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/DAO/User.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase.DAO +{ + public class UserDao + { + public required string FIO { get; set; } + public required int UserId { get; set; } + public int GroupId { get; set; } + public GroupDao? Group { get; set; } + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/DAO/UserAttendance.cs b/Demo/Data/RemoteData/RemoteDataBase/DAO/UserAttendance.cs new file mode 100644 index 0000000..b26f69c --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/DAO/UserAttendance.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase.DAO +{ + public class UserAttendance + { + public int UserId { get; set; } + public double Attended { get; set; } + public double Missed { get; set; } + public double AttendanceRate { get; set; } + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/RemoteDatabase.cs b/Demo/Data/RemoteData/RemoteDataBase/RemoteDatabase.cs new file mode 100644 index 0000000..546b626 --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/RemoteDatabase.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase +{ + internal class RemoteDatabase + { + } +} diff --git a/Demo/Data/RemoteData/RemoteDataBase/RemoteDatabaseContext.cs b/Demo/Data/RemoteData/RemoteDataBase/RemoteDatabaseContext.cs new file mode 100644 index 0000000..31255d9 --- /dev/null +++ b/Demo/Data/RemoteData/RemoteDataBase/RemoteDatabaseContext.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Demo.Data.RemoteData.RemoteDataBase +{ + public class RemoteDatabaseContext: DbContext + { + public DbSet Groups { get; set; } + public DbSet Users { get; set; } + public DbSet PresenceDaos { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Database=presencedb;Username=postgres;Password=123;Include Error Detail=True;"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasKey(group=>group.Id); + modelBuilder.Entity().Property(group => group.Id).ValueGeneratedOnAdd(); + modelBuilder.Entity().HasKey(user=>user.UserId); + modelBuilder.Entity().Property(user=>user.UserId).ValueGeneratedOnAdd(); + modelBuilder.Entity().HasKey(presence =>presence.PresenceId); + modelBuilder.Entity().Property(presence=>presence.PresenceId).ValueGeneratedOnAdd(); + + } + } +} diff --git a/Demo/Data/Repository/GroupRepositoryImpl.cs b/Demo/Data/Repository/GroupRepositoryImpl.cs new file mode 100644 index 0000000..5f5a7fb --- /dev/null +++ b/Demo/Data/Repository/GroupRepositoryImpl.cs @@ -0,0 +1,60 @@ +using Demo.Data.Exceptions; +using Demo.Data.LocalData; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.Data.Repository; +using Demo.domain.Models; +using System.Collections.Generic; +using System.Linq; + +public class GroupRepositoryImpl +{ + private List _groups = LocalStaticData.groups; + + + public GroupLocalEntity? GetGroupById(int groupId) + { + foreach (var group in _groups) + { + if (group.Id == groupId) + { + return group; + } + } + return null; + } + + + + + // Метод для получения всех групп + public List GetAllGroups() => _groups; + + // Метод для добавления новой группы + public void AddGroup(GroupLocalEntity group) + { + group.Id = _groups.Any() ? _groups.Max(g => g.Id) + 1 : 1; + _groups.Add(group); + } + + // Метод для обновления существующей группы + public void UpdateGroupById(int groupId, GroupLocalEntity updatedGroup) + { + var existingGroup = GetGroupById(groupId); + if (existingGroup == null) throw new GroupNotFoundException(groupId); + } + + public void RemoveGroupById(int groupId) + { + var existingGroup = GetGroupById(groupId); + if (existingGroup == null) throw new GroupNotFoundException(groupId); + if (_groups.Contains(existingGroup)) + { + _groups.Remove(existingGroup); + } + } + + public bool AddGroup(string Name) + { + throw new NotImplementedException(); + } +} diff --git a/Demo/Data/Repository/IGroupRepository.cs b/Demo/Data/Repository/IGroupRepository.cs new file mode 100644 index 0000000..d5f20ef --- /dev/null +++ b/Demo/Data/Repository/IGroupRepository.cs @@ -0,0 +1,18 @@ +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.Repository +{ + public interface IGroupRepository + { + List GetAllGroups(); + bool UpdateGroupById(int groupID, GroupDao updatedGroup); + GroupDao GetGroupById(int groupID); + bool AddGroup(string Name); + } +} \ No newline at end of file diff --git a/Demo/Data/Repository/IPresenceRepository.cs b/Demo/Data/Repository/IPresenceRepository.cs new file mode 100644 index 0000000..51ff137 --- /dev/null +++ b/Demo/Data/Repository/IPresenceRepository.cs @@ -0,0 +1,27 @@ + +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using Demo.Domain.UseCase; +using Microsoft.EntityFrameworkCore.Metadata; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Data.Repository +{ + public interface IPresenceRepository + { + List GetPresenceByDateAndGroup(DateTime date, int groupId); + void SavePresence(List presences); + List GetPresenceByGroup(int groupId); + DateOnly? GetLastDateByGroupId(int groupId); + List GetPresenceForAbsent(DateTime date, int GroupId); + GroupAttendanceStatistics GetGeneralPresenceForGroup(int groupId); + void UpdateAtt(int userId, int groupId, int firstLesson, int lastLesson, DateOnly date, bool isAttendance); + List GetAttendanceByGroup(int groupId); + } +} + diff --git a/Demo/Data/Repository/IUserRepository.cs b/Demo/Data/Repository/IUserRepository.cs new file mode 100644 index 0000000..7550e04 --- /dev/null +++ b/Demo/Data/Repository/IUserRepository.cs @@ -0,0 +1,14 @@ +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using System.Collections.Generic; + +namespace Demo.Data.Repository +{ + public interface IUserRepository + { + List GetAllUsers(); + bool RemoveUserById(int userId); + UserDao UpdateUser(int userId, string newFIO, int groupId); + List GetUserNames(); + } +} diff --git a/Demo/Data/Repository/PresenceRepositoryImpl.cs b/Demo/Data/Repository/PresenceRepositoryImpl.cs new file mode 100644 index 0000000..0f0b8df --- /dev/null +++ b/Demo/Data/Repository/PresenceRepositoryImpl.cs @@ -0,0 +1,53 @@ +using Demo.Data.LocalData; +using Demo.Data.RemoteData.RemoteDataBase; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Demo.Data.Repository +{ + public class PresenceRepositoryImpl + { + private List _presences; + + public PresenceRepositoryImpl() + { + _presences = new List(); // Ваши реальные данные + } + + public void SavePresence(List presences) + { + foreach (var presence in presences) + { + var existingPresence = _presences.FirstOrDefault(p => + p.Date == presence.Date && + p.UserId == presence.UserId && + p.LessonNumber == presence.LessonNumber); + + if (existingPresence == null) + { + _presences.Add(presence); + } + else + { + existingPresence.IsAttedance = presence.IsAttedance; + } + } + } + + public List GetPresenceByDateAndGroup(DateTime date, int groupId) + { + return _presences.Where(p => p.Date.Date == date.Date && + LocalStaticData.users.Any(u => u.GroupID == groupId && u.ID == p.UserId)).ToList(); + } + + public List GetPresenceByGroup(int groupId) + { + return _presences.Where(p => p.GroupId == groupId).ToList(); + } + + + } +} diff --git a/Demo/Data/Repository/SQLGroupRepositoryImpl.cs b/Demo/Data/Repository/SQLGroupRepositoryImpl.cs new file mode 100644 index 0000000..d8de132 --- /dev/null +++ b/Demo/Data/Repository/SQLGroupRepositoryImpl.cs @@ -0,0 +1,116 @@ +using Demo.Data.RemoteData.RemoteDataBase; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Demo.Data.Repository +{ + public class SQLGroupRepositoryImpl : IGroupRepository + { + private readonly RemoteDatabaseContext _remoteDatabaseContext; + + public SQLGroupRepositoryImpl(RemoteDatabaseContext remoteDatabaseContext) + { + _remoteDatabaseContext = remoteDatabaseContext; + } + + // Метод для добавления новой группы + public bool AddGroup(GroupDao newGroup) + { + var groupDao = new GroupDao + { + Name = newGroup.Name + }; + _remoteDatabaseContext.Groups.Add(groupDao); + _remoteDatabaseContext.SaveChanges(); + return true; + } + + // Метод для получения группы по ID + public GroupDao GetGroupById(int groupId) + { + var groupDao = _remoteDatabaseContext.Groups + .Include(g => g.Users) + .FirstOrDefault(g => g.Id == groupId); + if (groupDao == null) return null; + + return new GroupDao + { + Id = groupDao.Id, + Name = groupDao.Name, + Users = groupDao.Users.Select(u => new UserDao + { + UserId = u.UserId, + FIO = u.FIO, + GroupId = u.GroupId + }).ToList() + }; + } + + // Метод для получения всех групп + public List GetAllGroups() + { + return _remoteDatabaseContext.Groups + .Include(g => g.Users) + .Select(g => new GroupDao + { + Id = g.Id, + Name = g.Name, + Users = g.Users.Select(u => new UserDao + { + UserId = u.UserId, + FIO = u.FIO, + GroupId = u.GroupId + }).ToList() + }) + .ToList(); + } + + // Метод для обновления группы по ID + public bool UpdateGroupById(int groupId, GroupDao updatedGroup) + { + var groupDao = _remoteDatabaseContext.Groups + .Include(g => g.Users) + .FirstOrDefault(g => g.Id == groupId); + if (groupDao == null) return false; + + groupDao.Name = updatedGroup.Name; + // Пример обновления списка пользователей + groupDao.Users = updatedGroup.Users.Select(user => new UserDao + { + UserId = user.UserId, + FIO = user.FIO, + GroupId = user.GroupId + }).ToList(); + + _remoteDatabaseContext.SaveChanges(); + return true; + } + + // Метод для удаления группы по ID + public bool RemoveGroupById(int groupId) + { + var groupDao = _remoteDatabaseContext.Groups.Find(groupId); + if (groupDao == null) return false; + + _remoteDatabaseContext.Groups.Remove(groupDao); + _remoteDatabaseContext.SaveChanges(); + return true; + } + + public bool AddGroup(string Name) + { + + var groupDao = new GroupDao + { + Name = Name + }; + _remoteDatabaseContext.Groups.Add(groupDao); + _remoteDatabaseContext.SaveChanges(); + return true; + } + } +} diff --git a/Demo/Data/Repository/SQLPresenceRepository.cs b/Demo/Data/Repository/SQLPresenceRepository.cs new file mode 100644 index 0000000..e70bfc0 --- /dev/null +++ b/Demo/Data/Repository/SQLPresenceRepository.cs @@ -0,0 +1,220 @@ +using Demo.Data.Exceptions; +using Demo.Data.LocalData; +using Demo.Data.RemoteData.RemoteDataBase; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using DocumentFormat.OpenXml.InkML; +using DocumentFormat.OpenXml.Spreadsheet; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Demo.Data.Repository +{ + public class SQLPresenceRepositoryImpl : IPresenceRepository + { + private readonly RemoteDatabaseContext _remoteDatabaseContext; + + public SQLPresenceRepositoryImpl(RemoteDatabaseContext remoteDatabaseContext) + { + _remoteDatabaseContext = remoteDatabaseContext; + } + + + public List GetAttendanceByGroup(int groupId) + { + // Получаем записи посещаемости для указанной группы + return _remoteDatabaseContext.PresenceDaos + .Where(p => p.GroupId == groupId) + .Select(p => new PresenceDao + { + UserId = p.UserId, + GroupId = p.GroupId, + Date = p.Date, + LessonNumber = p.LessonNumber, + IsAttedance = p.IsAttedance + }) + .ToList(); + } + + + public List GetPresenceForAbsent(DateTime date, int GroupId) + { + return _remoteDatabaseContext.PresenceDaos.Where(p => p.GroupId == GroupId && p.Date == DateOnly.FromDateTime(date)).ToList(); + } + public List GetPresenceByDateAndGroup(DateTime date, int groupId) + { + return _remoteDatabaseContext.PresenceDaos.Where(p => p.Date == DateOnly.FromDateTime(date) && + _remoteDatabaseContext.Users.Any(u => u.GroupId == groupId && u.UserId == p.UserId)).ToList(); + } + + // Реализация метода для получения всех данных по группе + public List GetPresenceByGroup(int groupId) + { + return _remoteDatabaseContext.PresenceDaos.Where(p => p.GroupId == groupId) + .OrderBy(p => p.Date) + .ThenBy(p=>p.UserId).ToList(); + } + + public void SavePresence(List presences) + { + _remoteDatabaseContext.PresenceDaos.AddRange(presences.Select(it => new PresenceDao + { + Date = it.Date, + IsAttedance = it.IsAttedance, + LessonNumber = it.LessonNumber, + UserId = it.UserId, + GroupId = it.GroupId + })); + _remoteDatabaseContext.SaveChanges(); + } + + public void UpdateAtt(int userId, int groupId, int firstLesson, int lastLesson, DateOnly date, bool isAttendance) + { + // Находим все записи по UserId, GroupId, LessonNumber (в диапазоне) и дате + var presences = _remoteDatabaseContext.PresenceDaos + .Where(p => p.UserId == userId + && p.GroupId == groupId + && p.LessonNumber >= firstLesson + && p.LessonNumber <= lastLesson + && p.Date == date) + .ToList(); + + // Обновляем значение IsAttendance для всех найденных записей + foreach (var presence in presences) + { + presence.IsAttedance = isAttendance; + } + + _remoteDatabaseContext.SaveChanges(); // Сохраняем изменения в базе данных + } + + public DateOnly? GetLastDateByGroupId(int groupId) + { + // Проверяем наличие записей о посещаемости в базе данных для данной группы. + var lastDate = _remoteDatabaseContext.PresenceDaos + .Where(p => p.GroupId == groupId) + .OrderByDescending(p => p.Date) + .Select(p => p.Date) + .FirstOrDefault(); + + return lastDate == default ? (DateOnly?)null : lastDate; + } + + public List PresenceSort(List presences) + { + presences=_remoteDatabaseContext.PresenceDaos.OrderBy(p=>p.Date).ToList(); + return presences; + } + + + public GroupAttendanceStatistics GetGeneralPresenceForGroup(int groupId) + { + var presences = _remoteDatabaseContext.PresenceDaos.Where(p => p.GroupId == groupId).OrderBy(p => p.LessonNumber).ToList(); + var dates = _remoteDatabaseContext.PresenceDaos; + var distDates = dates.Select(p => p.Date).Distinct().ToList(); + int lesId = 0; + int lesNum = 1; + double att = 0; + int days = -1; + int countAllLes = 0; + DateOnly date = DateOnly.MinValue; + List usersId = new List(); + + foreach (var presence in presences) + { + if (!usersId.Contains(presence.UserId)) + { + usersId.Add(presence.UserId); + } + if (presence.Date != date) + { + date = presence.Date; + lesId++; + lesNum = presence.LessonNumber; + days++; + } + if (presence.LessonNumber != lesNum && date == presence.Date) + { + lesNum = presence.LessonNumber; + countAllLes++; + lesId++; + } + + + if (presence.IsAttedance) + { + att++; + } + + } + List a = new List(); + List ids = new List(); + double ok = 0; + double skip = 0; + int userId = 0; + foreach (var user in usersId) + { + var users = _remoteDatabaseContext.PresenceDaos.Where(p => p.UserId == user); + foreach (var usera in users) + { + userId = usera.UserId; + if (!ids.Contains(usera.UserId)) + { + skip = 0; + ok = 0; + ids.Add(userId); + a.Add(new UserAttendance { UserId = userId, Attended = ok, Missed = skip }); + userId = usera.UserId; + if (usera.IsAttedance) + { + a.First(a => a.UserId == usera.UserId).Attended = ok += 1; + } + else + { + a.First(a => a.UserId == usera.UserId).Missed = skip += 1; + } + } + else + { + if (usera.IsAttedance) + { + a.First(a => a.UserId == usera.UserId).Attended = ok += 1; + } + else + { + a.First(a => a.UserId == usera.UserId).Missed = skip += 1; + } + } + } + } + + var statistics = new GroupAttendanceStatistics + { + UserCount = usersId.Count, + TotalLessons = lesId, + AttendancePercentage = att / usersId.Count / lesNum / distDates.Count() * 100 + }; + + foreach (var user in a) + { + statistics.UserAttendanceDetails.Add(new UserAttendance + { + UserId = user.UserId, + Attended = user.Attended, + Missed = user.Missed, + AttendanceRate = user.Attended / (user.Missed + user.Attended) * 100 + }); + } + + return statistics; + } + } +} diff --git a/Demo/Data/Repository/SQLUserRepositoryImpl.cs b/Demo/Data/Repository/SQLUserRepositoryImpl.cs new file mode 100644 index 0000000..b726895 --- /dev/null +++ b/Demo/Data/Repository/SQLUserRepositoryImpl.cs @@ -0,0 +1,61 @@ +using Demo.Data.Exceptions; +using Demo.Data.RemoteData.RemoteDataBase; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Demo.Data.Repository +{ + public class SQLUserRepositoryImpl : IUserRepository + { + private readonly RemoteDatabaseContext _remoteDatabaseContext; + + public SQLUserRepositoryImpl(RemoteDatabaseContext remoteDatabaseContext) + { + _remoteDatabaseContext = remoteDatabaseContext; + } + + public bool RemoveUserById(int userId) + { + var user = _remoteDatabaseContext.Users.FirstOrDefault(u => u.UserId == userId); + if (user == null) throw new UserNotFoundException(userId); + + _remoteDatabaseContext.Users.Remove(user); + _remoteDatabaseContext.SaveChanges(); + return true; + } + + public UserDao UpdateUser(int userId, string newFio, int groupId) + { + var existingUser = _remoteDatabaseContext.Users.FirstOrDefault(u => u.UserId == userId); + if (existingUser == null) throw new UserNotFoundException(userId); + + // Обновляем поля существующего пользователя + existingUser.FIO = newFio; + existingUser.GroupId = groupId; + _remoteDatabaseContext.SaveChanges(); + + return existingUser; + } + + public List GetAllUsers() + { + // Возвращаем пользователей, отсортированных по UserId + return _remoteDatabaseContext.Users.OrderBy(u => u.UserId).ToList(); + } + + public List GetUserNames() + { + var users = GetAllUsers(); + List names = new List(); + foreach (var user in users) + { + names.Add(new UserDao{UserId=user.UserId, FIO=user.FIO }); + } + return names; + } + } +} diff --git a/Demo/Data/Repository/UserRepositoryImpl.cs b/Demo/Data/Repository/UserRepositoryImpl.cs new file mode 100644 index 0000000..e1d6e40 --- /dev/null +++ b/Demo/Data/Repository/UserRepositoryImpl.cs @@ -0,0 +1,37 @@ +using Demo.Data.Exceptions; +using Demo.Data.LocalData; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.domain.Models; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Demo.Data.Repository +{ + public class UserRepositoryImpl + { + private List _users; + + public UserRepositoryImpl() + { + _users = LocalStaticData.users; + } + + public IEnumerable GetAllUsers => _users; + + + public bool RemoveUserById(int userId) + { + var user = _users.FirstOrDefault(u => u.ID == userId); + if (user == null) throw new UserNotFoundException(userId); + + _users.Remove(user); + return true; + } + + public UserDao? UpdateUser(UserDao user) + { + throw new NotImplementedException(); + } + } +} diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj new file mode 100644 index 0000000..760f09a --- /dev/null +++ b/Demo/Demo.csproj @@ -0,0 +1,27 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/Demo/Domain/Models/Group.cs b/Demo/Domain/Models/Group.cs new file mode 100644 index 0000000..ce0914b --- /dev/null +++ b/Demo/Domain/Models/Group.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.domain.Models +{ + public class Group + { + public required int Id { get; set; } + public required string Name { get; set; } + } +} diff --git a/Demo/Domain/Models/Presence.cs b/Demo/Domain/Models/Presence.cs new file mode 100644 index 0000000..110a364 --- /dev/null +++ b/Demo/Domain/Models/Presence.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.domain.Models +{ + public class Presence + { + + public required User User { get; set; } + public required int GroupId { get; set; } + public bool IsAttedance { get; set; } = true; + public required DateTime Date { get; set; } + public required int LessonNumber { get; set; } + } +} diff --git a/Demo/Domain/Models/User.cs b/Demo/Domain/Models/User.cs new file mode 100644 index 0000000..8e09028 --- /dev/null +++ b/Demo/Domain/Models/User.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.domain.Models +{ + public class User + { + public required string FIO { get; set; } + public int ID { get; set; } + public required Group Group { get; set; } + } +} diff --git a/Demo/Domain/UseCase/GroupUseCase.cs b/Demo/Domain/UseCase/GroupUseCase.cs new file mode 100644 index 0000000..0f3492b --- /dev/null +++ b/Demo/Domain/UseCase/GroupUseCase.cs @@ -0,0 +1,88 @@ +using Demo.Data.LocalData; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.Data.Repository; +using Demo.domain.Models; + +namespace Demo.Domain.UseCase +{ + public class GroupUseCase + { + private readonly IGroupRepository _SQLGroupRepositoryImpl; + + public GroupUseCase(IGroupRepository SQlGroupRepositoryImpl) + { + _SQLGroupRepositoryImpl = SQlGroupRepositoryImpl; + } + + // Приватный метод для валидации имени группы + private void ValidateGroupName(string groupName) + { + if (string.IsNullOrWhiteSpace(groupName)) + { + throw new ArgumentException("Имя группы не может быть пустым."); + } + } + + private void ValidateGroupId(int GroupId) + { + if(GroupId < 1) + { + throw new ArgumentException("Введите корректный ID группы."); + } + } + + // Приватный метод для валидации существования группы по ID + private GroupDao ValidateGroupExistence(int groupId) + { + var existingGroup = _SQLGroupRepositoryImpl.GetAllGroups() + .FirstOrDefault(g => g.Id == groupId); + + if (existingGroup == null) + { + throw new ArgumentException("Группа не найдена."); + } + + return existingGroup; + } + + + // Метод для получения списка всех групп + public List GetAllGroups() + { + return [.. _SQLGroupRepositoryImpl.GetAllGroups() + .Select(it => new GroupDao { Id = it.Id, Name = it.Name })]; + } + + // Метод для получения группы по ID + public string FindGroupById(int IdGroup) + { + string groups = _SQLGroupRepositoryImpl.GetGroupById(IdGroup).Name; + + return groups; + } + + + // Метод для добавления новой группы + public void AddGroup(string groupName) + { + ValidateGroupName(groupName); + GroupDao newGroup = new GroupDao + { + Name = groupName + }; + + _SQLGroupRepositoryImpl.AddGroup(newGroup.Name); + } + + + // Метод для изменения названия группы + public void UpdateGroup(int groupId, string newGroupName) + { + ValidateGroupName(newGroupName); + var existingGroup = ValidateGroupExistence(groupId); + + existingGroup.Name = newGroupName; + _SQLGroupRepositoryImpl.UpdateGroupById(groupId,existingGroup); + } + } +} \ No newline at end of file diff --git a/Demo/Domain/UseCase/UseCaseGeneratePresence.cs b/Demo/Domain/UseCase/UseCaseGeneratePresence.cs new file mode 100644 index 0000000..71e0b64 --- /dev/null +++ b/Demo/Domain/UseCase/UseCaseGeneratePresence.cs @@ -0,0 +1,192 @@ +using ClosedXML.Excel; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.Data.Repository; +using Demo.domain.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Demo.Domain.UseCase +{ + public class UseCaseGeneratePresence + { + public readonly IUserRepository _userRepository; + public readonly IPresenceRepository _presenceRepository; + private readonly IGroupRepository _groupRepository; + + public UseCaseGeneratePresence(IUserRepository userRepository, IPresenceRepository presenceRepository, IGroupRepository groupRepository) + { + _userRepository = userRepository; + _presenceRepository = presenceRepository; + _groupRepository = groupRepository; + } + + public Dictionary> GetAllAttendanceByGroups() + { + var attendanceByGroup = new Dictionary>(); + var allGroups = _groupRepository.GetAllGroups(); + + foreach (var group in allGroups) + { + var groupAttendance = _presenceRepository.GetAttendanceByGroup(group.Id); + var attendanceRecords = new List(); + + foreach (var record in groupAttendance) + { + var names = _userRepository.GetUserNames().Where(u => u.UserId == record.UserId); + foreach (var name in names) + { + attendanceRecords.Add(new AttendanceRecord + { + UserName = name.FIO, + UserId = name.UserId, + Date = record.Date, + IsAttedance = record.IsAttedance, + LessonNumber = record.LessonNumber, + GroupName = group.Name + }); + } + } + + attendanceByGroup.Add(group.Name, attendanceRecords); + } + + return attendanceByGroup; + } + + public void ExportAttendanceToExcel() + { + var attendanceByGroup = GetAllAttendanceByGroups(); + string projectDirectory = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.FullName; + string reportsFolderPath = Path.Combine(projectDirectory, "Reports"); + string filePath = Path.Combine(reportsFolderPath, "AttendanceReport.xlsx"); + + // Создаем папку, если она не существует + if (!Directory.Exists(reportsFolderPath)) + { + Directory.CreateDirectory(reportsFolderPath); + } + using (var workbook = new XLWorkbook()) + { + foreach (var group in attendanceByGroup) + { + var worksheet = workbook.Worksheets.Add($"{group.Key}"); + worksheet.Cell(1, 1).Value = "ФИО"; + worksheet.Cell(1, 2).Value = "Группа"; + worksheet.Cell(1, 3).Value = "Дата"; + worksheet.Cell(1, 4).Value = "Занятие"; + worksheet.Cell(1, 5).Value = "Статус"; + + int row = 2; + int lesNum = 1; + foreach (var record in group.Value.OrderBy(r => r.Date).ThenBy(r => r.LessonNumber).ThenBy(r => r.UserId)) + { + if (lesNum != record.LessonNumber) + { + row++; + } + worksheet.Cell(row, 1).Value = record.UserName; + worksheet.Cell(row, 2).Value = record.GroupName; + worksheet.Cell(row, 3).Value = record.Date.ToString("dd.MM.yyyy"); + worksheet.Cell(row, 4).Value = record.LessonNumber; + worksheet.Cell(row, 5).Value = record.IsAttedance ? "Присутствует" : "Отсутствует"; + row++; + + + + lesNum = record.LessonNumber; + } + + worksheet.Columns().AdjustToContents(); + } + + workbook.SaveAs(filePath); + } + } + + + + + public List GetPresenceByDateAndGroup(DateTime date, int groupId) + { + return _presenceRepository.GetPresenceByDateAndGroup(date, groupId); + } + + public void GeneratePresenceDaily(int firstLesson, int lastLesson, int groupId) + { + var users = _userRepository.GetAllUsers().Where(u => u.GroupId == groupId).ToList(); + + // Находим последнюю дату посещаемости для данной группы + DateOnly startDate = _presenceRepository.GetLastDateByGroupId(groupId)?.AddDays(1) + ?? DateOnly.FromDateTime(DateTime.Today); + + List presences = new List(); + for (int lessonNumber = firstLesson; lessonNumber <= lastLesson; lessonNumber++) + { + foreach (var user in users) + { + var presence = new PresenceDao + { + UserId = user.UserId, + GroupId = user.GroupId, + Date = startDate, + LessonNumber = lessonNumber, + IsAttedance = true + }; + _presenceRepository.SavePresence(new List { presence }); + } + } + } + + + + + public void GenerateWeeklyPresence(int firstLesson, int lastLesson, int groupId, DateTime startTime) + { + for (int i = 0; i < 7; i++) + { + DateTime currentTime = startTime.AddDays(i); + GeneratePresenceDaily(firstLesson, lastLesson, groupId); + } + } + + + + // Отметить пользователя как отсутствующего на диапазоне занятий + public bool MarkUserAbsentForLessons(int userId, int groupId, int firstLesson, int lastLesson, DateTime date) + { + List presences = _presenceRepository.GetPresenceForAbsent(date, groupId); + if (presences.Where(p => p.UserId == userId).Count() > 0) + { + // Обновляем состояние присутствия для указанных занятий + foreach (var presence in presences.Where(p => p.UserId == userId && p.LessonNumber >= firstLesson && p.LessonNumber <= lastLesson)) + { + presence.IsAttedance = false; // Устанавливаем отсутствие + } + // Сохраняем изменения в репозитории + _presenceRepository.UpdateAtt(userId, groupId, firstLesson, lastLesson, DateOnly.FromDateTime(date), false); + return true; + } + else + { + return false; + } + } + + public List GetAllPresenceByGroup(int groupId) + { + return _presenceRepository.GetPresenceByGroup(groupId); + } + + public GroupAttendanceStatistics GetGeneralPresence(int groupId) + { + return _presenceRepository.GetGeneralPresenceForGroup(groupId); + } + + + + } +} diff --git a/Demo/Domain/UseCase/UserUseCase.cs b/Demo/Domain/UseCase/UserUseCase.cs new file mode 100644 index 0000000..9a7bf71 --- /dev/null +++ b/Demo/Domain/UseCase/UserUseCase.cs @@ -0,0 +1,146 @@ +using Demo.Data.Exceptions; +using Demo.Data.RemoteData.RemoteDataBase; +using Demo.Data.RemoteData.RemoteDataBase.DAO; +using Demo.Data.Repository; +using Demo.domain.Models; + +namespace Demo.Domain.UseCase +{ + public class UserUseCase + { + private readonly IUserRepository _repositoryUserImpl; + private readonly IGroupRepository _repositoryGroupImpl; + private readonly IPresenceRepository _repositoryPresenceImpl; + public UserUseCase(IUserRepository repositoryImpl, IGroupRepository repositoryGroupImpl, IPresenceRepository presenceRepository) + { + _repositoryUserImpl = repositoryImpl; + _repositoryGroupImpl = repositoryGroupImpl; + _repositoryPresenceImpl = presenceRepository; + } + + // Приватный метод для валидации ФИО пользователя + private void ValidateUserFIO(string fio) + { + if (string.IsNullOrWhiteSpace(fio)) + { + throw new ArgumentException("ФИО не может быть пустым."); + } + } + + public void RemovePresenceByUserId(int userId) + { + using (var context = new RemoteDatabaseContext()) + { + var presences = context.PresenceDaos.Where(p => p.UserId == userId).ToList(); + context.PresenceDaos.RemoveRange(presences); + context.SaveChanges(); + } + } + + // Приватный метод для валидации существования пользователя по ID + private UserDao ValidateUserExistence(int userId) + { + var user = _repositoryUserImpl.GetAllUsers() + .FirstOrDefault(u => u.UserId == userId); + + if (user == null) + { + throw new Exception("Пользователь не найден."); + } + + return user; + } + + // Приватный метод для валидации существования группы по ID + private GroupDao ValidateGroupExistence(int groupId) + { + var group = _repositoryGroupImpl.GetAllGroups() + .FirstOrDefault(g => g.Id == groupId); + + if (group == null) + { + throw new Exception("Группа не найдена."); + } + + return group; + } + + // Вывести всех пользователей + //упростить под ef + public List GetAllUsers() => _repositoryUserImpl.GetAllUsers() + .Join(_repositoryGroupImpl.GetAllGroups(), + user => user.GroupId, // Ключ для пользователей + group => group.Id, // Ключ для групп + (user, group) => // Результирующий объект + new UserDao + { + UserId = user.UserId, + FIO = user.FIO, + Group = new GroupDao { Id = group.Id, Name = group.Name } + }).ToList(); + + // Удалить пользователя по id + public bool RemoveUserById(int userId) + { + try + { + return _repositoryUserImpl.RemoveUserById(userId); + } + catch (UserNotFoundException ex) + { + Console.WriteLine($"Ошибка: {ex.Message}"); + return false; + } + catch (RepositoryException ex) + { + Console.WriteLine($"Ошибка в репозитории: {ex.Message}"); + return false; + } + } + + // Обновить пользователя по id + public UserDao UpdateUser(int userId, string newFio, int groupId) + { + ValidateUserFIO(newFio); + ValidateGroupExistence(groupId); + + UserDao userDao = new UserDao + { + UserId = userId, + FIO = newFio, + GroupId = groupId + }; + + UserDao? result = _repositoryUserImpl.UpdateUser(userId, newFio, groupId); + + if (result == null) + { + throw new Exception("Ошибка при обновлении пользователя."); + } + + var groupEntity = ValidateGroupExistence(result.GroupId); + + return new UserDao + { + UserId= userId, + FIO = newFio, + GroupId = groupId + }; + + } + + // Найти пользователя по id + public UserDao FindUserById(int userId) + { + var user = ValidateUserExistence(userId); + var group = ValidateGroupExistence(user.GroupId); + + return new UserDao + { + UserId = user.UserId, + FIO = user.FIO, + Group = new GroupDao { Id = group.Id, Name=group.Name } + }; + } + } +} diff --git a/Demo/Migrations/20241103105727_CreateDatabase.Designer.cs b/Demo/Migrations/20241103105727_CreateDatabase.Designer.cs new file mode 100644 index 0000000..d2f3403 --- /dev/null +++ b/Demo/Migrations/20241103105727_CreateDatabase.Designer.cs @@ -0,0 +1,113 @@ +// +using System; +using Demo.Data.RemoteData.RemoteDataBase; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Demo.Migrations +{ + [DbContext(typeof(RemoteDatabaseContext))] + [Migration("20241103105727_CreateDatabase")] + partial class CreateDatabase + { + /// + 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("Demo.Data.RemoteData.RemoteDataBase.DAO.GroupDao", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.PresenceDao", b => + { + b.Property("PresenceId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PresenceId")); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("GroupId") + .HasColumnType("integer"); + + b.Property("IsAttedance") + .HasColumnType("boolean"); + + b.Property("LessonNumber") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("PresenceId"); + + b.ToTable("PresenceDaos"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.UserDao", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("FIO") + .IsRequired() + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("integer"); + + b.HasKey("UserId"); + + b.HasIndex("GroupId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.UserDao", b => + { + b.HasOne("Demo.Data.RemoteData.RemoteDataBase.DAO.GroupDao", "Group") + .WithMany("Users") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.GroupDao", b => + { + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Demo/Migrations/20241103105727_CreateDatabase.cs b/Demo/Migrations/20241103105727_CreateDatabase.cs new file mode 100644 index 0000000..7ec3b53 --- /dev/null +++ b/Demo/Migrations/20241103105727_CreateDatabase.cs @@ -0,0 +1,84 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Demo.Migrations +{ + /// + public partial class CreateDatabase : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Groups", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Groups", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "PresenceDaos", + columns: table => new + { + PresenceId = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "integer", nullable: false), + IsAttedance = table.Column(type: "boolean", nullable: false), + Date = table.Column(type: "date", nullable: false), + LessonNumber = table.Column(type: "integer", nullable: false), + GroupId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PresenceDaos", x => x.PresenceId); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + UserId = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + FIO = table.Column(type: "text", nullable: false), + GroupId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.UserId); + table.ForeignKey( + name: "FK_Users_Groups_GroupId", + column: x => x.GroupId, + principalTable: "Groups", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Users_GroupId", + table: "Users", + column: "GroupId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PresenceDaos"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Groups"); + } + } +} diff --git a/Demo/Migrations/RemoteDatabaseContextModelSnapshot.cs b/Demo/Migrations/RemoteDatabaseContextModelSnapshot.cs new file mode 100644 index 0000000..3ee588f --- /dev/null +++ b/Demo/Migrations/RemoteDatabaseContextModelSnapshot.cs @@ -0,0 +1,110 @@ +// +using System; +using Demo.Data.RemoteData.RemoteDataBase; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Demo.Migrations +{ + [DbContext(typeof(RemoteDatabaseContext))] + partial class RemoteDatabaseContextModelSnapshot : 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("Demo.Data.RemoteData.RemoteDataBase.DAO.GroupDao", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.PresenceDao", b => + { + b.Property("PresenceId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PresenceId")); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("GroupId") + .HasColumnType("integer"); + + b.Property("IsAttedance") + .HasColumnType("boolean"); + + b.Property("LessonNumber") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("PresenceId"); + + b.ToTable("PresenceDaos"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.UserDao", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("FIO") + .IsRequired() + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("integer"); + + b.HasKey("UserId"); + + b.HasIndex("GroupId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.UserDao", b => + { + b.HasOne("Demo.Data.RemoteData.RemoteDataBase.DAO.GroupDao", "Group") + .WithMany("Users") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Demo.Data.RemoteData.RemoteDataBase.DAO.GroupDao", b => + { + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Demo/Program.cs b/Demo/Program.cs new file mode 100644 index 0000000..df70b1d --- /dev/null +++ b/Demo/Program.cs @@ -0,0 +1,28 @@ +using Demo.Data.RemoteData.RemoteDataBase; +using Demo.Data.Repository; +using Demo.Domain.UseCase; +using Demo.UI; +using Microsoft.Extensions.DependencyInjection; + +// Создаем экземпляр репозиториев + +IServiceCollection services = new ServiceCollection(); + +services + .AddDbContext() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + + +var serviceProvider = services.BuildServiceProvider(); +// Создаем пользовательский интерфейс +MainMenuUI mainMenuUI = serviceProvider.GetService(); + +// Выводим главное меню +mainMenuUI.DisplayMenu(); diff --git a/Demo/Reports/AttendanceReport.xlsx b/Demo/Reports/AttendanceReport.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e0323979f7964063dca4569539819802ee17a29a GIT binary patch literal 10544 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 presences = _presenceUseCase.GetPresenceByDateAndGroup(date, groupId); + + if (presences == null || presences.Count == 0) + { + Console.WriteLine("Посещаемость на выбранную дату отсутствует."); + return; + } + + // Сортируем присутствия по номеру занятия и ID пользователя + var sortedPresences = presences.OrderBy(p => p.LessonNumber) + .ThenBy(p => p.UserId); + + Console.WriteLine($"\nПосещаемость на {date.ToShortDateString()} для группы с ID {groupId}:"); + Console.WriteLine("---------------------------------------------"); + + int previousLessonNumber = -1; // Инициализация для сравнения + foreach (var presence in sortedPresences) + { + if (previousLessonNumber != presence.LessonNumber) + { + Console.WriteLine("---------------------------------------------"); + previousLessonNumber = presence.LessonNumber; + } + string status = presence.IsAttedance ? "Присутствует" : "Отсутствует"; + Console.WriteLine($"Пользователь ID: {presence.UserId}, Занятие {presence.LessonNumber}: {status}"); + } + Console.WriteLine("---------------------------------------------"); + } + catch (Exception ex) + { + Console.WriteLine($"Ошибка при выводе посещаемости: {ex.Message}"); + } + + } + + public void MarkUserAbsent(DateTime date, int groupId, int userId, int firstLesson, int lastLesson) + { + bool check=_presenceUseCase.MarkUserAbsentForLessons(userId, groupId, firstLesson, lastLesson, date); + if (check) + { + Console.WriteLine("Пользователь отмечен как осутсвующий"); + } + else + { + Console.WriteLine($"Посещаемость для пользователя ID: {userId} на дату {date.ToShortDateString()}" + + $" с {firstLesson} по {lastLesson} уроки не найдена."); + } + } + + public void DisplayGeneralPresence(int groupId) + { + var statistics = _presenceUseCase.GetGeneralPresence(groupId); + Console.WriteLine($"Человек в группе: {statistics.UserCount}, " + + $"Количество проведённых занятий: {statistics.TotalLessons}, " + + $"Общий процент посещаемости группы: {statistics.AttendancePercentage}%"); + + foreach (var user in statistics.UserAttendanceDetails) + { + Console.ForegroundColor = user.AttendanceRate < 40 ? ConsoleColor.Red : ConsoleColor.White; + Console.WriteLine($"ID Пользователя: {user.UserId}, Посетил: {user.Attended}, " + + $"Пропустил: {user.Missed}, Процент посещаемости: {user.AttendanceRate}%"); + } + Console.ForegroundColor = ConsoleColor.White; + } + + + + public void DisplayAllPresenceByGroup(int groupId) + { + try + { + // Получаем все посещения для группы + var presences = _presenceUseCase.GetAllPresenceByGroup(groupId); + + if (presences == null || presences.Count == 0) + { + Console.WriteLine($"Посещаемость для группы с ID {groupId} отсутствует."); + return; + } + + // Группируем по дате + var groupedPresences = presences.GroupBy(p => p.Date); + + foreach (var group in groupedPresences) + { + Console.WriteLine("==================================================="); + Console.WriteLine($"Дата: {group.Key.ToString("dd.MM.yyyy")}"); + Console.WriteLine("==================================================="); + + // Группируем по занятию + var groupedByLesson = group.GroupBy(p => p.LessonNumber); + + foreach (var lessonGroup in groupedByLesson) + { + Console.WriteLine($"Занятие {lessonGroup.Key}:"); + + // Создаем HashSet для уникальных пользователей + var userIds = new HashSet(); + + foreach (var presence in lessonGroup) + { + // Проверяем, добавляется ли пользователь в HashSet + if (userIds.Add(presence.UserId)) + { + string status = presence.IsAttedance ? "Присутствует" : "Отсутствует"; + Console.WriteLine($"Пользователь ID: {presence.UserId}, Статус: {status}"); + } + } + + Console.WriteLine("---------------------------------------------------"); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Ошибка при выводе посещаемости: {ex.Message}"); + } + } + + + } +} diff --git a/Demo/UI/UserConsole.cs b/Demo/UI/UserConsole.cs new file mode 100644 index 0000000..b3b5c52 --- /dev/null +++ b/Demo/UI/UserConsole.cs @@ -0,0 +1,81 @@ +using Demo.Data.RemoteData.RemoteDataBase; +using Demo.Data.Repository; +using Demo.Domain.UseCase; +using System; +using System.Text; + +namespace Demo.UI +{ + public class UserConsoleUI + { + private readonly UserUseCase _userUseCase; + + public UserConsoleUI(UserUseCase userUseCase) + { + _userUseCase = userUseCase; + } + + // Метод для отображения всех пользователей + public void DisplayAllUsers() + { + Console.WriteLine("\n=== Список всех пользователей ==="); + StringBuilder userOutput = new StringBuilder(); + + foreach (var user in _userUseCase.GetAllUsers()) + { + userOutput.AppendLine($"{user.UserId}\t{user.FIO}\t{user.Group.Name}"); + } + + Console.WriteLine(userOutput); + Console.WriteLine("===============================\n"); + } + + // Метод для удаления пользователя по ID + public void RemoveUserById(int userId) + { + // Сначала удаляем все записи о присутствии пользователя + _userUseCase.RemovePresenceByUserId(userId); + + // Теперь удаляем пользователя + string output = _userUseCase.RemoveUserById(userId) ? "Пользователь удален" : "Пользователь не найден"; + Console.WriteLine($"\n{output}\n"); + } + + // Метод для обновления пользователя по ID + public void UpdateUserById(int userId) + { + try + { + var user = _userUseCase.FindUserById(userId); + + + Console.WriteLine($"Текущие данные: {user.FIO}"); + Console.Write("\nВведите новое ФИО: "); + string newFIO = Console.ReadLine(); + Console.Write("\nВведите новый ID группы (или оставьте такой же): "); + int GroupId = int.Parse(Console.ReadLine()); + _userUseCase.UpdateUser(userId, newFIO, GroupId); + + Console.WriteLine("\nПользователь обновлен.\n"); + } + catch (Exception ex) + { + Console.WriteLine($"Ошибка: {ex.Message}\n"); + } + } + + // Метод для поиска пользователя по ID + public void FindUserById(int userId) + { + var user = _userUseCase.FindUserById(userId); + if (user != null) + { + Console.WriteLine($"\nПользователь найден: {user.UserId}, {user.FIO}, {user.Group.Name}\n"); + } + else + { + Console.WriteLine("\nПользователь не найден.\n"); + } + } + } +} diff --git a/presence.sln b/presence.sln new file mode 100644 index 0000000..34ad780 --- /dev/null +++ b/presence.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "presence", "presence\presence.csproj", "{5DBB1FC4-E89F-4AC4-B5FD-D8A5321300CA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5DBB1FC4-E89F-4AC4-B5FD-D8A5321300CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DBB1FC4-E89F-4AC4-B5FD-D8A5321300CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DBB1FC4-E89F-4AC4-B5FD-D8A5321300CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DBB1FC4-E89F-4AC4-B5FD-D8A5321300CA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/presence/Class1.cs b/presence/Class1.cs new file mode 100644 index 0000000..22c197a --- /dev/null +++ b/presence/Class1.cs @@ -0,0 +1,7 @@ +namespace presence +{ + public class Class1 + { + + } +} diff --git a/presence/presence.csproj b/presence/presence.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/presence/presence.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + +