CI / build-and-test (push) Canceled after 0s
Ein Zwischenstand ist ein Durchgang aus Stichtag, Anlass und Schülerauswahl (ganze Gruppe, Teilgruppe oder Einzelfall, mit Schnellfilter für gefährdete Schüler). Der Zeugnisnoten-Assistent geht die gewählten Schüler im Zwischenstand-Modus durch und notiert vorläufige schriftliche, mündliche und Gesamtnote samt Notiz. Gespeichert wird das als eigenes Modell InterimGrade, getrennt von der Zeugnisnote. Die Berechnung endet am Stichtag und rechnet den laufenden Mitarbeits-Abschnitt mit ein. Zwischenstände erscheinen im Assistenten als Liste und Marker im Verlauf sowie als graue Spalte in der Notenübersicht (nicht in "Gesamt", nicht im PDF). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
209 lines
9.7 KiB
C#
209 lines
9.7 KiB
C#
using Avalonia.Controls;
|
|
using Avalonia.Data;
|
|
using Avalonia.Interactivity;
|
|
using LehrerApp.Core.Interfaces;
|
|
using LehrerApp.Core.Services;
|
|
using LehrerApp.Desktop.Services;
|
|
using LehrerApp.Desktop.ViewModels.Groups;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace LehrerApp.Desktop.Views.Groups;
|
|
|
|
public partial class GradeOverviewTabView : UserControl
|
|
{
|
|
private GradeOverviewTabViewModel? _vm;
|
|
|
|
public GradeOverviewTabView() => InitializeComponent();
|
|
|
|
protected override void OnDataContextChanged(EventArgs e)
|
|
{
|
|
base.OnDataContextChanged(e);
|
|
|
|
if (DataContext is GradeOverviewTabViewModel vm)
|
|
{
|
|
_vm = vm;
|
|
vm.OnManageStudentGrades = ShowStudentGradesDialog;
|
|
vm.OnCollectiveGrade = ShowCollectiveGradeDialog;
|
|
vm.OnReportGrades = ShowReportGradesDialog;
|
|
vm.OnInterimGrades = async studentId =>
|
|
{
|
|
if (TopLevel.GetTopLevel(this) is Window owner) await ShowInterimRun(owner, studentId);
|
|
};
|
|
vm.OnShowPerformanceOverview = ShowPerformanceOverviewDialog;
|
|
vm.PropertyChanged += (_, pe) =>
|
|
{
|
|
if (pe.PropertyName == nameof(GradeOverviewTabViewModel.RebuildColumnsSignal))
|
|
BuildColumns();
|
|
};
|
|
BuildColumns();
|
|
}
|
|
}
|
|
|
|
private async Task ShowStudentGradesDialog(GradeOverviewRow row)
|
|
{
|
|
var dialogVm = new StudentGradesDialogViewModel(
|
|
App.Services.GetRequiredService<IGradeRepository>(),
|
|
row.StudentId, _vm!.GroupId, row.Name);
|
|
|
|
var dialog = new StudentGradesDialog { DataContext = dialogVm };
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is not null) await dialog.ShowDialog(owner);
|
|
}
|
|
|
|
private async Task ShowCollectiveGradeDialog()
|
|
{
|
|
var dialogVm = new CollectiveGradeDialogViewModel(
|
|
App.Services.GetRequiredService<IGradeRepository>(),
|
|
App.Services.GetRequiredService<IStudentRepository>(),
|
|
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
|
_vm!.GroupId);
|
|
|
|
var dialog = new CollectiveGradeDialog { DataContext = dialogVm };
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is not null) await dialog.ShowDialog(owner);
|
|
}
|
|
|
|
private async Task ShowReportGradesDialog()
|
|
{
|
|
var dialogVm = new ReportGradeDialogViewModel(
|
|
App.Services.GetRequiredService<IGradeRepository>(),
|
|
App.Services.GetRequiredService<IExamRepository>(),
|
|
App.Services.GetRequiredService<IExamResultRepository>(),
|
|
App.Services.GetRequiredService<IStudentRepository>(),
|
|
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
|
App.Services.GetRequiredService<IGradingSchemeRepository>(),
|
|
App.Services.GetRequiredService<IReportGradeRepository>(),
|
|
App.Services.GetRequiredService<IParticipationSessionRepository>(),
|
|
App.Services.GetRequiredService<IParticipationRepository>(),
|
|
App.Services.GetRequiredService<AttendanceBalanceService>(),
|
|
App.Services.GetRequiredService<GradingService>(),
|
|
_vm!.GroupId, _vm.GroupType, _vm.GradingSystem, _vm.GroupLabel, _vm.SchoolYear);
|
|
|
|
var dialog = new ReportGradeDialog { DataContext = dialogVm, OnOpenAssistant = owner => ShowReportGradeAssistant(owner) };
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is not null) await dialog.ShowDialog(owner);
|
|
}
|
|
|
|
private async Task ShowReportGradeAssistant(Window owner, InterimRunOptions? interim = null)
|
|
{
|
|
var dialogVm = new ReportGradeAssistantViewModel(
|
|
App.Services.GetRequiredService<IGradeRepository>(),
|
|
App.Services.GetRequiredService<IExamRepository>(),
|
|
App.Services.GetRequiredService<IExamResultRepository>(),
|
|
App.Services.GetRequiredService<IStudentRepository>(),
|
|
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
|
App.Services.GetRequiredService<IGradingSchemeRepository>(),
|
|
App.Services.GetRequiredService<IReportGradeRepository>(),
|
|
App.Services.GetRequiredService<IParticipationSessionRepository>(),
|
|
App.Services.GetRequiredService<IParticipationRepository>(),
|
|
App.Services.GetRequiredService<IParticipationAspectRepository>(),
|
|
App.Services.GetRequiredService<IParticipationSectionRepository>(),
|
|
App.Services.GetRequiredService<IInterimGradeRepository>(),
|
|
App.Services.GetRequiredService<AttendanceBalanceService>(),
|
|
App.Services.GetRequiredService<GradingService>(),
|
|
_vm!.GroupId, _vm.GroupType, _vm.GradingSystem, _vm.GroupLabel, _vm.SchoolYear,
|
|
App.Services.GetRequiredService<IGroupRepository>().GetById(_vm.GroupId)?.GradeLevel ?? 0,
|
|
interim: interim);
|
|
|
|
var dialog = new ReportGradeAssistantDialog { DataContext = dialogVm };
|
|
dialogVm.OnStartInterim = studentId => ShowInterimRun(dialog, studentId);
|
|
await dialog.ShowDialog(owner);
|
|
}
|
|
|
|
/// Zwischenstand: erst Stichtag, Anlass und Schüler festlegen, dann geht der Assistent genau
|
|
/// diese Schüler durch.
|
|
private async Task ShowInterimRun(Window owner, Guid? preselectedStudentId)
|
|
{
|
|
var dialogVm = new InterimRunDialogViewModel(
|
|
App.Services.GetRequiredService<IGradeRepository>(),
|
|
App.Services.GetRequiredService<IExamRepository>(),
|
|
App.Services.GetRequiredService<IExamResultRepository>(),
|
|
App.Services.GetRequiredService<IStudentRepository>(),
|
|
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
|
App.Services.GetRequiredService<IGradingSchemeRepository>(),
|
|
App.Services.GetRequiredService<IReportGradeRepository>(),
|
|
App.Services.GetRequiredService<IParticipationSessionRepository>(),
|
|
App.Services.GetRequiredService<IParticipationRepository>(),
|
|
App.Services.GetRequiredService<IParticipationAspectRepository>(),
|
|
App.Services.GetRequiredService<IParticipationSectionRepository>(),
|
|
App.Services.GetRequiredService<IInterimGradeRepository>(),
|
|
App.Services.GetRequiredService<GradingService>(),
|
|
_vm!.GroupId, _vm.GroupType, _vm.GradingSystem, _vm.GroupLabel, _vm.SchoolYear,
|
|
preselectedStudentId);
|
|
|
|
var dialog = new InterimRunDialog { DataContext = dialogVm };
|
|
if (await dialog.ShowDialog<bool>(owner) && dialogVm.Result is { } run)
|
|
await ShowReportGradeAssistant(owner, run);
|
|
}
|
|
|
|
private async Task ShowPerformanceOverviewDialog(GradeOverviewRow row)
|
|
{
|
|
var dialogVm = new StudentPerformanceOverviewViewModel(
|
|
App.Services.GetRequiredService<IGradeRepository>(),
|
|
App.Services.GetRequiredService<IExamRepository>(),
|
|
App.Services.GetRequiredService<IExamResultRepository>(),
|
|
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
|
App.Services.GetRequiredService<IGradingSchemeRepository>(),
|
|
App.Services.GetRequiredService<GradingService>(),
|
|
_vm!.GroupId, row.StudentId, _vm.GroupType, _vm.GradingSystem, row.Name, _vm.GroupLabel, _vm.SchoolYear);
|
|
|
|
var dialog = new StudentPerformanceOverviewDialog { DataContext = dialogVm };
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is not null) await dialog.ShowDialog(owner);
|
|
}
|
|
|
|
private async void OnExportPdfClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
var topLevel = TopLevel.GetTopLevel(this);
|
|
if (topLevel is null || _vm is null) return;
|
|
|
|
// Vorläufige Zwischenstände gehören nicht in die gedruckte Notenliste. Ihre Spalten
|
|
// stehen immer am Ende, die Zellen lassen sich also einfach abschneiden.
|
|
var printed = _vm.Columns.Where(c => !c.IsInterim).ToList();
|
|
var data = new GradeListPrintData(
|
|
_vm.GroupLabel, _vm.SchoolYear, _vm.SelectedPeriod.Label,
|
|
printed.Select(c => c.Header).ToList(),
|
|
_vm.Rows.Select(r => new GradeListPrintRow(r.Name, r.Cells.Take(printed.Count).ToList(), r.TotalDisplay)).ToList());
|
|
var pdf = App.Services.GetRequiredService<PdfExportService>().BuildGradeListPdf(data);
|
|
|
|
await App.Services.GetRequiredService<ExportService>().SaveAsync(topLevel.StorageProvider,
|
|
ExportFile.Pdf("Notenliste als PDF speichern",
|
|
$"Notenliste_{_vm.GroupLabel}_{_vm.SchoolYear.Replace('/', '-')}", pdf));
|
|
}
|
|
|
|
private void BuildColumns()
|
|
{
|
|
var grid = this.FindControl<DataGrid>("GradesGrid");
|
|
if (grid is null || _vm is null) return;
|
|
|
|
grid.Columns.Clear();
|
|
grid.Columns.Add(new DataGridTextColumn
|
|
{
|
|
Header = "Schüler",
|
|
Binding = new Binding("Name"),
|
|
Width = new DataGridLength(160, DataGridLengthUnitType.Pixel),
|
|
});
|
|
|
|
foreach (var (col, i) in _vm.Columns.Select((c, i) => (c, i)))
|
|
{
|
|
var idx = i;
|
|
var column = new DataGridTextColumn
|
|
{
|
|
Header = col.Header,
|
|
Binding = new Binding($"Cells[{idx}]"),
|
|
Width = new DataGridLength(120, DataGridLengthUnitType.Pixel),
|
|
};
|
|
// Zwischenstände sind vorläufig — grau, damit sie nicht wie eingetragene Noten wirken.
|
|
if (col.IsInterim) column.Foreground = Avalonia.Media.Brushes.Gray;
|
|
grid.Columns.Add(column);
|
|
}
|
|
|
|
grid.Columns.Add(new DataGridTextColumn
|
|
{
|
|
Header = "Gesamt",
|
|
Binding = new Binding("TotalDisplay"),
|
|
Width = new DataGridLength(80, DataGridLengthUnitType.Pixel),
|
|
});
|
|
}
|
|
}
|