This commit is contained in:
IsaykinEugene 2025-04-28 11:51:01 +03:00
commit 158990bea7
358 changed files with 18269 additions and 0 deletions

13
.idea/.idea.Presence/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/projectSettingsUpdater.xml
/modules.xml
/contentModel.xml
/.idea.Presence.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AvaloniaProject">
<option name="projectPerEditor">
<map>
<entry key="Presence/AddOrEditUserWindow.axaml" value="Presence/Presence.csproj" />
<entry key="Presence/AttendanceWindow.axaml" value="Presence/Presence.csproj" />
<entry key="Presence/GroupsWindow.axaml" value="Presence/Presence.csproj" />
<entry key="Presence/MainWindow.axaml" value="Presence/Presence.csproj" />
<entry key="Presence/StudentsWindow.axaml" value="Presence/Presence.csproj" />
</map>
</option>
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>

40
Presence.sln Normal file
View File

@ -0,0 +1,40 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Presence", "Presence\Presence.csproj", "{BB0DE670-741D-45E9-9EBE-B8A9EAB65606}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresenceConsole", "PresenceConsole\PresenceConsole.csproj", "{6C68882B-47D2-4CAC-B4E6-D962E49D101D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "data", "data\data.csproj", "{7DD0FFD6-9AE3-4E74-ACF2-84874C809D0D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "domain", "domain\domain.csproj", "{3A0731CE-9C7A-4711-9E2D-14D10219DC1A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UI", "UI\UI.csproj", "{D6544F65-7A64-4360-8CA6-5B82BC778A14}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BB0DE670-741D-45E9-9EBE-B8A9EAB65606}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB0DE670-741D-45E9-9EBE-B8A9EAB65606}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB0DE670-741D-45E9-9EBE-B8A9EAB65606}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB0DE670-741D-45E9-9EBE-B8A9EAB65606}.Release|Any CPU.Build.0 = Release|Any CPU
{6C68882B-47D2-4CAC-B4E6-D962E49D101D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C68882B-47D2-4CAC-B4E6-D962E49D101D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C68882B-47D2-4CAC-B4E6-D962E49D101D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C68882B-47D2-4CAC-B4E6-D962E49D101D}.Release|Any CPU.Build.0 = Release|Any CPU
{7DD0FFD6-9AE3-4E74-ACF2-84874C809D0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DD0FFD6-9AE3-4E74-ACF2-84874C809D0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DD0FFD6-9AE3-4E74-ACF2-84874C809D0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DD0FFD6-9AE3-4E74-ACF2-84874C809D0D}.Release|Any CPU.Build.0 = Release|Any CPU
{3A0731CE-9C7A-4711-9E2D-14D10219DC1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A0731CE-9C7A-4711-9E2D-14D10219DC1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A0731CE-9C7A-4711-9E2D-14D10219DC1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A0731CE-9C7A-4711-9E2D-14D10219DC1A}.Release|Any CPU.Build.0 = Release|Any CPU
{D6544F65-7A64-4360-8CA6-5B82BC778A14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6544F65-7A64-4360-8CA6-5B82BC778A14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6544F65-7A64-4360-8CA6-5B82BC778A14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6544F65-7A64-4360-8CA6-5B82BC778A14}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,21 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Presence.AddOrEditUserWindow"
x:CompileBindings="False"
Title="Добавить / Редактировать студента" Width="400" Height="250">
<StackPanel Margin="20" Spacing="10">
<TextBlock Text="ФИО:"/>
<TextBox Name="FioTextBox" />
<TextBlock Text="Группа:"/>
<ComboBox Name="GroupComboBox">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Сохранить" Click="SaveButton_Click" Margin="0,10,0,0"/>
</StackPanel>
</Window>

View File

