348 lines
13 KiB
C#
348 lines
13 KiB
C#
using System.Globalization;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
using Livia.Models;
|
|
using Livia.Models.Data;
|
|
using Livia.Properties;
|
|
using Livia.Utility;
|
|
using Livia.Utility.DependencyInjection;
|
|
using Livia.Views.Utility;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Livia.ViewModels;
|
|
|
|
public class MainWindowViewModel : ObservableRecipient, IDisposable, IRecipient<LogoutMessage>
|
|
{
|
|
public int TransitionerSelectedIndex
|
|
{
|
|
get
|
|
{
|
|
if (_versionUpdateManager.Updating)
|
|
return 2;
|
|
|
|
if (_underMaintenance)
|
|
return 3;
|
|
|
|
return ServerHandler.IsLoggedIn switch
|
|
{
|
|
true => 1,
|
|
_ => 0
|
|
};
|
|
}
|
|
}
|
|
public bool DialogOpen { get => _dialogOpen; set => SetProperty(ref _dialogOpen, value); }
|
|
public string? MaintenanceMessage { get => _maintenanceMessage; private set => SetProperty(ref _maintenanceMessage, value); }
|
|
public static bool ConfirmExiting { get; set; } = true;
|
|
public static bool ShowHistoryButton { get; set; } = true;
|
|
public static bool ShowRemainingQuota { get; set; } = true;
|
|
public static bool ShowLogoutButton { get; set; } = true;
|
|
public static bool ShowLoadDataButton { get; set; } = true;
|
|
public static bool ShowExportReportButton { get; set; } = true;
|
|
public static bool ShowCheckReportsButton { get; set; } = true;
|
|
public static bool ShowSearchButton { get; set; } = true;
|
|
public static bool HideTitleBar { get; set; }
|
|
public static bool ClearFilesOnExit { get; set; } = true;
|
|
|
|
public IServerHandler ServerHandler { get; }
|
|
public IPatientInfoManager PatientInfoManager { get; }
|
|
public IDataBlockLoader DataBlockLoader { get; }
|
|
public DataBlockTableViewModel JobListTableViewModel { get; }
|
|
public DataBlockTableViewModel SearchResultTableViewModel { get; }
|
|
public SearchQueryInputControlViewModel SearchQueryViewModel { get; }
|
|
|
|
private DataBlock? _currentProcessingDataBlock;
|
|
|
|
private bool _dialogOpen;
|
|
private bool _underMaintenance;
|
|
private string? _maintenanceMessage;
|
|
private readonly ILogger _logger;
|
|
private readonly IWarningSystem _warningSystem;
|
|
private readonly IVersionUpdateManager _versionUpdateManager;
|
|
|
|
public MainWindowViewModel(ILogger logger, IWarningSystem warningSystem, IServerHandler serverHandler, IVersionUpdateManager versionUpdateManager, IPatientInfoManager patientInfoManager, IDataBlockLoader dataBlockLoader)
|
|
{
|
|
_warningSystem = warningSystem;
|
|
ServerHandler = serverHandler;
|
|
_versionUpdateManager = versionUpdateManager;
|
|
PatientInfoManager = patientInfoManager;
|
|
DataBlockLoader = dataBlockLoader;
|
|
_logger = logger;
|
|
|
|
JobListTableViewModel = ActivatorUtilities.GetServiceOrCreateInstance<DataBlockTableViewModel>(ServiceProviderFactory.ServiceProvider);
|
|
SearchResultTableViewModel = ActivatorUtilities.GetServiceOrCreateInstance<DataBlockTableViewModel>(ServiceProviderFactory.ServiceProvider);
|
|
SearchResultTableViewModel.SetSearchResult();
|
|
SearchQueryViewModel = ActivatorUtilities.GetServiceOrCreateInstance<SearchQueryInputControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
|
|
|
if (!Settings.Default.ApplicationQuitNormally)
|
|
{
|
|
_warningSystem.ShowDialog(WarningWindowKind.Warning, false, "ApplicationDidNotQuitNormallyWarning");
|
|
}
|
|
Settings.Default.ApplicationQuitNormally = false;
|
|
Settings.Default.Save();
|
|
_warningSystem = warningSystem;
|
|
|
|
_versionUpdateManager.CheckForUpdate(false).ContinueWith(_ => InstallUpdate());
|
|
|
|
//update TransitionerSelectedIndex
|
|
_versionUpdateManager.PropertyChanged += (_, args) =>
|
|
{
|
|
if (args.PropertyName != nameof(IVersionUpdateManager.Updating)) return;
|
|
OnPropertyChanged(nameof(TransitionerSelectedIndex));
|
|
};
|
|
|
|
ServerHandler.PropertyChanged += (_, args) =>
|
|
{
|
|
if (args.PropertyName != nameof(IServerHandler.IsLoggedIn)) return;
|
|
OnPropertyChanged(nameof(TransitionerSelectedIndex));
|
|
};
|
|
|
|
//create temp folder
|
|
Directory.CreateDirectory(ServiceConfigurations.TempFolder);
|
|
WeakReferenceMessenger.Default.RegisterAll(this);
|
|
|
|
//get server notice
|
|
Task.Run(UpdateNotice);
|
|
}
|
|
|
|
private async Task UpdateNotice()
|
|
{
|
|
ServerNotice? notice = await ServerHandler.GetNotice().ConfigureAwait(false);
|
|
if (notice == null)
|
|
return;
|
|
|
|
_underMaintenance = notice.Value.ServerStatus == 0;
|
|
if (_underMaintenance)
|
|
{
|
|
MaintenanceMessage = notice.Value.Content;
|
|
OnPropertyChanged(nameof(TransitionerSelectedIndex));
|
|
}
|
|
else if (Settings.Default.LastReadNoticeId != notice.Value.Id)
|
|
{
|
|
_warningSystem.ShowDialogString(WarningWindowKind.Info, false, notice.Value.Content);
|
|
Settings.Default.LastReadNoticeId = notice.Value.Id;
|
|
}
|
|
}
|
|
|
|
private void InstallUpdate()
|
|
{
|
|
if (!_versionUpdateManager.UpdateAvailable)
|
|
return;
|
|
|
|
bool updateNow = _warningSystem.ShowDialog(WarningWindowKind.Info, true, "NewVersionAvailableInfoMessage") ?? false;
|
|
if (updateNow)
|
|
{
|
|
Task.Run(_versionUpdateManager.UpdateApp);
|
|
}
|
|
}
|
|
|
|
public async Task ProcessData(string path, CancellationToken token)
|
|
{
|
|
_currentProcessingDataBlock = new DataBlock(path);
|
|
_logger.LogInformation("Creating data block from {path}", path);
|
|
(bool success, string messageIndex) = await ServerHandler.ProcessData(_currentProcessingDataBlock, token).ConfigureAwait(false);
|
|
|
|
if (success)
|
|
{
|
|
//TODO::this is bad
|
|
await Application.Current.Dispatcher.Invoke(() => DataBlockLoader.LoadData(_currentProcessingDataBlock));
|
|
}
|
|
else
|
|
{
|
|
if (token.IsCancellationRequested)
|
|
{
|
|
_logger.LogInformation("Data processing canceled");
|
|
}
|
|
else
|
|
{
|
|
_warningSystem.ShowDialog(WarningWindowKind.Error, true, messageIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//from https://stackoverflow.com/questions/3625658/how-do-you-create-the-hash-of-a-folder-in-c
|
|
private static string CreateMd5ForFolder(string path)
|
|
{
|
|
// assuming you want to include nested folders
|
|
List<string> files = Directory.GetFiles(path, "*", SearchOption.AllDirectories).OrderBy(p => p).ToList();
|
|
|
|
MD5 md5 = MD5.Create();
|
|
|
|
for (int i = 0; i < files.Count; i++)
|
|
{
|
|
string file = files[i];
|
|
|
|
// hash path
|
|
string relativePath = file[(path.Length + 1)..];
|
|
byte[] pathBytes = Encoding.UTF8.GetBytes(relativePath.ToLower());
|
|
md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);
|
|
|
|
// hash contents
|
|
byte[] contentBytes = File.ReadAllBytes(file);
|
|
if (i == files.Count - 1)
|
|
md5.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
|
|
else
|
|
md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
|
|
}
|
|
|
|
return md5.Hash == null ? string.Empty : BitConverter.ToString(md5.Hash).Replace("-", "").ToLower();
|
|
}
|
|
|
|
public async Task ProcessDataCommandLine(Dictionary<string, string> argsDict, CancellationToken token)
|
|
{
|
|
string path = argsDict["input_dir"];
|
|
string hash;
|
|
try
|
|
{
|
|
hash = CreateMd5ForFolder(path);
|
|
Directory.CreateDirectory(argsDict["output_dir"]);
|
|
Directory.CreateDirectory(argsDict["log_dir"]);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Error creating MD5 for {dir}", path);
|
|
_warningSystem.ShowDialog(WarningWindowKind.Error, true, "PathNotFoundError");
|
|
return;
|
|
}
|
|
|
|
_logger.LogInformation("Folder hash = {hash}", hash);
|
|
if (hash == string.Empty)
|
|
{
|
|
_warningSystem.ShowDialog(WarningWindowKind.Error, true, "ServerErrorUnknown");
|
|
Application.Current.Dispatcher.Invoke(Application.Current.Shutdown);
|
|
return;
|
|
}
|
|
|
|
_currentProcessingDataBlock = new DataBlock(path)
|
|
{
|
|
Key = hash,
|
|
ArgsDict = argsDict
|
|
};
|
|
string resultPath = Path.Combine(ServiceConfigurations.TempFolder, _currentProcessingDataBlock.Key);
|
|
if (File.Exists(Path.Join(resultPath, "complete")))
|
|
{
|
|
//cache exists, load them
|
|
_currentProcessingDataBlock.ResultPath = Path.Join(resultPath, "result");
|
|
//TODO::this is bad
|
|
await Application.Current.Dispatcher.Invoke(() => DataBlockLoader.LoadData(_currentProcessingDataBlock)).ConfigureAwait(false);
|
|
return;
|
|
}
|
|
|
|
_logger.LogInformation("Creating data block from {path}", path);
|
|
(bool success, string messageIndex) = await ServerHandler.ProcessData(_currentProcessingDataBlock, token).ConfigureAwait(false);
|
|
|
|
if (success)
|
|
{
|
|
//TODO::this is bad
|
|
await Application.Current.Dispatcher.Invoke(() => DataBlockLoader.LoadData(_currentProcessingDataBlock)).ConfigureAwait(false);
|
|
}
|
|
else
|
|
{
|
|
if (token.IsCancellationRequested)
|
|
{
|
|
_logger.LogInformation("Data processing canceled");
|
|
}
|
|
else
|
|
{
|
|
_warningSystem.ShowDialog(WarningWindowKind.Warning, true, messageIndex);
|
|
}
|
|
Application.Current.Dispatcher.Invoke(Application.Current.Shutdown);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (ServerHandler.Processing && _currentProcessingDataBlock != null)
|
|
{
|
|
ServerHandler.Cancel(_currentProcessingDataBlock.Key);
|
|
}
|
|
|
|
//remove everything from temp folder
|
|
if (ClearFilesOnExit && Directory.Exists(ServiceConfigurations.TempFolder))
|
|
{
|
|
foreach (string file in Directory.GetFiles(ServiceConfigurations.TempFolder))
|
|
{
|
|
try
|
|
{
|
|
File.Delete(file);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Cannot delete file: {file}", file);
|
|
}
|
|
}
|
|
|
|
foreach (string dir in Directory.GetDirectories(ServiceConfigurations.TempFolder))
|
|
{
|
|
try
|
|
{
|
|
Directory.Delete(dir, true);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Cannot delete directory: {file}", dir);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_logger.LogInformation("TempFolder does not exist, skipping");
|
|
}
|
|
|
|
//clear old logs
|
|
if (ClearFilesOnExit && Directory.Exists(ServiceConfigurations.TempFolder))
|
|
{
|
|
TimeSpan threshold = TimeSpan.FromDays(30);
|
|
string[] files = Directory.GetFiles(ServiceProviderFactory.LogFolder);
|
|
foreach (string file in files)
|
|
{
|
|
try
|
|
{
|
|
string fileName = Path.GetFileNameWithoutExtension(file);
|
|
string timeString = Regex.Match(fileName, @"\d+").Value;
|
|
DateTime logDate = DateTime.ParseExact(timeString, "yyyyMMdd", CultureInfo.InvariantCulture);
|
|
if (DateTime.Now - logDate < threshold)
|
|
continue;
|
|
|
|
File.Delete(file);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Cannot process file: {file}", file);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_logger.LogInformation("LogFolder does not exist, skipping");
|
|
}
|
|
|
|
Settings.Default.ApplicationQuitNormally = true;
|
|
Settings.Default.Save();
|
|
}
|
|
|
|
public bool ConfirmClosing()
|
|
{
|
|
if (!ConfirmExiting)
|
|
return true;
|
|
|
|
return _warningSystem.ShowDialog(WarningWindowKind.Confirmation, true, "ConfirmExitWarning") ?? true;
|
|
}
|
|
|
|
public bool ConfirmLogout()
|
|
{
|
|
return _warningSystem.ShowDialog(WarningWindowKind.Confirmation, true, "ConfirmLogoutWarning") ?? false;
|
|
}
|
|
|
|
public void Receive(LogoutMessage message)
|
|
{
|
|
DialogOpen = false;
|
|
_logger.LogInformation("Logging out");
|
|
}
|
|
} |