forked from amkovkov/GranuSightSoftware2
335 lines
13 KiB
C#
335 lines
13 KiB
C#
using System.Collections.ObjectModel;
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
using Avalonia.Threading;
|
|
|
|
using GSS2.Core.Hardware;
|
|
using GSS2.Core.Analysis.AmineContent;
|
|
using GSS2.Core.Analysis.AmineContent.Database;
|
|
|
|
using GSS2.UI.Core.ViewModels;
|
|
using GSS2.UI.Core.Services;
|
|
|
|
namespace GSS2.ViewModels.AmineContent;
|
|
|
|
public partial class ExportViewModel : ViewModelBase
|
|
{
|
|
public partial class UsbDiskViewModel : ViewModelBase
|
|
{
|
|
[ObservableProperty] public partial string Id { get; set; }
|
|
[ObservableProperty] public partial string Name { get; set; }
|
|
[ObservableProperty] public partial bool Mounted { get; set; }
|
|
[ObservableProperty] public partial string? MountPath { get; set; }
|
|
|
|
public UsbDiskViewModel(string id, string name, bool mounted, string mountPath)
|
|
{
|
|
Id = id;
|
|
Name = name;
|
|
Mounted = mounted;
|
|
MountPath = mountPath;
|
|
}
|
|
}
|
|
|
|
public partial class RecordListViewModel : ViewModelBase
|
|
{
|
|
[ObservableProperty] public partial int Id { get; set; }
|
|
[ObservableProperty] public partial string SampleName { get; set; }
|
|
|
|
public RecordListViewModel(int id, string sampleName)
|
|
{
|
|
Id = id;
|
|
SampleName = sampleName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Логгер
|
|
/// </summary>
|
|
private readonly ILogger<ExportViewModel> _logger;
|
|
/// <summary>
|
|
/// Контекст базы данных
|
|
/// </summary>
|
|
private readonly Context _context;
|
|
/// <summary>
|
|
/// Сервис дисков
|
|
/// </summary>
|
|
private readonly UsbService _usb;
|
|
|
|
/// <summary>
|
|
/// Диск смонтирован
|
|
/// </summary>
|
|
[ObservableProperty] public partial bool DiskMounted { get; set; } = false;
|
|
/// <summary>
|
|
/// Список дисков
|
|
/// </summary>
|
|
[ObservableProperty] public partial ObservableCollection<UsbDiskViewModel> Disks { get; set; }
|
|
/// <summary>
|
|
/// Индекс выбранного диска
|
|
/// </summary>
|
|
[ObservableProperty] public partial int DisksSelectedIndex { get; set; } = -1;
|
|
/// <summary>
|
|
/// Записи
|
|
/// </summary>
|
|
[ObservableProperty] public partial ObservableCollection<RecordListViewModel> Records { get; set; }
|
|
/// <summary>
|
|
/// Выбранные для экспорта записи
|
|
/// </summary>
|
|
[ObservableProperty] public partial ObservableCollection<RecordListViewModel> SelectedRecords { get; set; } = new();
|
|
/// <summary>
|
|
/// Путь точки монтирования
|
|
/// </summary>
|
|
[ObservableProperty] public partial string? DiskMountPath { get; set; } = null;
|
|
/// <summary>
|
|
/// Оповещение отображено
|
|
/// </summary>
|
|
[ObservableProperty] public partial bool IsNotificationVisible { get; set; } = false;
|
|
/// <summary>
|
|
/// Текст оповещения
|
|
/// </summary>
|
|
[ObservableProperty] public partial string NotificationMessage { get; set; } = string.Empty;
|
|
/// <summary>
|
|
/// Оповещение об удаче
|
|
/// </summary>
|
|
[ObservableProperty] public partial bool IsSuccessNotification { get; set; } = false;
|
|
|
|
public ExportViewModel(
|
|
ILogger<ExportViewModel> logger,
|
|
Context context,
|
|
UsbService usb
|
|
)
|
|
{
|
|
// Устанавливаем readonly поля
|
|
_logger = logger;
|
|
_context = context;
|
|
_usb = usb;
|
|
_logger.LogInformation("Инициализация");
|
|
|
|
// Получаем список дисков
|
|
Disks = new();
|
|
RefreshDisks();
|
|
|
|
// Получаем список результатов
|
|
Records = new ObservableCollection<RecordListViewModel>(_context.ResultRecords.Select(r => new RecordListViewModel(r.Id, r.SampleName)));
|
|
|
|
// Подписываемся на изменения базы данных для отслеживания изменений списка результатов
|
|
_context.ChangeTracker.StateChanged += (_, ea) =>
|
|
{
|
|
// Обновляем данные в интерфейсе
|
|
// Dispatcher.UIThread.Invoke нужен потому-что этот метод может (и скорее всего будет) вызван в другом потоке,
|
|
// а изменения данных в интерфейсе могут быть вызваны только из потока в котором интерфейс был создан
|
|
_ = Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
// Если запись является ResultRecord
|
|
if (ea.Entry.Entity is ResultRecord resultRecord)
|
|
{
|
|
// Если запись добавлена
|
|
if (ea.OldState is EntityState.Added && ea.NewState is EntityState.Unchanged)
|
|
{
|
|
// Добавляем запись
|
|
Records.Add(new RecordListViewModel(resultRecord.Id, resultRecord.SampleName));
|
|
}
|
|
// Если запись удалена
|
|
else if (ea.NewState is EntityState.Deleted)
|
|
{
|
|
// Ищем удалённую запись в списке
|
|
var deletedItem = Records.FirstOrDefault(r => r.Id == resultRecord.Id);
|
|
// Если запись не найдена в списке, то логгируем как ошибку
|
|
if (deletedItem is null)
|
|
{
|
|
_logger.LogError("Не удалось найти элемент с Id({}) в списке {}", resultRecord.Id, nameof(Records));
|
|
return;
|
|
}
|
|
// Если удаляемый элемент выбран, то удаляем его из выбранных
|
|
if (SelectedRecords.Contains(deletedItem))
|
|
SelectedRecords.Remove(deletedItem);
|
|
// Удаляем элемент из списка
|
|
Records.Remove(deletedItem);
|
|
}
|
|
// Если запись изменилась
|
|
else if (ea.NewState is EntityState.Modified)
|
|
{
|
|
// Ищем изменённую запись в списке
|
|
var editedItem = Records.FirstOrDefault(r => r.Id == resultRecord.Id);
|
|
// Если запись не найдена в списке, то логгируем как ошибку
|
|
if (editedItem is null)
|
|
{
|
|
_logger.LogError("Не удалось найти элемент с Id({}) в списке {}", resultRecord.Id, nameof(Records));
|
|
return;
|
|
}
|
|
editedItem.SampleName = resultRecord.SampleName;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
_logger.LogInformation("Инициализировано");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Обновить список дисков
|
|
/// </summary>
|
|
[RelayCommand]
|
|
private void RefreshDisks()
|
|
{
|
|
Disks.Clear();
|
|
var disks = _usb.GetDevices();
|
|
|
|
disks.ForEach(d => Disks.Add(new UsbDiskViewModel(d.id, d.name, d.mounted, d.mountPath)));
|
|
if (disks.Count() == 0 ||
|
|
DisksSelectedIndex >= disks.Count()
|
|
)
|
|
DisksSelectedIndex = -1;
|
|
if (DisksSelectedIndex == -1 &&
|
|
disks.Count() != 0
|
|
)
|
|
DisksSelectedIndex = 0;
|
|
|
|
}
|
|
/// <summary>
|
|
/// Смонтировать диск
|
|
/// </summary>
|
|
[RelayCommand]
|
|
private void MountDisk()
|
|
{
|
|
if (DiskMounted)
|
|
{
|
|
_logger.LogError("Диск уже смонтирован");
|
|
RefreshDisks();
|
|
return;
|
|
}
|
|
if (DisksSelectedIndex < 0 || DisksSelectedIndex >= Disks.Count())
|
|
{
|
|
_logger.LogError("Выбранный диск вне дисапзона");
|
|
RefreshDisks();
|
|
return;
|
|
}
|
|
|
|
var disk = Disks[DisksSelectedIndex];
|
|
if (disk.Mounted)
|
|
{
|
|
_logger.LogWarning("Выбранный диск уже смонтирован");
|
|
DiskMountPath = disk.MountPath;
|
|
DiskMounted = DiskMountPath is not null;
|
|
disk.Mounted = DiskMountPath is not null;
|
|
RefreshDisks();
|
|
}
|
|
else
|
|
{
|
|
DiskMountPath = _usb.MountDevice(disk.Id);
|
|
DiskMounted = DiskMountPath is not null;
|
|
disk.Mounted = DiskMountPath is not null;
|
|
RefreshDisks();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Размонтировать диск
|
|
/// </summary>
|
|
[RelayCommand]
|
|
private void UnmountDisk()
|
|
{
|
|
if (!DiskMounted)
|
|
{
|
|
_logger.LogError("Диск не смонтирован");
|
|
return;
|
|
}
|
|
if (DisksSelectedIndex < 0 || DisksSelectedIndex >= Disks.Count())
|
|
{
|
|
_logger.LogError("Выбранный диск вне дисапзона");
|
|
return;
|
|
}
|
|
|
|
var disk = Disks[DisksSelectedIndex];
|
|
if (!disk.Mounted)
|
|
{
|
|
_logger.LogError("Выбранный диск не смонтирован");
|
|
return;
|
|
}
|
|
var result = _usb.UnmountDevice(disk.Id);
|
|
if (result)
|
|
{
|
|
DiskMountPath = null;
|
|
DiskMounted = false;
|
|
disk.Mounted = false;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Экспортировать выбранные результаты в csv
|
|
/// </summary>
|
|
[RelayCommand]
|
|
private void ExportCsv()
|
|
{
|
|
if (!DiskMounted || DiskMountPath is null)
|
|
{
|
|
_logger.LogError("Диск не смонтирован");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var fileName = $"{DateTime.Now:yyyyMMdd_HHmmss}.csv";
|
|
var exportPath = Path.Join(DiskMountPath, fileName);
|
|
var exportingRecords = SelectedRecords.Select(r => _context.ResultRecords.Find(r.Id));
|
|
if (exportingRecords.Count() == 0)
|
|
{
|
|
_logger.LogWarning("Не выбраны записи для экспорта");
|
|
ShowNotification("Не выбраны записи для экспорта", false);
|
|
return;
|
|
}
|
|
_logger.LogInformation("Количество записей для экспорта: {}", exportingRecords.Count());
|
|
ResultExporter.ExportCsv(exportPath, exportingRecords);
|
|
_logger.LogInformation("Результаты экспортированны в файл: {}", exportPath);
|
|
ShowNotification($"Файл успешно экспортирован:\n{fileName}", true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Исключение во время экспорта результатов");
|
|
ShowNotification("Ошибка при экспорте файла", false);
|
|
UnmountDisk();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Запкрыть оповещение
|
|
/// </summary>
|
|
[RelayCommand]
|
|
private void DismissNotification()
|
|
{
|
|
IsNotificationVisible = false;
|
|
}
|
|
/// <summary>
|
|
/// Экспортировать выбранные результаты в pdf
|
|
/// </summary>
|
|
// [RelayCommand]
|
|
// private void ExportPdf()
|
|
// {
|
|
// if (!DiskMounted || MountPath is null)
|
|
// {
|
|
// _logger.LogError("Диск не смонтирован");
|
|
// return;
|
|
// }
|
|
|
|
// try
|
|
// {
|
|
// var exportPath = Path.Join(MountPath, $"{DateTime.Now:ddMMyyyy_hhmmss}.csv");
|
|
// var exportingRecords = SelectedRecords.Select(r => _context.Find(r.Id));
|
|
// GSS2.Core.RecordsExporter.ExportPdf(exportPath, exportingRecords);
|
|
// }
|
|
// catch (Exception ex)
|
|
// {
|
|
// _logger.LogError(ex, "Исключение во время экспорта результатов");
|
|
// }
|
|
// }
|
|
|
|
private void ShowNotification(string message, bool isSuccess)
|
|
{
|
|
NotificationMessage = message;
|
|
IsSuccessNotification = isSuccess;
|
|
IsNotificationVisible = true;
|
|
Task.Delay(5000).ContinueWith(_ => IsNotificationVisible = false);
|
|
}
|
|
}
|