@ -0,0 +1,77 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using System.Linq;
using data.RemoteData.RemoteDataBase;
using data.RemoteData.RemoteDataBase.DAO;
using Microsoft.EntityFrameworkCore;
namespace Presence
{
public partial class AddOrEditUserWindow : Window
{
private readonly RemoteDatabaseContext _context;
private readonly UserDAO? _userToEdit;
public AddOrEditUserWindow()
{
InitializeComponent();
}
public AddOrEditUserWindow(RemoteDatabaseContext context, UserDAO? userToEdit = null) : this()
{
_context = context;
_userToEdit = userToEdit;
LoadGroups();
if (_userToEdit != null)
{
FioTextBox.Text = _userToEdit.FIO;
GroupComboBox.SelectedItem = _context.Groups.FirstOrDefault(g => g.Id == _userToEdit.GroupId);
}
}
private void LoadGroups()
{
GroupComboBox.ItemsSource = _context.Groups.ToList();
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
var fio = FioTextBox.Text?.Trim();
var selectedGroup = GroupComboBox.SelectedItem as GroupDAO;
if (string.IsNullOrWhiteSpace(fio) || selectedGroup == null)
return;
if (_userToEdit == null)
{
// Новый пользователь
var newUser = new UserDAO
{
FIO = fio,
GroupId = selectedGroup.Id
};
_context.Users.Add(newUser);
}
else
{
// Обновляем существующего пользователя
_userToEdit.FIO = fio;
_userToEdit.GroupId = selectedGroup.Id;
if (_context.Entry(_userToEdit).State == EntityState.Detached)
{
_context.Users.Attach(_userToEdit);
}
_context.Entry(_userToEdit).State = EntityState.Modified;
}
_context.SaveChanges();
Close();
}
}
}

10
Presence/App.axaml Normal file
View File

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

23
Presence/App.axaml.cs Normal file
View File

@ -0,0 +1,23 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Presence;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}

View File

@ -0,0 +1,36 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Presence.AttendanceWindow"
x:CompileBindings="False"
Title="Посещаемость" Width="800" Height="550">
<StackPanel Margin="20" Spacing="10">
<!-- ПАНЕЛЬ ФИЛЬТРОВ -->
<StackPanel Orientation="Horizontal" Spacing="10">
<!-- Выбор группы -->
<ComboBox Name="GroupComboBox" Width="200">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Выбор даты -->
<DatePicker Name="DatePicker" />
<!-- Кнопка применить фильтр -->
<Button Name="BtnFilter" Content="Показать" Width="100"/>
</StackPanel>
<!-- Список посещаемости -->
<ListBox Name="AttendanceList" Height="420">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>

View File

@ -0,0 +1,73 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using System;
using System.Linq;
using data.RemoteData.RemoteDataBase;
using data.RemoteData.RemoteDataBase.DAO;
namespace Presence
{
public partial class AttendanceWindow : Window
{
private readonly RemoteDatabaseContext _context;
public AttendanceWindow()
{
InitializeComponent();
_context = new RemoteDatabaseContext();
// Инициализируем фильтры
LoadGroups();
DatePicker.SelectedDate = DateTime.Today;
// Навешиваем обработчики
BtnFilter.Click += BtnFilter_Click;
// Загрузка сразу при открытии
LoadAttendance();
}
private void LoadGroups()
{
var groups = _context.Groups.ToList();
GroupComboBox.ItemsSource = groups;
GroupComboBox.SelectedIndex = 0;
}
private void BtnFilter_Click(object? sender, RoutedEventArgs e)
{
LoadAttendance();
}
private void LoadAttendance()
{
// 1) Получаем Nullable<DateTimeOffset> из DatePicker
var selectedDto = DatePicker.SelectedDate ?? DateTimeOffset.Now;
// 2) Преобразуем в DateTime
DateTime dt = selectedDto.DateTime;
// 3) Делаем DateOnly
DateOnly date = DateOnly.FromDateTime(dt);
// Выбранная группа
var selectedGroup = GroupComboBox.SelectedItem as GroupDAO;
// Фильтруем посещаемость по DateOnly и GroupId
var records = _context.Presences
.Where(a => a.Date == date) // a.Date — DateOnly
.Join(_context.Users,
a => a.UserId,
u => u.UserId,
(a, u) => new { a, u })
.Where(x => selectedGroup == null || x.u.GroupId == selectedGroup.Id)
.Select(x =>
$"{x.u.FIO} — {x.a.Date:yyyy-MM-dd} — " +
(x.a.IsAttendance ? "Присутствовал" : "Отсутствовал"))
.ToList();
AttendanceList.ItemsSource = records;
}
}
}

View File

@ -0,0 +1,34 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Presence.GroupsWindow"
x:CompileBindings="False"
Title="Группы" Width="600" Height="550">
<StackPanel Margin="20">
<ComboBox Name="GroupComboBox" Width="300" Margin="0,0,0,10">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ListBox Name="UsersList" Height="350">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FIO}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Редактировать" Click="EditUser_Click"/>
<MenuItem Header="Удалить" Click="DeleteUser_Click"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Button Name="BtnAddUser" Content="Добавить студента" Width="150"/>
<Button Name="BtnRefresh" Content="Обновить" Width="100" Margin="10,0,0,0"/>
</StackPanel>
</StackPanel>
</Window>

View File

@ -0,0 +1,80 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using System.Linq;
using data.RemoteData.RemoteDataBase;
using data.RemoteData.RemoteDataBase.DAO;
namespace Presence
{
public partial class GroupsWindow : Window
{
private readonly RemoteDatabaseContext _context;
public GroupsWindow()
{
InitializeComponent();
_context = new RemoteDatabaseContext();
LoadGroups();
GroupComboBox.SelectionChanged += GroupComboBox_SelectionChanged;
BtnRefresh.Click += (s, e) => LoadGroups();
BtnAddUser.Click += BtnAddUser_Click;
}
private void LoadGroups()
{
var groups = _context.Groups.ToList();
GroupComboBox.ItemsSource = groups;
LoadUsers();
}
private void GroupComboBox_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
LoadUsers();
}
private void LoadUsers()
{
if (GroupComboBox.SelectedItem is GroupDAO selectedGroup)
{
var users = _context.Users
.Where(u => u.GroupId == selectedGroup.Id)
.ToList();
UsersList.ItemsSource = users;
}
else
{
UsersList.ItemsSource = null;
}
}
private async void BtnAddUser_Click(object? sender, RoutedEventArgs e)
{
var addWindow = new AddOrEditUserWindow(_context);
await addWindow.ShowDialog(this);
LoadUsers();
}
private async void EditUser_Click(object? sender, RoutedEventArgs e)
{
if (UsersList.SelectedItem is UserDAO selectedUser)
{
var editWindow = new AddOrEditUserWindow(_context, selectedUser);
await editWindow.ShowDialog(this);
LoadUsers();
}
}
private void DeleteUser_Click(object? sender, RoutedEventArgs e)
{
if (UsersList.SelectedItem is UserDAO selectedUser)
{
_context.Users.Remove(selectedUser);
_context.SaveChanges();
LoadUsers();
}
}
}
}

10
Presence/MainWindow.axaml Normal file
View File

@ -0,0 +1,10 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Presence.MainWindow"
Title="Presence - Главное окно" Width="800" Height="600">
<StackPanel Margin="20">
<Button Name="BtnOpenGroups" Content="Открыть группы" Margin="0,0,0,10" />
<Button Name="BtnOpenStudents" Content="Открыть студентов" Margin="0,0,0,10" />
<Button Name="BtnOpenAttendance" Content="Открыть посещаемость" Margin="0,0,0,10" />
</StackPanel>
</Window>

View File

@ -0,0 +1,35 @@
using Avalonia.Controls;
using System;
namespace Presence
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
BtnOpenGroups.Click += BtnOpenGroups_Click;
BtnOpenStudents.Click += BtnOpenStudents_Click;
BtnOpenAttendance.Click += BtnOpenAttendance_Click;
}
private void BtnOpenGroups_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var window = new GroupsWindow();
window.Show();
}
private void BtnOpenStudents_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var window = new StudentsWindow();
window.Show();
}
private void BtnOpenAttendance_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var window = new AttendanceWindow();
window.Show();
}
}
}

31
Presence/Presence.csproj Normal file
View File

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.1"/>
<PackageReference Include="Avalonia.Desktop" Version="11.2.1"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.1">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="ClosedXML" Version="0.105.0-rc" />
<PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0-rc.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\data\data.csproj" />
<ProjectReference Include="..\domain\domain.csproj" />
</ItemGroup>
</Project>

21
Presence/Program.cs Normal file
View File

@ -0,0 +1,21 @@
using Avalonia;
using System;
namespace Presence;
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();
}

View File

@ -0,0 +1,9 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Presence.StudentsWindow"
Title="Студенты" Width="600" Height="400">
<StackPanel Margin="20">
<ListBox Name="StudentsList" Height="300" />
<Button Name="BtnRefreshStudents" Content="Обновить" Margin="0,10,0,0"/>
</StackPanel>
</Window>

View File

@ -0,0 +1,28 @@
using Avalonia.Controls;
using System.Linq;
using data;
using data.RemoteData.RemoteDataBase;
namespace Presence
{
public partial class StudentsWindow : Window
{
private readonly RemoteDatabaseContext _context;
public StudentsWindow()
{
InitializeComponent();
_context = new RemoteDatabaseContext();
LoadStudents();
BtnRefreshStudents.Click += (s, e) => LoadStudents();
}
private void LoadStudents()
{
var users = _context.Users.ToList();
StudentsList.ItemsSource = users.Select(s => $"{s.FIO}").ToList();
}
}
}

18
Presence/app.manifest Normal file
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"/>
<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>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,14 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
"configProperties": {
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.InteropServices.BuiltInComInterop.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More