Merge branch 'Release/TaskExecMan01'

This commit is contained in:
Samuele Locatelli
2024-10-25 19:04:26 +02:00
57 changed files with 2901 additions and 218 deletions
+1
View File
@@ -0,0 +1 @@
{"_type":"export","__export_format":4,"__export_date":"2024-10-23T15:46:37.409Z","__export_source":"insomnia.desktop.app:v2023.5.8","resources":[{"_id":"req_d6789dcf78d94816bbd8572ed8e50c32","parentId":"wrk_e3f940b0e7364ede9659ddc904ed76da","modified":1680674272654,"created":1680674272654,"url":"https://localhost:7212/api/Recipe/GetRecipe?idxPODL=1656","name":"New Request","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1680616613017,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"wrk_e3f940b0e7364ede9659ddc904ed76da","parentId":null,"modified":1680674272619,"created":1680674272619,"name":"MP-IOC","description":"","scope":"collection","_type":"workspace"},{"_id":"req_0990f1f8b8054c6aa775c08233beb68a","parentId":"wrk_e3f940b0e7364ede9659ddc904ed76da","modified":1680674272646,"created":1680674272646,"url":"{{ _.BASE_URL }}/api/Recipe/GetRecipe?idxPODL=1656","name":"GET RECIPE (calc)","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1680615241541.5,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_86ef875060b240979a923807d5726572","parentId":"wrk_e3f940b0e7364ede9659ddc904ed76da","modified":1680674272651,"created":1680674272651,"url":"{{ BASE_URL }}/api/RecipeArchive/GetFile?idxMacc=SIMUL_02&fileName=10002.xml","name":"GET FILE (from archive)","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1679846511868.4375,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_80ed4f0bb8ac463b90112c98d457c478","parentId":"wrk_e3f940b0e7364ede9659ddc904ed76da","modified":1680679124960,"created":1680679120652,"url":"{{ BASE_URL }}/api/RecipeArchive/GetFile?idxMacc=SIMUL_02&fileName=999.xml","name":"GET FILE - not exists","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1679462147031.9062,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"env_ca021599ba8443c1a79a2eb69aa92128","parentId":"wrk_e3f940b0e7364ede9659ddc904ed76da","modified":1680674272620,"created":1680674272620,"name":"Base Environment","data":{},"dataPropertyOrder":{},"color":null,"isPrivate":false,"metaSortKey":1680615228640,"_type":"environment"},{"_id":"jar_8fed13ca3e9a477dad1b0a6699c43b70","parentId":"wrk_e3f940b0e7364ede9659ddc904ed76da","modified":1680674272634,"created":1680674272634,"name":"Default Jar","cookies":[],"_type":"cookie_jar"},{"_id":"spc_aaf61ce728e747a89c242f3c012bb23c","parentId":"wrk_e3f940b0e7364ede9659ddc904ed76da","modified":1680674272662,"created":1680674272637,"fileName":"MP-IOC","contents":"","contentType":"yaml","_type":"api_spec"},{"_id":"env_b5e3d19ecc18458b96877ecc73b04729","parentId":"env_ca021599ba8443c1a79a2eb69aa92128","modified":1680679494694,"created":1680674272621,"name":"DEV","data":{"BASE_URL":"https://localhost:7050"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#ff0000","isPrivate":false,"metaSortKey":1680615301619,"_type":"environment"},{"_id":"env_89e83800dd9d435ebe6745b4f80b2082","parentId":"env_ca021599ba8443c1a79a2eb69aa92128","modified":1680674621408,"created":1680674272628,"name":"IIS01","data":{"BASE_URL":"https://iis01.egalware.com/MP/IOC"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#6600ff","isPrivate":false,"metaSortKey":1680615328099,"_type":"environment"}]}
+1
View File
@@ -0,0 +1 @@
{"_type":"export","__export_format":4,"__export_date":"2024-10-23T15:45:49.150Z","__export_source":"insomnia.desktop.app:v2023.5.8","resources":[{"_id":"req_bb23a3e0a78f44999350ddcefebaaf82","parentId":"wrk_0d1a3f6f42fe486dacbeeea3026c648b","modified":1729691634502,"created":1729691518776,"url":"{{ _.BASE_URL }}/api/Health","name":"Health","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1680616613017,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"wrk_0d1a3f6f42fe486dacbeeea3026c648b","parentId":null,"modified":1729691518738,"created":1729691518738,"name":"MAPO-PROG","description":"","scope":"collection","_type":"workspace"},{"_id":"req_2b3ff677e75040ed9dfe30463fb5d408","parentId":"fld_cf0e8b6c31334d67bc421487d31a5540","modified":1729691689274,"created":1729691666872,"url":"{{ _.BASE_URL }}/api/FileChange","name":"FileChange Health","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1729691677997,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"fld_cf0e8b6c31334d67bc421487d31a5540","parentId":"wrk_0d1a3f6f42fe486dacbeeea3026c648b","modified":1729691675552,"created":1729691673364,"name":"FileChange","description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1680616270148.125,"_type":"request_group"},{"_id":"req_8fa584b2405b4c3588e1da50c6c02cbc","parentId":"fld_cf0e8b6c31334d67bc421487d31a5540","modified":1729691870054,"created":1729691818717,"url":"{{ _.BASE_URL }}/api/FileChange/approve/ALL?numDayPrev=7","name":"FileChange AutoSave","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1726323968489,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_b28dbcb90b9f4a30ae621f46ca80abf1","parentId":"fld_cf0e8b6c31334d67bc421487d31a5540","modified":1729698051601,"created":1729698043734,"url":"{{ _.BASE_URL }}/api/FileChange/check/ALL?numDayPrev=0","name":"FileChange CheckAll","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1724640113735,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"env_f533a867e5e74bc7a712974eb5185fe2","parentId":"wrk_0d1a3f6f42fe486dacbeeea3026c648b","modified":1729691518740,"created":1729691518740,"name":"Base Environment","data":{},"dataPropertyOrder":{},"color":null,"isPrivate":false,"metaSortKey":1680615228640,"_type":"environment"},{"_id":"jar_52975036fdb946749a04bf8b105649dc","parentId":"wrk_0d1a3f6f42fe486dacbeeea3026c648b","modified":1729691518752,"created":1729691518752,"name":"Default Jar","cookies":[],"_type":"cookie_jar"},{"_id":"spc_034d8153209c43289ef94bb511a381e4","parentId":"wrk_0d1a3f6f42fe486dacbeeea3026c648b","modified":1729691518782,"created":1729691518757,"fileName":"MAPO-PROG","contents":"","contentType":"yaml","_type":"api_spec"},{"_id":"env_6acd2ea246a54f2fa42dfa7191704e4f","parentId":"env_f533a867e5e74bc7a712974eb5185fe2","modified":1729691571662,"created":1729691518742,"name":"DEV","data":{"BASE_URL":"https://localhost:5001"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#ff0000","isPrivate":false,"metaSortKey":1680615301619,"_type":"environment"},{"_id":"env_add3f929a9774e949eaacdfef9669698","parentId":"env_f533a867e5e74bc7a712974eb5185fe2","modified":1729691576277,"created":1729691518746,"name":"IIS01","data":{"BASE_URL":"https://iis01.egalware.com/MP/PROG"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#6600ff","isPrivate":false,"metaSortKey":1680615328099,"_type":"environment"},{"_id":"env_78f831742ec84968878b6bb3e2dbbee3","parentId":"env_f533a867e5e74bc7a712974eb5185fe2","modified":1729691592437,"created":1729691579006,"name":"PROD","data":{"BASE_URL":"https://iis01.egalware.com/MP/PROG"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#6600ff","isPrivate":false,"metaSortKey":1692979703520,"_type":"environment"}]}
+1 -1
View File
@@ -1 +1 @@
{"_type":"export","__export_format":4,"__export_date":"2023-04-04T15:23:33.215Z","__export_source":"insomnia.desktop.app:v2023.1.0","resources":[{"_id":"req_cd57cb8728854cd5a7d026b9aba60047","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680616613886,"created":1680616613017,"url":"https://localhost:7212/api/Recipe/GetRecipe?idxPODL=1656","name":"New Request","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1680616613017,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","parentId":null,"modified":1680615228636,"created":1680615228636,"name":"MAPO-SPEC","description":"","scope":"collection","_type":"workspace"},{"_id":"req_4d233d6fb970485ea929bdffdcfae05a","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680621773464,"created":1680615246346,"url":"{{ _.BASE_URL }}/api/Recipe/GetRecipe?idxPODL=1656","name":"GET RECIPE (calc)","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1680615241541.5,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_278d5f0a6555457e88ad2e58cfdaae23","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680621754168,"created":1680615436433,"url":"{{ BASE_URL }}/api/RecipeArchive/GetFile?idxMacc=SIMUL_02&fileName=10002.xml","name":"GET FILE (from archive)","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1679846511868.4375,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"env_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680615377532,"created":1680615228640,"name":"Base Environment","data":{},"dataPropertyOrder":{},"color":null,"isPrivate":false,"metaSortKey":1680615228640,"_type":"environment"},{"_id":"jar_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680615228642,"created":1680615228642,"name":"Default Jar","cookies":[],"_type":"cookie_jar"},{"_id":"spc_b61936e963e34a759001c6a22ad8da60","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680615228637,"created":1680615228637,"fileName":"MAPO-SPEC","contents":"","contentType":"yaml","_type":"api_spec"},{"_id":"env_2757d8ba1b17435987847d0499e9369e","parentId":"env_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","modified":1680616993850,"created":1680615301619,"name":"DEV","data":{"BASE_URL":"https://localhost:7212"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#ff0000","isPrivate":false,"metaSortKey":1680615301619,"_type":"environment"},{"_id":"env_f5b6aef3c2b444aab04c9056404d63f9","parentId":"env_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","modified":1680616996428,"created":1680615328099,"name":"IIS01","data":{"BASE_URL":"https://iis01.egalware.com/MP/SPEC"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#6600ff","isPrivate":false,"metaSortKey":1680615328099,"_type":"environment"}]}
{"_type":"export","__export_format":4,"__export_date":"2024-10-23T15:46:09.187Z","__export_source":"insomnia.desktop.app:v2023.5.8","resources":[{"_id":"req_cd57cb8728854cd5a7d026b9aba60047","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680616613886,"created":1680616613017,"url":"https://localhost:7212/api/Recipe/GetRecipe?idxPODL=1656","name":"New Request","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1680616613017,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","parentId":null,"modified":1680615228636,"created":1680615228636,"name":"MAPO-SPEC","description":"","scope":"collection","_type":"workspace"},{"_id":"req_4d233d6fb970485ea929bdffdcfae05a","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680621773464,"created":1680615246346,"url":"{{ _.BASE_URL }}/api/Recipe/GetRecipe?idxPODL=1656","name":"GET RECIPE (calc)","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1680615241541.5,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_278d5f0a6555457e88ad2e58cfdaae23","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680621754168,"created":1680615436433,"url":"{{ BASE_URL }}/api/RecipeArchive/GetFile?idxMacc=SIMUL_02&fileName=10002.xml","name":"GET FILE (from archive)","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1679846511868.4375,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"env_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680615377532,"created":1680615228640,"name":"Base Environment","data":{},"dataPropertyOrder":{},"color":null,"isPrivate":false,"metaSortKey":1680615228640,"_type":"environment"},{"_id":"jar_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680615228642,"created":1680615228642,"name":"Default Jar","cookies":[],"_type":"cookie_jar"},{"_id":"spc_b61936e963e34a759001c6a22ad8da60","parentId":"wrk_86debe1a96ca41cdbfb53ed71a9d6d84","modified":1680615228637,"created":1680615228637,"fileName":"MAPO-SPEC","contents":"","contentType":"yaml","_type":"api_spec"},{"_id":"env_2757d8ba1b17435987847d0499e9369e","parentId":"env_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","modified":1680616993850,"created":1680615301619,"name":"DEV","data":{"BASE_URL":"https://localhost:7212"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#ff0000","isPrivate":false,"metaSortKey":1680615301619,"_type":"environment"},{"_id":"env_f5b6aef3c2b444aab04c9056404d63f9","parentId":"env_ea1cd34b6839260f9a1edf3546f3c605bb6f3a2c","modified":1680616996428,"created":1680615328099,"name":"IIS01","data":{"BASE_URL":"https://iis01.egalware.com/MP/SPEC"},"dataPropertyOrder":{"&":["BASE_URL"]},"color":"#6600ff","isPrivate":false,"metaSortKey":1680615328099,"_type":"environment"}]}
+10 -2
View File
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.AppAuth", "MP.AppAuth\MP
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Egw.Core", "Egw.Core\Egw.Core.csproj", "{D3D348EF-1313-43DF-94FB-28CD38B68212}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.Data", "MP.Data\MP.Data.csproj", "{EE871AE5-9B5E-493E-8E59-F77234979AD7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug_LiManDebug|Any CPU = Debug_LiManDebug|Any CPU
@@ -22,8 +24,8 @@ Global
{D949AB45-9B65-4594-A97E-182BC3831707}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D949AB45-9B65-4594-A97E-182BC3831707}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D949AB45-9B65-4594-A97E-182BC3831707}.Release|Any CPU.Build.0 = Release|Any CPU
{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Debug_LiManDebug|Any CPU.ActiveCfg = Debug|Any CPU
{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Debug_LiManDebug|Any CPU.Build.0 = Debug|Any CPU
{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Debug_LiManDebug|Any CPU.ActiveCfg = Debug_LiManDebug|Any CPU
{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Debug_LiManDebug|Any CPU.Build.0 = Debug_LiManDebug|Any CPU
{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -34,6 +36,12 @@ Global
{D3D348EF-1313-43DF-94FB-28CD38B68212}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3D348EF-1313-43DF-94FB-28CD38B68212}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3D348EF-1313-43DF-94FB-28CD38B68212}.Release|Any CPU.Build.0 = Release|Any CPU
{EE871AE5-9B5E-493E-8E59-F77234979AD7}.Debug_LiManDebug|Any CPU.ActiveCfg = Debug|Any CPU
{EE871AE5-9B5E-493E-8E59-F77234979AD7}.Debug_LiManDebug|Any CPU.Build.0 = Debug|Any CPU
{EE871AE5-9B5E-493E-8E59-F77234979AD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE871AE5-9B5E-493E-8E59-F77234979AD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE871AE5-9B5E-493E-8E59-F77234979AD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE871AE5-9B5E-493E-8E59-F77234979AD7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+1 -1
View File
@@ -10,7 +10,7 @@ namespace MP.AppAuth
{
#region Private Fields
private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
private static Logger Log = LogManager.GetCurrentClassLogger();
private IConfiguration _configuration;
+4
View File
@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;Debug_LiManDebug</Configurations>
</PropertyGroup>
<ItemGroup>
@@ -16,7 +17,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.3.3" />
<PackageReference Include="RestSharp" Version="112.0.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
</ItemGroup>
<ItemGroup>
@@ -1,7 +1,4 @@
using Egw.Core;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using MP.AppAuth.DTO;
using MP.AppAuth.Models;
using Newtonsoft.Json;
@@ -11,12 +8,12 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using static Egw.Core.LiManObj;
using static System.Net.Mime.MediaTypeNames;
namespace MP.Land.Data
namespace MP.AppAuth.Services
{
public class SyncService
{
@@ -31,15 +28,17 @@ namespace MP.Land.Data
_configuration = configuration;
// sistemo conf secondo Target compilazione
#if DEBUG_LIMANDEBUG
apiUrl = "https://localhost:5003/";
#else
apiUrl = "https://liman.egalware.com/ELM.API/";
#endif
// fix opzioni base del RestClient
restOptStd = new RestClientOptions { Timeout = TimeSpan.FromSeconds(60), BaseUrl = new Uri(apiUrl) };
restOptStd = new RestClientOptions
{
Timeout = TimeSpan.FromSeconds(60),
BaseUrl = new Uri(apiUrl)
};
}
#endregion Public Constructors
@@ -82,7 +81,7 @@ namespace MP.Land.Data
answ = response.Content.Replace("\"", "");
}
}
return await Task.FromResult(answ);
return answ;
}
/// <summary>
@@ -241,16 +240,20 @@ namespace MP.Land.Data
private static IConfiguration? _configuration;
/// <summary>
/// URL dell'API x chiamate gestione licenze
/// </summary>
private string apiUrl { get; set; } = "https://localhost:5003/";
/// <summary>
/// Classe logger
/// </summary>
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
#region Private Properties
/// <summary>
/// URL dell'API x chiamate gestione licenze
/// </summary>
private string apiUrl { get; set; } = "https://localhost:5003/";
/// <summary>
/// Conf client RestSharp standard:
/// - base URI al sito
@@ -258,6 +261,6 @@ namespace MP.Land.Data
/// </summary>
private RestClientOptions restOptStd { get; set; } = new RestClientOptions();
#endregion Private Fields
#endregion Private Properties
}
}
+474
View File
@@ -0,0 +1,474 @@
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MP.Data.DatabaseModels;
using MP.Data.Objects;
using NLog;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using static MP.Data.Objects.Enums;
namespace MP.Data.Controllers
{
public class MpLandController : IDisposable
{
#region Public Constructors
public MpLandController(IConfiguration configuration)
{
_configuration = configuration;
Log.Info("Avviato MpLandController");
}
#endregion Public Constructors
#region Public Methods
public DateTime CalcNextExe(TaskListModel taskRec)
{
DateTime dtNext = DateTime.Today;
try
{
// calcolo next exec da tipo...
switch (taskRec.Freq)
{
case TaskFreqType.ND:
dtNext = taskRec.DtLastExec.AddDays(taskRec.Cad);
break;
case TaskFreqType.Sec:
dtNext = taskRec.DtLastExec.AddSeconds(taskRec.Cad);
break;
case TaskFreqType.Min:
dtNext = taskRec.DtLastExec.AddMinutes(taskRec.Cad);
break;
case TaskFreqType.Hour:
dtNext = taskRec.DtLastExec.AddHours(taskRec.Cad);
break;
case TaskFreqType.Day:
dtNext = taskRec.DtLastExec.AddDays(taskRec.Cad);
break;
case TaskFreqType.Week:
dtNext = taskRec.DtLastExec.AddDays(7 * taskRec.Cad);
break;
case TaskFreqType.Month:
dtNext = taskRec.DtLastExec.AddMonths(taskRec.Cad);
break;
case TaskFreqType.Year:
dtNext = taskRec.DtLastExec.AddYears(taskRec.Cad);
break;
default:
dtNext = taskRec.DtLastExec.AddDays(taskRec.Cad);
break;
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in CalcNextExe{Environment.NewLine}{exc}");
}
return dtNext;
}
/// <summary>
/// Elenco da tabella Config
/// </summary>
/// <returns></returns>
public List<ConfigModel> ConfigGetAll()
{
List<ConfigModel> dbResult = new List<ConfigModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
dbResult = dbCtx
.DbSetConfig
.AsNoTracking()
.OrderBy(x => x.Chiave)
.ToList();
}
return dbResult;
}
public void Dispose()
{
_configuration = null;
}
/// <summary>
/// Elenco operatori
/// </summary>
/// <returns></returns>
public List<AnagOperatoriModel> ElencoOperatori()
{
List<AnagOperatoriModel> dbResult = new List<AnagOperatoriModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
dbResult = dbCtx
.DbOperatori
.Where(s => s.MatrOpr > 0)
.AsNoTracking()
.OrderBy(x => x.MatrOpr)
.ToList();
}
return dbResult;
}
/// <summary>
/// Chiamata esecuzione di un singolo task programmato
/// </summary>
/// <param name="TaskId"></param>
/// <param name="SchedNext">Se true rischedula successiva chiamata</param>
/// <returns></returns>
public TaskResultModel ExecuteSqlTask(int TaskId, bool SchedNext)
{
TaskResultModel callRes = new TaskResultModel();
using (var dbCtx = new MoonPro_STATSContext(_configuration))
{
// imposto timeout a 5 min
//var currTimeout = dbCtx.Database.GetCommandTimeout();
dbCtx.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
try
{
DateTime dtStart = DateTime.Now;
// recupero i dati da richiamare...
var currRec = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == TaskId)
.FirstOrDefault();
if (currRec != null)
{
// recupero comando
string sqlCommand = currRec.Command;
string rawParams = currRec.Args;
callRes = dbCtx
.DbSetTaskResult
.FromSqlRaw($"EXEC {sqlCommand} {rawParams}")
.AsNoTracking()
.AsEnumerable()
.FirstOrDefault();
DateTime dtEnd = DateTime.Now;
// preparo record esecuzione...
TaskExecModel resRec = new TaskExecModel()
{
TaskId = TaskId,
DtStart = dtStart,
DtEnd = dtEnd,
IsError = callRes.ExecResult < 0,
Result = callRes.TextResult
};
dbCtx
.DbSetTaskExe
.Add(resRec);
// aggiorno record chiamata...
currRec.DtLastExec = dtStart;
currRec.LastResult = resRec.Result;
currRec.LastIsError = resRec.IsError;
currRec.LastDuration = dtEnd.Subtract(dtStart).TotalSeconds;
// solo se richiesto rischedulazione ricalcola chiamata
if (SchedNext)
{
// calcolo prossima esecuzione...
currRec.DtNextExec = CalcNextExe(currRec);
}
// segno modificato
dbCtx.Entry(currRec).State = EntityState.Modified;
// salvo modifiche!
dbCtx.SaveChanges();
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in ExecuteSqlCommand{Environment.NewLine}{exc}");
}
}
return callRes;
}
/// <summary>
/// Annulla modifiche su una specifica entity (cancel update)
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool RollBackEntity(object item)
{
bool answ = false;
using (var dbCtx = new MoonPro_STATSContext(_configuration))
{
try
{
if (dbCtx.Entry(item).State == Microsoft.EntityFrameworkCore.EntityState.Deleted || dbCtx.Entry(item).State == Microsoft.EntityFrameworkCore.EntityState.Modified)
{
dbCtx.Entry(item).Reload();
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in rollBackEntity{Environment.NewLine}{exc}");
}
}
return answ;
}
/// <summary>
/// Ricerca task dato tipo + num max (desc)
/// </summary>
/// <param name="TaskId">TaskId da cui deriva</param>
/// <returns></returns>
public List<TaskExecModel> TaskExecGetFilt(int TaskId, int maxRec)
{
List<TaskExecModel> dbResult = new List<TaskExecModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
dbResult = dbCtx
.DbSetTaskExe
.Include(x => x.TaskListNav)
.Where(x => (x.TaskId == TaskId))
.OrderByDescending(x => x.DtStart)
.Take(maxRec)
.ToList();
}
return dbResult;
}
/// <summary>
/// Upsert record TaskExec
/// </summary>
/// <param name="rec2upd">Record da aggiornare/inserire</param>
/// <returns></returns>
public bool TaskExecUpsert(TaskExecModel rec2upd)
{
bool done = false;
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
var currData = dbCtx
.DbSetTaskExe
.Where(x => x.TaskExecId == rec2upd.TaskExecId)
.FirstOrDefault();
if (currData != null)
{
currData.TaskId = rec2upd.TaskId;
currData.DtStart = rec2upd.DtStart;
currData.DtEnd = rec2upd.DtEnd;
currData.IsError = rec2upd.IsError;
currData.Result = rec2upd.Result;
dbCtx.Entry(currData).State = EntityState.Modified;
}
else
{
dbCtx
.DbSetTaskExe
.Add(rec2upd);
}
dbCtx.SaveChanges();
done = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskExecUpsert{Environment.NewLine}{exc}");
}
}
return done;
}
/// <summary>
/// Ricerca task dato tipo e
/// </summary>
/// <param name="TType"></param>
/// <returns></returns>
public List<TaskListModel> TaskListGetAll(Task2ExeType TType)
{
List<TaskListModel> dbResult = new List<TaskListModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
dbResult = dbCtx
.DbSetTaskList
.Where(x => (TType == Task2ExeType.ND || x.TType == TType))
.OrderBy(x => x.Ordinal)
.ToList();
}
return dbResult;
}
/// <summary>
/// Esegue registrazione di un Task generico (NON SQL)
/// </summary>
/// <param name="TaskId"></param>
/// <param name="SchedNext">Se true rischedula successiva chiamata</param>
/// <param name="ResRec">Record esecuzione task esterno</param>
/// <returns></returns>
public TaskResultModel TaskExecSaveExecuted(int TaskId, bool SchedNext, TaskExecModel ResRec)
{
TaskResultModel callRes = new TaskResultModel();
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
// recupero i dati da richiamare...
var currRec = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == TaskId)
.FirstOrDefault();
if (currRec != null)
{
// registro task ricevuto
dbCtx
.DbSetTaskExe
.Add(ResRec);
// aggiorno record chiamata...
currRec.DtLastExec = ResRec.DtStart;
currRec.LastResult = ResRec.Result;
currRec.LastIsError = ResRec.IsError;
currRec.LastDuration = ResRec.DtEnd.Subtract(ResRec.DtStart).TotalSeconds;
// solo se richiesto rischedulazione ricalcola chiamata
if (SchedNext)
{
// calcolo prossima esecuzione...
currRec.DtNextExec = CalcNextExe(currRec);
}
// segno modificato
dbCtx.Entry(currRec).State = EntityState.Modified;
// salvo modifiche!
dbCtx.SaveChanges();
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskExecSaveExecuted{Environment.NewLine}{exc}");
}
}
return callRes;
}
/// <summary>
/// Update ordinamento task
/// </summary>
/// <param name="rec2upd">Record da spostare x priorità</param>
/// <returns></returns>
public bool TaskListMove(TaskListModel rec2upd, bool moveUp)
{
bool done = false;
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
var currData = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == rec2upd.TaskId)
.FirstOrDefault();
if (currData != null)
{
int actOrdinal = currData.Ordinal;
TaskListModel? otherRec = null;
// cerco, secondo richiesta, precedente o successivo
if (moveUp)
{
otherRec = dbCtx
.DbSetTaskList
.Where(x => x.Ordinal < currData.Ordinal)
.OrderByDescending(x => x.Ordinal)
.FirstOrDefault();
}
else
{
otherRec = dbCtx
.DbSetTaskList
.Where(x => x.Ordinal > currData.Ordinal)
.OrderBy(x => x.Ordinal)
.FirstOrDefault();
}
// inverto ordinale SE ho record
if (otherRec != null)
{
currData.Ordinal = otherRec.Ordinal;
otherRec.Ordinal = actOrdinal;
dbCtx.Entry(currData).State = EntityState.Modified;
dbCtx.Entry(otherRec).State = EntityState.Modified;
}
}
//salvo
dbCtx.SaveChanges();
done = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskListUpsert{Environment.NewLine}{exc}");
}
}
return done;
}
/// <summary>
/// Upsert record TaskList
/// </summary>
/// <param name="rec2upd">Record da aggiornare/inserire</param>
/// <returns></returns>
public bool TaskListUpsert(TaskListModel rec2upd)
{
bool done = false;
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
var currData = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == rec2upd.TaskId && rec2upd.TaskId > 0)
.FirstOrDefault();
if (currData != null)
{
currData.Ordinal = rec2upd.Ordinal;
currData.Name = rec2upd.Name;
currData.Descript = rec2upd.Descript;
currData.Command = rec2upd.Command;
currData.Args = rec2upd.Args;
currData.Freq = rec2upd.Freq;
currData.Cad = rec2upd.Cad;
currData.DtLastExec = rec2upd.DtLastExec;
currData.DtNextExec = rec2upd.DtNextExec;
currData.LastDuration = rec2upd.LastDuration;
currData.LastResult = rec2upd.LastResult;
dbCtx.Entry(currData).State = EntityState.Modified;
}
else
{
dbCtx
.DbSetTaskList
.Add(rec2upd);
}
dbCtx.SaveChanges();
done = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskListUpsert{Environment.NewLine}{exc}");
}
}
return done;
}
#endregion Public Methods
#region Private Fields
private static IConfiguration _configuration;
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
}
}
+1 -1
View File
@@ -541,7 +541,7 @@ namespace MP.Data.Controllers
}
/// <summary>
/// Ricerca task dato tipo e
/// Ricerca task dato tipo
/// </summary>
/// <param name="TType"></param>
/// <returns></returns>
+2
View File
@@ -34,6 +34,8 @@
<PackageReference Include="MongoDB.Driver" Version="2.19.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="RestSharp" Version="112.0.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
</ItemGroup>
</Project>
+9
View File
@@ -99,6 +99,11 @@ namespace MP.Data
public virtual DbSet<ParetoFluxLogDTO> DbSetParetoFluxLog { get; set; }
public virtual DbSet<TaskListModel> DbSetTaskList { get; set; }
public virtual DbSet<TaskExecModel> DbSetTaskExe { get; set; }
public virtual DbSet<TaskResultModel> DbSetTaskResult { get; set; }
#endregion Public Properties
#region Private Methods
@@ -122,6 +127,10 @@ namespace MP.Data
{
connString = _configuration.GetConnectionString("MP.STATS");
}
if (string.IsNullOrEmpty(connString))
{
connString = _configuration.GetConnectionString("MP.Land");
}
optionsBuilder.UseSqlServer(connString);
//optionsBuilder.UseSqlServer("Server=SQL2016DEV;Database=MoonPro;Trusted_Connection=True;");
+11 -1
View File
@@ -271,7 +271,17 @@ namespace MP.Data.Objects
/// <summary>
/// Chiamata a SQL Stored Procedure
/// </summary>
SqlStored
SqlStored,
/// <summary>
/// Chiamata REST tipo Get
/// </summary>
RestCallGet,
///// <summary>
///// Chiamata REST tipo Post
///// </summary>
//RestCallPost
}
//[JsonConverter(typeof(StringEnumConverter))]
+111
View File
@@ -0,0 +1,111 @@
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NLog;
using RestSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using static System.Net.Mime.MediaTypeNames;
namespace MP.AppAuth.Services
{
public class RestCallService
{
#region Public Constructors
/// <summary>
/// Init classe
/// </summary>
/// <param name="configuration"></param>
public RestCallService(IConfiguration configuration)
{
_configuration = configuration;
// verifico la url base
apiUrl = _configuration.GetValue<string>("ServerConf:Prog.ApiUrl");
// fix opzioni base del RestClient
restOptStd = new RestClientOptions
{
Timeout = TimeSpan.FromSeconds(60),
BaseUrl = new Uri(apiUrl)
};
}
private string apiUrl = "";
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Stato server da chiamare x verifiche preliminari stato API REST
/// </summary>
/// <param name="ApiUrl"></param>
/// <returns></returns>
public async Task<string> CheckServer()
{
string answ = "ND";
// cerco online
using (RestClient client = new RestClient(restOptStd))
{
var request = new RestRequest($"api/health", Method.Get);
var response = await client.GetAsync(request);
// controllo risposta
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
// verifico risposta
if (response.Content != null)
{
answ = response.Content.Replace("\"", "");
}
}
}
return answ;
}
/// <summary>
/// Effettua una generica chiamata ApiRest
/// </summary>
/// <param name="ApiUrl">URL Api di base</param>
/// <param name="ApiRequest">Richiesta completa</param>
/// <returns></returns>
public async Task<RestResponse> CallRestGet(string ApiUrl, string ApiRequest)
{
RestResponse response;
// cerco online
using (RestClient client = new RestClient(ApiUrl))
{
var request = new RestRequest(ApiRequest, Method.Get);
response = await client.GetAsync(request);
}
return response;
}
#endregion Public Methods
#region Private Fields
private static IConfiguration? _configuration;
/// <summary>
/// Classe logger
/// </summary>
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
#region Private Properties
/// <summary>
/// Conf client RestSharp standard:
/// - base URI al sito
/// - timeout 1 min
/// </summary>
private RestClientOptions restOptStd { get; set; } = new RestClientOptions();
#endregion Private Properties
}
}
+370
View File
@@ -0,0 +1,370 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using MP.AppAuth.Services;
using MP.Data.Controllers;
using MP.Data.DatabaseModels;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static MP.Data.Objects.Enums;
namespace MP.Data.Services
{
public class TaskService : BaseServ, IDisposable
{
#region Public Constructors
/// <summary>
/// Init servizio TAB
/// </summary>
/// <param name="configuration"></param>
public TaskService(IConfiguration configuration)
{
_configuration = configuration;
// setup compoenti REDIS
redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis"));
redisDb = redisConn.GetDatabase();
// conf DB
ConnStr = _configuration.GetConnectionString("MP.All");
if (string.IsNullOrEmpty(ConnStr))
{
Log.Error("ConnString empty!");
}
else
{
StringBuilder sb = new StringBuilder();
MLController = new MpLandController(configuration);
sb.AppendLine($"TaskService | MpLandController OK");
Log.Info(sb.ToString());
// sistemo i parametri x redHas...
CodModulo = _configuration.GetValue<string>("ServerConf:CodModulo");
var cstringArray = ConnStr.Split(";");
foreach (var item in cstringArray)
{
var cData = item.Trim().Split("=");
if (cData.Length == 2)
{
if (!connStrParams.ContainsKey(cData[0]))
{
connStrParams.Add(cData[0], cData[1]);
}
}
}
// sistemo
DataSource = connStrParams["Server"];
DataBase = connStrParams["Database"];
}
// conf rest call service
RCallService = new RestCallService(_configuration);
}
#endregion Public Constructors
#region Public Events
/// <summary>
/// Evento richiesta rilettura dati pagina (x refresh pagine aperte)
/// </summary>
public event EventHandler ReloadRequest = delegate { };
#endregion Public Events
#region Public Methods
public void Dispose()
{
// Clear database controller
MLController.Dispose();
// redis dispose
redisConn = null;
redisDb = null;
}
/// <summary>
/// Chiamata esecuzione di un singolo task programmato
/// </summary>
/// <param name="TaskRec">Task richiesto</param>
/// <param name="SchedNext">Se true rischedula successiva chiamata</param>
/// <returns></returns>
public async Task<TaskResultModel> ExecuteTask(TaskListModel TaskRec, bool SchedNext)
{
TaskResultModel answ = new TaskResultModel()
{
Task = $"TaskId: {TaskRec.TaskId} | {TaskRec.TType}",
ExecResult = -1,
TextResult = "Task Not recognized"
};
// verifico tipo di task ed eseguo di conseguenza...
switch (TaskRec.TType)
{
//case Task2ExeType.ND:
// break;
//case Task2ExeType.Exe:
// break;
//case Task2ExeType.SqlCommand:
// break;
case Task2ExeType.SqlStored:
answ = MLController.ExecuteSqlTask(TaskRec.TaskId, SchedNext);
break;
case Task2ExeType.RestCallGet:
DateTime dtStart = DateTime.Now;
// in primis testo la chiamata al servizio Health
string rAnsw = await RCallService.CheckServer();
// se ok effettuo vera chiamata...
if (rAnsw.ToUpper() == "OK")
{
var callResp = await RCallService.CallRestGet(TaskRec.Command, TaskRec.Args);
DateTime dtEnd = DateTime.Now;
TaskExecModel tExeMod = new TaskExecModel()
{
DtEnd = dtEnd,
DtStart = dtStart,
IsError = callResp.StatusCode != System.Net.HttpStatusCode.OK,
TaskId = TaskRec.TaskId,
Result = $"{callResp.Content}".Replace("\"", ""),
};
// salvo su DB
answ = MLController.TaskExecSaveExecuted(TaskRec.TaskId, SchedNext, tExeMod);
}
break;
default:
break;
}
// svuoto cache!
await FlushCache("Task");
return answ;
}
/// <summary>
/// Pulizia cache Redis (tutta)
/// </summary>
/// <returns></returns>
public async Task<bool> FlushCache()
{
RedisValue pattern = new RedisValue($"{redisBaseKey}:*");
bool answ = await ExecFlushRedisPattern(pattern);
return answ;
}
/// <summary>
/// Pulizia cache Redis per chiave specifica (da redisBaseKey...)
/// </summary>
/// <returns></returns>
public async Task<bool> FlushCache(string KeyReq)
{
RedisValue pattern = new RedisValue($"{redisBaseKey}:{KeyReq}:*");
bool answ = await ExecFlushRedisPattern(pattern);
return answ;
}
/// <summary>
/// Invio notifica rilettura (con parametro)
/// </summary>
/// <param name="message"></param>
public void NotifyReloadRequest(string message)
{
if (ReloadRequest != null)
{
// messaggio
ReloadEventArgs rea = new ReloadEventArgs(message);
ReloadRequest.Invoke(this, rea);
}
}
public void rollBackEdit(object item)
{
MLController.RollBackEntity(item);
}
/// <summary>
/// Ricerca task dato tipo + num max (desc)
/// </summary>
/// <param name="TaskId">TaskId da cui deriva</param>
/// <returns></returns>
public async Task<List<TaskExecModel>> TaskExecGetFilt(int TaskId, int maxRec, string searchVal)
{
// setup parametri costanti
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<TaskExecModel> result = new List<TaskExecModel>();
// cerco in redis...
DateTime adesso = DateTime.Now;
string currKey = $"{redisBaseKey}:Task:ExecList:{TaskId}:{adesso:yyMMdd}:{adesso:HHmm}:{maxRec}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<TaskExecModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = MLController.TaskExecGetFilt(TaskId, maxRec);
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(currKey, rawData, FastCache);
}
if (result == null)
{
result = new List<TaskExecModel>();
}
sw.Stop();
Log.Debug($"TaskExecGetFilt | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco TaskList gestiti
/// </summary>
/// <param name="CurrFilter"></param>
/// <param name="searchVal"></param>
/// <returns></returns>
public async Task<List<TaskListModel>> TaskListAll(Task2ExeType TType, string searchVal = "")
{
// setup parametri costanti
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<TaskListModel> result = new List<TaskListModel>();
// cerco in redis...
DateTime adesso = DateTime.Now;
string currKey = $"{redisBaseKey}:Task:List:{TType}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<TaskListModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = MLController.TaskListGetAll(TType);
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(currKey, rawData, FastCache);
}
if (result == null)
{
result = new List<TaskListModel>();
}
// se necessario filtro..
if (!string.IsNullOrEmpty(searchVal))
{
result = result
.Where(x => x.Name.Contains(searchVal, StringComparison.InvariantCultureIgnoreCase)
|| x.Descript.Contains(searchVal, StringComparison.InvariantCultureIgnoreCase))
.ToList();
}
sw.Stop();
Log.Debug($"TaskListAll | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Update ordinamento task
/// </summary>
/// <param name="rec2upd">Record da spostare x priorità</param>
/// <returns></returns>
public async Task<bool> TaskListMove(TaskListModel rec2upd, bool moveUp)
{
bool dbResult = MLController.TaskListMove(rec2upd, moveUp);
// svuoto cache!
await FlushCache("Task");
return await Task.FromResult(dbResult);
}
/// <summary>
/// Update/Insert record TaskList
/// </summary>
/// <param name="rec2upd"></param>
/// <returns></returns>
public async Task<bool> TaskListUpsert(TaskListModel rec2upd)
{
bool dbResult = MLController.TaskListUpsert(rec2upd);
// svuoto cache!
await FlushCache("Task");
return dbResult;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Oggetto per connessione a REDIS
/// </summary>
protected ConnectionMultiplexer redisConn = null!;
/// <summary>
/// Oggetto DB redis da impiegare x chiamate R/W
/// </summary>
protected IDatabase redisDb = null!;
#endregion Protected Fields
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private string CodModulo = "";
private string ConnStr = "";
private Dictionary<string, string> connStrParams = new Dictionary<string, string>();
private string DataBase = "";
private string DataSource = "";
private string redisBaseKey = "MP:TASK";
#endregion Private Fields
#region Private Properties
private static MpLandController MLController { get; set; } = null!;
private RestCallService RCallService { get; set; } = null!;
#endregion Private Properties
#region Private Methods
/// <summary>
/// Esegue flush memoria redis dato pattern
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
private async Task<bool> ExecFlushRedisPattern(RedisValue pattern)
{
bool answ = false;
var listEndpoints = redisConn.GetEndPoints();
foreach (var endPoint in listEndpoints)
{
//var server = redisConnAdmin.GetServer(listEndpoints[0]);
var server = redisConn.GetServer(endPoint);
if (server != null)
{
var keyList = server.Keys(redisDb.Database, pattern);
foreach (var item in keyList)
{
await redisDb.KeyDeleteAsync(item);
}
answ = true;
}
}
// notifico update ai client in ascolto x reset cache
NotifyReloadRequest($"FlushRedisCache | {pattern}");
return answ;
}
#endregion Private Methods
}
}
+95 -21
View File
@@ -115,8 +115,9 @@ namespace MP.FileData.Controllers
/// <param name="forceTag">Forza il controllo dei Tags</param>
/// <param name="currRule">Regole di ricerca applicate</param>
/// <param name="UserName">Utente connesso x approvazione</param>
/// <param name="DoApprove">indica se vada approvata la modifica o lasciato "in sospeso"</param>
/// <returns></returns>
public int CheckFileArchived(string idxMacchina, string path, int numDayPre, string searchPattern, bool forceTag, SearchRules currRule, string UserName)
public int CheckFileArchived(string idxMacchina, string path, int numDayPre, string searchPattern, bool forceTag, SearchRules currRule, string UserName, bool DoApprove)
{
Log.Info($"CheckFileArchived S00 | macchina: {idxMacchina} | path: {path} | pattern: {searchPattern} | # ExcludedFileExt: {currRule.ExcludedFileExt.Count()}");
int checkDone = 0;
@@ -213,7 +214,7 @@ namespace MP.FileData.Controllers
if (fileNew != null && fileNew.Count > 0)
{
checkDone += fileNew.Count;
FileInsert(idxMacchina, path, fileNew, UserName, 0, currRule);
FileInsert(idxMacchina, path, fileNew, UserName, 0, currRule, DoApprove);
Log.Trace($"CheckFileArchived S03 | insert {fileNew.Count} files");
}
// aggiorno i file modificati
@@ -247,7 +248,7 @@ namespace MP.FileData.Controllers
dbCtx.Dispose();
}
public int FileCountFilt(string IdxMacchina, bool OnlyActive, bool OnlyMod, bool OnlyNoTag, string FileName, string Tag, string SearchVal)
public int FileCountFilt(string IdxMacchina, bool OnlyActive, bool OnlyMod, bool OnlyNoTag, string FileName, string UserName, string Tag, string SearchVal)
{
int answ = 0;
using (MoonPro_ProgContext localDbCtx = new MoonPro_ProgContext(_configuration))
@@ -259,6 +260,7 @@ namespace MP.FileData.Controllers
&& (x.Active == OnlyActive || !OnlyActive) && (x.Tags.Count() == 0 || !OnlyNoTag)
&& (!OnlyMod || x.DiskStatus != FileState.Ok)
&& (x.Path.Equals(FileName) || string.IsNullOrEmpty(FileName))
&& (x.UserAppr.Equals(UserName) || string.IsNullOrEmpty(UserName))
&& (x.Tags.Where(t => t.TagId == Tag).Count() > 0 || string.IsNullOrEmpty(Tag))
&& ((!string.IsNullOrEmpty(SearchVal) && (x.Path.Contains(SearchVal) || x.Tags.Where(t => t.TagId.Contains(SearchVal)).Count() > 0)) || string.IsNullOrEmpty(SearchVal))
).Count();
@@ -281,24 +283,26 @@ namespace MP.FileData.Controllers
.DbSetProgFile
.Where(x => x.FileId == currItem.FileId)
.FirstOrDefault();
localDbCtx
.DbSetProgFile
.Remove(file2del);
// se ce ne fosse un altro precedente --> lo (ri)attiva
var file2open = localDbCtx
.DbSetProgFile
.Where(x => x.Name == currItem.Name && x.IdxMacchina == currItem.IdxMacchina)
.OrderByDescending(y => y.Rev)
.FirstOrDefault();
.DbSetProgFile
.Where(x => !x.Active && x.IdxMacchina == currItem.IdxMacchina && x.Path == currItem.Path)
.OrderByDescending(y => y.Rev)
.FirstOrDefault();
if (file2open != null)
{
file2open.Active = true;
}
// elimino record!
localDbCtx
.DbSetProgFile
.Remove(file2del);
// salvo!
localDbCtx.SaveChanges();
done = true;
int numDone = localDbCtx.SaveChanges();
done = numDone != 0;
}
return done;
}
@@ -331,6 +335,11 @@ namespace MP.FileData.Controllers
return done;
}
/// <summary>
/// Cerca il file x chiave ID
/// </summary>
/// <param name="FileId"></param>
/// <returns></returns>
public FileModel FileGetByKey(int FileId)
{
FileModel thisFile = null;
@@ -344,9 +353,42 @@ namespace MP.FileData.Controllers
return thisFile;
}
public List<DatabaseModels.FileModel> FileGetByPath(string path, bool onlyActive)
/// <summary>
/// Cerca un file dato un fice successivo specificando rev richiesta)
/// </summary>
/// <param name="FileId"></param>
/// <param name="Rev"></param>
/// <returns></returns>
public FileModel FileGetByKeyRev(int FileId, int Rev)
{
List<DatabaseModels.FileModel> dbResult = new List<DatabaseModels.FileModel>();
FileModel oFile = null;
FileModel tFile = null;
using (MoonPro_ProgContext localDbCtx = new MoonPro_ProgContext(_configuration))
{
oFile = localDbCtx
.DbSetProgFile
.Where(x => x.FileId == FileId)
.FirstOrDefault();
// se la rev è diversa --> la cerco!
if (oFile.Rev == Rev)
{
tFile = oFile;
}
else
{
tFile = localDbCtx
.DbSetProgFile
.Where(x => x.Name == oFile.Name && x.IdxMacchina == oFile.IdxMacchina && x.Path == oFile.Path)
.FirstOrDefault();
}
}
return tFile;
}
public List<FileModel> FileGetByPath(string path, bool onlyActive)
{
List<FileModel> dbResult = new List<FileModel>();
using (MoonPro_ProgContext localDbCtx = new MoonPro_ProgContext(_configuration))
{
dbResult = localDbCtx
@@ -359,9 +401,9 @@ namespace MP.FileData.Controllers
return dbResult;
}
public List<DatabaseModels.FileModel> FileGetFilt(string IdxMacchina, bool OnlyActive, bool OnlyMod, bool OnlyNoTag, string FileName, string Tag, string SearchVal, int NumStart, int NumRecords)
public List<FileModel> FileGetFilt(string IdxMacchina, bool OnlyActive, bool OnlyMod, bool OnlyNoTag, string FileName, string UserName, string Tag, string SearchVal, int NumStart, int NumRecords)
{
List<DatabaseModels.FileModel> dbResult = new List<DatabaseModels.FileModel>();
List<FileModel> dbResult = new List<FileModel>();
using (MoonPro_ProgContext localDbCtx = new MoonPro_ProgContext(_configuration))
{
dbResult = localDbCtx
@@ -373,6 +415,7 @@ namespace MP.FileData.Controllers
&& (x.Active == OnlyActive || !OnlyActive) && (x.Tags.Count() == 0 || !OnlyNoTag)
&& (!OnlyMod || x.DiskStatus != FileState.Ok)
&& (x.Path.Equals(FileName) || string.IsNullOrEmpty(FileName))
&& (x.UserAppr.Equals(UserName) || string.IsNullOrEmpty(UserName))
&& (x.Tags.Where(t => t.TagId == Tag).Count() > 0 || string.IsNullOrEmpty(Tag))
&& ((!string.IsNullOrEmpty(SearchVal) && (x.Path.Contains(SearchVal) || x.Tags.Where(t => t.TagId.Contains(SearchVal)).Count() > 0)) || string.IsNullOrEmpty(SearchVal))
).OrderByDescending(x => x.LastMod)
@@ -392,8 +435,9 @@ namespace MP.FileData.Controllers
/// <param name="UserName">Utente che ha approvato la modifica, "" = NON confermata/eliminabile</param>
/// <param name="rev">rev da usare x creazione</param>
/// <param name="currRule">Configuraizone ricerca</param>
/// <param name="DoApprove">indica se vada approvata la modifica o lasciato "in sospeso"</param>
/// <returns></returns>
public bool FileInsert(string idxMacchina, string basePath, List<FileInfo> newFiles, string UserName, int rev, SearchRules currRule)
public bool FileInsert(string idxMacchina, string basePath, List<FileInfo> newFiles, string UserName, int rev, SearchRules currRule, bool DoApprove)
{
// fare: lettura conf x macchina
Log.Info($"FileInsert S01 per macchina {idxMacchina}: {newFiles.Count} files da valutare");
@@ -408,7 +452,7 @@ namespace MP.FileData.Controllers
List<FileModel> newRec = newFiles.Select(o => new FileModel()
{
Active = true,
DiskStatus = FileState.Ok,
DiskStatus = DoApprove ? FileState.Ok : FileState.Changed,
IdxMacchina = idxMacchina,
LastCheck = adesso,
LastMod = o.LastWriteTime,
@@ -496,6 +540,38 @@ namespace MP.FileData.Controllers
return answ;
}
/// <summary>
/// Imposta utente approvazione + data modifica File
/// </summary>
/// <param name="currFile"></param>
/// <param name="UserName"></param>
/// <returns></returns>
public bool FileSetUserApp(FileModel currFile, string UserName)
{
bool done = false;
List<FileInfo> listUpdate = new List<FileInfo>();
using (MoonPro_ProgContext localDbCtx = new MoonPro_ProgContext(_configuration))
{
FileModel currRec = localDbCtx
.DbSetProgFile
.Where(x => x.FileId == currFile.FileId)
.FirstOrDefault();
if (currRec != null)
{
DateTime adesso = DateTime.Now;
currRec.UserAppr = UserName;
currRec.LastMod = adesso;
currRec.LastCheck = adesso;
//currFile.DiskStatus = FileState.Ok;
localDbCtx.Entry(currRec).State = EntityState.Modified;
// salvo DB
localDbCtx.SaveChanges();
}
}
return done;
}
/// <summary>
/// Approvazione modifica File
/// </summary>
@@ -505,8 +581,6 @@ namespace MP.FileData.Controllers
public bool FileModApprove(FileModel currFile, string UserName)
{
bool done = false;
// recupero file regole json da macchina..
List<FileInfo> listUpdate = new List<FileInfo>();
using (MoonPro_ProgContext localDbCtx = new MoonPro_ProgContext(_configuration))
{
@@ -535,7 +609,7 @@ namespace MP.FileData.Controllers
if (currRule.Name != "ND")
{
// inserisco come REVISIONE
FileInsert(currFile.IdxMacchina, currMacchina.BasePath, listUpdate, UserName, currFile.Rev + 1, currRule);
FileInsert(currFile.IdxMacchina, currMacchina.BasePath, listUpdate, UserName, currFile.Rev + 1, currRule, true);
// archivio vecchio file
currFile.Active = false;
+101
View File
@@ -0,0 +1,101 @@
@if (CurrRecord != null)
{
<hr />
<div class="row g-1">
<div class="col-md-2">
<div class="form-floating">
<input type="text" class="form-control" @bind="@CurrRecord.Name">
<label class="small">Nome Task</label>
</div>
</div>
<div class="col-md-2">
<div class="form-floating">
<select class="form-select" @bind="@CurrRecord.TType">
@foreach (var option in Enum.GetValues(typeof(MP.Data.Objects.Enums.Task2ExeType)))
{
<option value="@option">
@option
</option>
}
</select>
<label class="small">Tipo Task</label>
</div>
</div>
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" @bind="@CurrRecord.Descript">
<label class="small">Descrizione Task</label>
</div>
</div>
@* <div class="col-md-2">
<div class="form-floating">
<select class="form-select" @bind="@CurrRecord.Freq">
@foreach (var option in Enum.GetValues(typeof(MP.Data.Objects.Enums.TaskFreqType)))
{
<option value="@option">
@option
</option>
}
</select>
<label class="small">Frequenza</label>
</div>
</div>
<div class="col-md-1">
<div class="form-floating">
<input type="number" class="form-control" @bind="@CurrRecord.Cad">
<label class="small">Cadenza</label>
</div>
</div> *@
</div>
<div class="row g-1">
<div class="col-md-4">
<div class="form-floating">
<input type="text" class="form-control" @bind="@CurrRecord.Command">
<label class="small">Comando</label>
</div>
</div>
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" @bind="@CurrRecord.Args">
<label class="small">Parametri</label>
</div>
</div>
</div>
<div class="row g-1">
<div class="col-md-3">
<div class="form-floating">
<input type="number" class="form-control" @bind="@CurrRecord.Ordinal">
<label class="small">Ordine Esecuzione</label>
</div>
</div>
<div class="col-md-3">
<div class="form-floating">
<input type="datetime-local" class="form-control" @bind="@CurrRecord.DtNextExec">
<label class="small">Prossima Esecuzione</label>
</div>
</div>
<div class="col-md-2">
<div class="form-floating">
<select class="form-select" @bind="@CurrRecord.Freq">
@foreach (var option in Enum.GetValues(typeof(MP.Data.Objects.Enums.TaskFreqType)))
{
<option value="@option">
@option
</option>
}
</select>
<label class="small">Frequenza</label>
</div>
</div>
<div class="col-md-2">
<div class="form-floating">
<input type="number" class="form-control" @bind="@CurrRecord.Cad">
<label class="small">Cadenza</label>
</div>
</div>
<div class="col-md-2 pt-2">
<button class="btn btn-lg w-100 btn-success" @onclick="()=>doSave()" title="Save"><i class="far fa-save"></i> Save</button>
</div>
</div>
}
+47
View File
@@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Components;
using MP.Data.DatabaseModels;
using MP.Data.Services;
using System.Threading.Tasks;
namespace MP.Land.Components
{
public partial class TaskEdit
{
#region Public Properties
[Parameter]
public TaskListModel? CurrRecord { get; set; } = null;
[Parameter]
public EventCallback<bool> EC_update { get; set; }
#endregion Public Properties
#region Protected Properties
[Inject]
protected TaskService TService { get; set; }
#endregion Protected Properties
#region Protected Methods
protected async Task doCancel()
{
await EC_update.InvokeAsync(false);
}
protected async Task doSave()
{
bool fatto = false;
await Task.Delay(1);
if (CurrRecord != null)
{
fatto = await TService.TaskListUpsert(CurrRecord);
}
await EC_update.InvokeAsync(fatto);
}
#endregion Protected Methods
}
}
+100
View File
@@ -0,0 +1,100 @@
<div class="card text-bg-light shadow">
<div class="card-header table-primary p-1">
<div class="d-flex justify-content-between">
<div class="ps-2 d-flex">
<div class="px-0">
<b class="fs-4">History</b>
</div>
<div class="px-2">
@if (CurrRecord != null)
{
<div title="@CurrRecord.Command">@TextReduce(CurrRecord.Command, 40)</div>
<div class="small text-break" title="@CurrRecord.Args">@TextReduce(CurrRecord.Args, 60)</div>
}
</div>
</div>
<div class="px-2">
<div class="input-group">
<select class="form-select" @bind="@ShowErrorMode">
<option value="0">--- ALL ---</option>
<option value="1">Solo Errori</option>
<option value="2">Solo OK</option>
</select>
</div>
</div>
</div>
</div>
<div class="card-body py-0 px-1">
@if (ListRecords == null)
{
<LoadingData></LoadingData>
}
else if (totalCount == 0)
{
<div class="alert alert-warning text-center display-6">Nessun record trovato</div>
}
else
{
<div class="row">
<div class="col-12">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>#</th>
<th>Inizio</th>
<th>Fine</th>
<th class="text-right">Esito</th>
</tr>
</thead>
<tbody>
@foreach (var record in ListRecords)
{
<tr>
<td>
@record.TaskExecId
</td>
<td>
@($"{record.DtStart:HH:mm:ss.fff}")
<div class="small">@($"{record.DtStart:yyyy-MM.dd ddd}")</div>
</td>
<td>
@($"{record.DtEnd:HH:mm:ss.fff}")
<div class="small">@($"{record.DtEnd:yyyy-MM.dd ddd}")</div>
</td>
<td class="text-right">
<div class="d-flex justify-content-between">
<div class="px-0">
@if (@record.IsError)
{
<i class="far fa-thumbs-down text-danger"></i>
}
else
{
<i class="far fa-thumbs-up text-success"></i>
}
</div>
<div class="px-0">
<b>@($"{record.Duration:N3}")</b> <sub>sec</sub>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="4">
<div class="p-1 small text-truncate alert @alCss(record) mb-0" style="max-width: 60rem;">
<pre>@record.Result</pre>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
</div>
<div class="card-footer py-1">
<DataPager PageSize="numRecord" currPage="currPage" numRecordChanged="ForceReload" numPageChanged="ForceReloadPage" exportEnabled="false" totalCount="totalCount" showLoading="isLoading" />
</div>
</div>
+136
View File
@@ -0,0 +1,136 @@
using MailKit;
using Microsoft.AspNetCore.Components;
using MongoDB.Bson;
using MP.Data.DatabaseModels;
using MP.Data.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MP.Land.Components
{
public partial class TaskExeList
{
#region Public Properties
[Parameter]
public TaskListModel? CurrRecord { get; set; } = null;
#endregion Public Properties
#region Protected Fields
protected bool isLoading = false;
#endregion Protected Fields
#region Protected Properties
[Inject]
protected NavigationManager NavManager { get; set; }
/// <summary>
/// Show error mode: 0 = tutti 1 = solo errori 2 = solo ok
/// </summary>
protected int ShowErrorMode
{
get => showErrorMode;
set
{
if (showErrorMode != value)
{
showErrorMode = value;
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
}
}
protected int totalCount { get; set; } = 0;
[Inject]
protected TaskService TService { get; set; }
#endregion Protected Properties
#region Protected Methods
protected string alCss(TaskExecModel TaskRec)
{
return TaskRec.IsError ? "alert-danger" : "alert-success";
}
protected async Task ForceReload(int newNum)
{
numRecord = newNum;
await ReloadData();
}
protected async Task ForceReloadPage(int newNum)
{
currPage = newNum;
await ReloadData();
}
protected override async Task OnInitializedAsync()
{
await ReloadData();
}
protected string TextReduce(string textOriginal, int maxChar)
{
string answ = textOriginal;
if (answ.Length > maxChar)
{
answ = $"{textOriginal.Substring(0, maxChar / 2)} ... {textOriginal.Substring(answ.Length - maxChar / 2)}";
}
return answ;
}
#endregion Protected Methods
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private List<TaskExecModel> ListRecords;
private List<TaskExecModel> SearchRecords;
#endregion Private Fields
#region Private Properties
private int currPage { get; set; } = 1;
private int numRecord { get; set; } = 10;
private int showErrorMode { get; set; } = 0;
#endregion Private Properties
#region Private Methods
private async Task ReloadData()
{
SearchRecords = await TService.TaskExecGetFilt(CurrRecord.TaskId, 1000, "");
// se non tutti filtro...
if (ShowErrorMode != 0)
{
if (ShowErrorMode == 1)
{
SearchRecords = SearchRecords.FindAll(x => x.IsError);
}
else if (ShowErrorMode == 2)
{
SearchRecords = SearchRecords.FindAll(x => !x.IsError);
}
}
totalCount = SearchRecords.Count;
ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList();
}
#endregion Private Methods
}
}
+2 -1
View File
@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>MP.Land</RootNamespace>
<Version>6.16.2410.2215</Version>
<Version>6.16.2410.2519</Version>
<Configurations>Debug;Release;Debug_LiManDebug</Configurations>
</PropertyGroup>
@@ -62,6 +62,7 @@
<ItemGroup>
<ProjectReference Include="..\MP.AppAuth\MP.AppAuth.csproj" />
<ProjectReference Include="..\MP.Data\MP.Data.csproj" />
</ItemGroup>
<ItemGroup>
+1
View File
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration;
using MP.AppAuth;
using MP.AppAuth.Models;
using MP.AppAuth.Services;
using MP.Land.Data;
using System;
using System.Collections.Generic;
+191
View File
@@ -0,0 +1,191 @@
@page "/TaskScheduler"
<div class="row">
<div class="@mainCss">
<div class="card shadow">
<div class="card-header table-primary p-1">
<div class="d-flex justify-content-between">
<div class="px-2">
<b class="fs-4">TaskList</b>
</div>
<div class="px-2">
<div class="input-group">
@if (TypeSel != MP.Data.Objects.Enums.Task2ExeType.ND)
{
@if (currRecord == null)
{
<button class="btn btn-success" @onclick="addNew" title="Add New"><i class="fas fa-plus"></i> Add New</button>
}
else
{
<button class="btn btn-secondary" @onclick="doCancel" title="Cancel"><i class="fas fa-undo"></i> Cancel</button>
}
}
else
{
<button class="btn btn-outline-secondary">Sel &rarr;</button>
}
<select class="form-select" @bind="@TypeSel">
@foreach (var option in Enum.GetValues(typeof(MP.Data.Objects.Enums.Task2ExeType)))
{
<option value="@option">
@option
</option>
}
</select>
</div>
</div>
</div>
<TaskEdit CurrRecord="@currRecord" EC_update="forceUpdate"></TaskEdit>
</div>
<div class="card-body py-0 px-1">
@if (isLoading)
{
<ProgressDisplay DisplaySize="ProgressDisplay.ModalSize.Medium" ExpTimeMSec="@expTimeMsec" CurrVal="@currVal" NextVal="@nextVal" MaxVal="@MaxVal" Title="Task Processing" RefreshInterval="200"></ProgressDisplay>
<LoadingData Title="Elaborazione..." DisplayMode="LoadingData.SpinMode.BounceLine" DisplaySize="LoadingData.CtrlSize.Large"></LoadingData>
}
else if (ListRecords == null)
{
<LoadingData></LoadingData>
}
else if (totalCount == 0)
{
<div class="alert alert-warning text-center display-4">Nessun record trovato</div>
}
else
{
<div class="row">
<div class="col-12">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>
<button class="btn btn-sm btn-info" @onclick="doReset" title="Reset"><i class="fas fa-sync"></i></button>
</th>
<th>Ord</th>
<th>Task</th>
<th>Tipo</th>
@if (detRecord == null)
{
<th>Command</th>
}
<th>Sched.</th>
@if (detRecord == null)
{
<th class="text-end">Last</th>
<th class="text-end">Next</th>
<th class="text-end">Result</th>
<th>
<button class="btn btn-sm btn-danger me-1" @onclick="forceAll" title="Force All"><i class="fas fa-play"></i></button>
</th>
}
</tr>
</thead>
<tbody>
@foreach (var record in ListRecords)
{
<tr class="@checkSelect(@record.TaskId)">
<td class="text-nowrap">
<button class="btn btn-sm btn-info" @onclick="()=>doSelect(record)" title="Select"><i class="fas fa-search"></i></button>
@if (detRecord == null)
{
@if (currRecord == null)
{
<button class="btn btn-sm btn-primary ms-1" @onclick="()=>doEdit(record)" title="Edit"><i class="fas fa-pencil-alt"></i></button>
<button class="btn btn-sm btn-success" @onclick="()=>doClone(record)" title="Clona"><i class="fas fa-magic"></i></button>
}
else
{
<button class="btn btn-sm btn-secondary ms-1" @onclick="()=>doCancel()" title="Cancel"><i class="fas fa-undo"></i></button>
}
}
</td>
<td class="text-nowrap">
@if (detRecord == null)
{
@if (record.Ordinal == minOrdinal)
{
<button class="btn btn-sm btn-outline-secondary mx-1" disabled title="Cannot Move"><i class="fas fa-caret-up"></i></button>
}
else
{
<button class="btn btn-sm btn-outline-primary mx-1" @onclick="()=>doMove(record, true)" title="Move Up"><i class="fas fa-caret-up"></i></button>
}
}
@record.Ordinal
@if (detRecord == null)
{
@if (record.Ordinal == maxOrdinal)
{
<button class="btn btn-sm btn-outline-secondary mx-1" disabled title="Cannot Move"><i class="fas fa-caret-down"></i></button>
}
else
{
<button class="btn btn-sm btn-outline-primary mx-1" @onclick="()=>doMove(record, false)" title="Move Down"><i class="fas fa-caret-down"></i></button>
}
}
</td>
<td class="text-nowrap">
<div>@record.Name</div>
<div class="small text-truncate">@record.Descript</div>
</td>
<td>
@record.TType
</td>
@if (detRecord == null)
{
<td class="small text-truncate text-break">
<div title="@record.Command">@TextReduce(record.Command, 32)</div>
<div class="small text-break" title="@record.Args">@TextReduce(record.Args, 40)</div>
</td>
}
<td>@record.Freq &times; @record.Cad</td>
@if (detRecord == null)
{
<td class="text-end text-nowrap">
<div>@($"{record.DtLastExec:yyyy-MM-dd}")</div>
<div class="small">@($"{record.DtLastExec:ddd HH:mm:ss}")</div>
</td>
<td class="text-end text-nowrap">
<div>@($"{record.DtNextExec:yyyy-MM-dd}")</div>
<div class="small">@($"{record.DtNextExec:ddd HH:mm:ss}")</div>
</td>
<td class="text-end">
<b>@($"{record.LastDuration:N3}")</b> <sub>sec</sub> <button class="btn btn-sm @btnCss(record)" @onclick="() => ToggleDetail(record)" title="Ultima esecuzione"><i class="far @iconCss(record)"></i></button>
</td>
<td>
<button class="btn btn-sm @btnRunCss(record.DtNextExec) me-1" @onclick="()=>doRun(record)" title="Run Now"><i class="fas fa-play"></i></button>
</td>
}
</tr>
@if (DetailTaskId != null && DetailTaskId.TaskId == record.TaskId)
{
<tr>
<td colspan="10">
<div class="d-flex justify-content-end">
<div class="p-1 small alert @alCss(record)">
<pre>@record.LastResult</pre>
</div>
</div>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
}
</div>
<div class="card-footer py-1">
<DataPager PageSize="numRecord" currPage="currPage" numRecordChanged="ForceReload" numPageChanged="ForceReloadPage" exportEnabled="false" exportRequested="ExportCsv" fileName="@fileName" totalCount="totalCount" showLoading="isLoading" />
</div>
</div>
</div>
@if (detRecord != null && !isLoading)
{
<div class="col-6">
<TaskExeList CurrRecord="detRecord"></TaskExeList>
</div>
}
</div>
+404
View File
@@ -0,0 +1,404 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MP.Data.DatabaseModels;
using MP.Land.Data;
using static MP.Data.Objects.Enums;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System;
using System.Linq;
using MP.Data.Services;
using DnsClient.Protocol;
namespace MP.Land.Pages
{
public partial class TaskScheduler : ComponentBase, IDisposable
{
#region Public Methods
public string checkSelect(int TaskId)
{
string answ = "";
if (currRecord != null)
{
try
{
answ = (currRecord.TaskId == TaskId) ? "table-info" : "";
}
catch
{ }
}
else if (detRecord != null)
{
answ = (detRecord.TaskId == TaskId) ? "table-info" : "";
}
return answ;
}
public void Dispose()
{
MessageService.EA_SearchUpdated -= OnSeachUpdated;
}
public async void OnSeachUpdated()
{
await InvokeAsync(() =>
{
Task task = ReloadData();
StateHasChanged();
});
}
#endregion Public Methods
#region Protected Fields
protected string fileName = "TaskList.csv";
#endregion Protected Fields
#region Protected Properties
[Inject]
protected IJSRuntime JSRuntime { get; set; }
protected string mainCss
{
get => detRecord == null ? "col-12" : "col-6";
}
protected int maxOrdinal { get; set; } = 999;
[Inject]
protected Data.MessageService MessageService { get; set; }
protected int minOrdinal { get; set; } = 0;
[Inject]
protected NavigationManager NavManager { get; set; }
protected int totalCount { get; set; } = 0;
[Inject]
protected TaskService TService { get; set; }
protected Task2ExeType TypeSel
{
get => typeSel;
set
{
if (typeSel != value)
{
typeSel = value;
var pUpd = Task.Run(async () =>
{
await ReloadData();
});
pUpd.Wait();
}
}
}
#endregion Protected Properties
#region Protected Methods
protected async Task addNew()
{
currRecord = new TaskListModel()
{
Name = "Nuovo Task",
TType = TypeSel,
Descript = "Descrizione Task",
DtLastExec = DateTime.Today,
DtNextExec = DateTime.Today.AddDays(1)
};
await ReloadData();
}
/// <summary>
/// Gestione display avanzamento step
/// </summary>
/// <param name="currStep"></param>
protected async Task advStep(int currStep)
{
currVal = currStep;
nextVal = currVal + 1;
await InvokeAsync(StateHasChanged);
}
protected string alCss(TaskListModel TaskRec)
{
return TaskRec.LastIsError ? "alert-danger" : "alert-success";
}
protected string btnCss(TaskListModel TaskRec)
{
string answ = DetailTaskId != null && DetailTaskId.TaskId == TaskRec.TaskId ? "btn-" : "btn-outline-";
answ += TaskRec.LastIsError ? "danger" : "success";
return answ;
}
protected async Task doCancel()
{
currRecord = null;
detRecord = null;
await ReloadData();
}
protected async Task doClone(TaskListModel selRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler duplicare il record selezionato?"))
return;
currRecord = new TaskListModel()
{
Args = selRec.Args,
Name = $"Copia di {selRec.Name}",
Cad = selRec.Cad,
Command = selRec.Command,
Descript = $"Copia di {selRec.Descript}",
DtNextExec = DateTime.Today.AddDays(1),
DtLastExec = DateTime.Today.AddYears(-10),
Freq = selRec.Freq,
LastDuration = 0,
LastIsError = false,
LastResult = "",
TType = selRec.TType,
Ordinal = SearchRecords.Count + 1,
};
await ReloadData();
}
protected async Task doEdit(TaskListModel selRec)
{
currRecord = selRec;
await ReloadData();
}
protected async Task doMove(TaskListModel currRec, bool goUp)
{
await TService.TaskListMove(currRec, goUp);
detRecord = null;
currRecord = null;
await ReloadData();
}
protected async Task doReset()
{
detRecord = null;
currRecord = null;
await TService.FlushCache();
await ReloadData();
}
protected async Task doRun(TaskListModel selRec)
{
// SE non è ancora scaduto chiedo conferma
if (selRec.DtNextExec > DateTime.Now)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi esecuzione forzata task non scaduto?{Environment.NewLine}[{selRec.TaskId}]: {selRec.Name} - {selRec.Descript}{Environment.NewLine}Prossima schedulazione: {selRec.DtNextExec:yyyy-MM-dd HH:mm:ss}"))
return;
}
// imposto tempo atteso esecuzione da ultimo...
isLoading = true;
MaxVal = 4;
int currStep = 0;
await advStep(currStep);
expTimeMsec = (int)(1000 * selRec.LastDuration) / 4;
detRecord = null;
await advStep(currStep++);
await Task.Delay(100);
await advStep(currStep++);
// chiama esecuzione task
var result = await TService.ExecuteTask(selRec, false);
await advStep(currStep++);
isLoading = false;
await Task.Delay(100);
await advStep(currStep++);
await ReloadData();
}
protected async Task doSelect(TaskListModel selRec)
{
detRecord = null;
currRecord = null;
DetailTaskId = null;
isLoading = true;
detRecord = selRec;
await ReloadData();
isLoading = false;
}
protected async Task forceAll()
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi esecuzione forzata di tutti i task?"))
return;
isLoading = true;
detRecord = null;
await Task.Delay(100);
foreach (var taskRec in SearchRecords)
{
var result = await TService.ExecuteTask(taskRec, false);
}
isLoading = false;
await Task.Delay(100);
await ReloadData();
}
protected async Task ForceReload(int newNum)
{
numRecord = newNum;
await ReloadData();
}
protected async Task ForceReloadPage(int newNum)
{
currPage = newNum;
await ReloadData();
}
protected async Task forceUpdate(bool doForce)
{
currRecord = null;
await ReloadData();
}
protected string iconCss(TaskListModel TaskRec)
{
return TaskRec.LastIsError ? "fa-thumbs-down" : "fa-thumbs-up";
}
protected override async Task OnInitializedAsync()
{
clearFile();
numRecord = 10;
MessageService.ShowSearch = false;
MessageService.PageName = "Task Scheduler";
MessageService.PageIcon = "oi oi-clock";
MessageService.EA_SearchUpdated += OnSeachUpdated;
await ReloadData();
}
protected void ResetData()
{
clearFile();
TService.rollBackEdit(currRecord);
currRecord = null;
}
protected async Task ResetFilter(SelectData newFilter)
{
clearFile();
detRecord = null;
currRecord = null;
SearchRecords = null;
ListRecords = null;
await ReloadData();
}
protected double righDiv(double num, double den)
{
if (den == 0)
{
den = 1;
}
double answ = num / den;
return answ;
}
protected string TextReduce(string textOriginal, int maxChar)
{
string answ = textOriginal;
if (answ.Length > maxChar)
{
answ = $"{textOriginal.Substring(0, maxChar / 2)} ... {textOriginal.Substring(answ.Length - maxChar / 2)}";
}
return answ;
}
protected void ToggleDetail(TaskListModel TaskRec)
{
if (DetailTaskId == null)
{
DetailTaskId = TaskRec;
}
else
{
DetailTaskId = (DetailTaskId.TaskId == TaskRec.TaskId) ? null : TaskRec;
}
}
#endregion Protected Methods
#region Private Fields
private double currVal = 0;
private TaskListModel? DetailTaskId = null;
private List<TaskListModel> ListRecords;
private int MaxVal = 10;
private double nextVal = 0;
private List<TaskListModel> SearchRecords;
#endregion Private Fields
#region Private Properties
private int currPage { get; set; } = 1;
private TaskListModel currRecord { get; set; } = null;
private TaskListModel detRecord { get; set; } = null;
private int expTimeMsec { get; set; } = 30000;
private string fullPath
{
get => $"{Directory.GetCurrentDirectory()}\\temp\\{fileName}";
}
private bool isLoading { get; set; } = false;
private int numRecord { get; set; } = 10;
private Task2ExeType typeSel { get; set; } = Task2ExeType.ND;
#endregion Private Properties
#region Private Methods
private string btnRunCss(DateTime dtNextExe)
{
DateTime adesso = DateTime.Now;
string answ = dtNextExe < adesso ? "btn-success" : "btn-warning";
return answ;
}
private async void clearFile()
{
await Task.Run(() => File.Delete(fullPath));
}
private async Task ExportCsv()
{
isLoading = true;
// salvo davvero!
await MP.Data.Utils.SaveToCsv(SearchRecords, fullPath, ';');
isLoading = false;
}
private async Task ReloadData()
{
SearchRecords = await TService.TaskListAll(TypeSel, "");
totalCount = SearchRecords.Count;
var firstRec = SearchRecords.OrderBy(x => x.Ordinal).FirstOrDefault();
minOrdinal = firstRec != null ? firstRec.Ordinal : 0;
var lastRec = SearchRecords.OrderByDescending(x => x.Ordinal).FirstOrDefault();
maxOrdinal = lastRec != null ? lastRec.Ordinal : 9999;
ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList();
}
#endregion Private Methods
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using MP.AppAuth;
using MP.AppAuth.Services;
using MP.Land.Data;
using Newtonsoft.Json;
using NLog;
@@ -501,7 +502,7 @@ namespace MP.Land.Pages
/// <exception cref="NotImplementedException"></exception>
private async Task<bool> UploadZip()
{
// chiamo SendZipFile di SyncService...
// chiamo SendZipFile di RestCallService...
FileInfo zFileInfo = new FileInfo(zFileName);
var fatto = await SyncServ.SendZipFile(LicServ.Applicazione, LicServ.Installazione, true, false, zFileInfo);
Log.Info($"Richiesto invio file | Applicazione: {LicServ.Applicazione} | Installazione: {LicServ.Installazione}");
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo Tablet MAPO - DotNet6</i>
<h4>Versione: 6.16.2410.2215</h4>
<h4>Versione: 6.16.2410.2519</h4>
<br />
Note di rilascio:
<ul>
+1 -1
View File
@@ -1 +1 @@
6.16.2410.2215
6.16.2410.2519
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2410.2215</version>
<version>6.16.2410.2519</version>
<url>https://nexus.steamware.net/repository/SWS/MP-LAND/stable/LAST/MP.Land.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-LAND/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+2 -2
View File
@@ -13,11 +13,11 @@
</div>
<CascadingValue Name="ShowSearch" Value=@ShowSearch>
<div class="main mr-1">
<div class="main pe-1">
<div class="top-row d-print-none">
<CmpTop></CmpTop>
</div>
<div class="content pt-1 pt-lg-2 mb-5">
<div class="content pt-1 pt-lg-2 mb-5 px-3">
@Body
</div>
<div class="fixed-bottom @cssRow">
+8
View File
@@ -53,6 +53,14 @@
<span class="fas fa-wrench fa-2x pe-2" aria-hidden="true"></span> System Info
</NavLink>
</div>
@if (IsSuperAdmin)
{
<div class="nav-item px-2">
<NavLink class="nav-link py-0 px-2 mb-0" href="TaskScheduler">
<span class="fas fa-stopwatch fa-2x pe-2" aria-hidden="true"></span> Task Scheduler
</NavLink>
</div>
}
<div class="nav-item px-2">
<NavLink class="nav-link py-0 px-2 mb-0" href="RefreshData">
<span class="fas fa-sync-alt fa-2x pe-2" aria-hidden="true"></span> Refresh Data
+5 -1
View File
@@ -10,6 +10,8 @@ using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MP.AppAuth.Services;
using MP.Data.Services;
using MP.Land.Data;
using StackExchange.Redis;
using System;
@@ -133,9 +135,11 @@ namespace MP.Land
services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<LicenseService>();
services.AddSingleton<SyncService>();
services.AddSingleton<RestCallService>();
services.AddScoped<AppAuthService>();
services.AddScoped<MessageService>();
services.AddScoped<TaskService>();
services.AddScoped<Data.MessageService>();
services.AddSingleton<IConnectionMultiplexer>(redisMultiplexer);
services.AddBlazoredLocalStorage();
+4 -1
View File
@@ -57,6 +57,7 @@
"appVers": "stable",
"ConnectionStrings": {
"DefaultConnection": "Server=SQL2016DEV;Database=MoonPro;Trusted_Connection=True;MultipleActiveResultSets=true",
"MP.All": "Server=SQL2016DEV;Database=MoonPro;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;",
"MP.Land": "Server=SQL2016DEV;Database=MoonPro;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;",
"MP.Land.Auth": "Server=SQL2016DEV;Database=MoonPro_Anagrafica;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;",
//"MP.Land": "Server=SQL2016DEV;Database=MoonPro_ONE;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;",
@@ -68,8 +69,10 @@
"ServerConf": {
"BaseUrl": "https://localhost:44309/",
"CodApp": "MP-LAND",
"CodModulo": "MP-LAND",
"IobUploadPath": "C:\\inetpub\\wwwroot\\MP\\fileUpload",
"Modulo": "MoonPro",
"downloadPath": "C:\\Steamware\\installers\\MP"
"downloadPath": "C:\\Steamware\\installers\\MP",
"Prog.ApiUrl": "https://office.egalware.com/MP/PROG"
}
}
+1 -1
View File
@@ -162,7 +162,7 @@ namespace MP.Prog.Components
sw.Restart();
// recupero elenco macchine
percLoading += 100 / numMacchine;
numChecks = await FDService.UpdateMachineArchive(idxMacchina, numDays, true, false, MServ.UserName);
numChecks = await FDService.UpdateMachineArchive(idxMacchina, numDays, true, false, MServ.UserName, false);
await Task.Delay(1);
setupMessages.Add($"{idxMacchina}: {numChecks} files");
await InvokeAsync(StateHasChanged);
+9 -2
View File
@@ -6,7 +6,7 @@
<div class="col-6 table-primary">
<div class="row">
<div class="col-4 text-primary">
<h4>Archivio</h4>
<h4><i class="fas fa-database"></i> @OldTitle</h4>
</div>
<div class="col-4">
</div>
@@ -18,7 +18,14 @@
<div class="col-6 table-warning">
<div class="row">
<div class="col-4 text-warning">
<h4>Attuale</h4>
@if(CompareVers)
{
<h4><i class="fas fa-database"></i> @NewTitle</h4>
}
else
{
<h4><i class="fas fa-server"></i> @NewTitle</h4>
}
</div>
<div class="col-4">
</div>
+14 -7
View File
@@ -12,23 +12,32 @@ namespace MP.Prog.Components
{
#region Public Properties
[Parameter]
public bool CompareVers { get; set; } = false;
[Parameter]
public EventCallback<int> diffDone { get; set; }
[Parameter]
public string newText
public string NewText
{
get => _newText;
set => _newText = value;
}
[Parameter]
public string oldText
public string NewTitle { get; set; } = "Attuale";
[Parameter]
public string OldText
{
get => _oldText;
set => _oldText = value;
}
[Parameter]
public string OldTitle { get; set; } = "Archivio";
#endregion Public Properties
#region Protected Fields
@@ -69,7 +78,7 @@ namespace MP.Prog.Components
numChanges = 0;
// calcolo diff
diff_match_patch dmp = new diff_match_patch();
List<Diff> diff = dmp.diff_main(oldText, newText);
List<Diff> diff = dmp.diff_main(OldText, NewText);
//List<Diff> diff = dmp.diff_main(oldTextFix, newTextFix);
dmp.diff_cleanupSemantic(diff);
@@ -86,12 +95,12 @@ namespace MP.Prog.Components
switch (item.operation)
{
case Operation.DELETE:
sbOld.Append($"<span class=\"border border-success table-success\">{HttpUtility.HtmlEncode(item.text)}</span>");
sbOld.Append($"<span class=\"border border-success bg-success bg-opacity-25\">{HttpUtility.HtmlEncode(item.text)}</span>");
numChanges++;
break;
case Operation.INSERT:
sbNew.Append($"<span class=\"border border-danger table-danger\">{HttpUtility.HtmlEncode(item.text)}</span>");
sbNew.Append($"<span class=\"border border-danger bg-danger bg-opacity-25\">{HttpUtility.HtmlEncode(item.text)}</span>");
numChanges++;
break;
@@ -129,8 +138,6 @@ namespace MP.Prog.Components
string fixVal = origVal.Trim()
.Replace(" ", "&nbsp;&nbsp;")
.Replace(Environment.NewLine, sepDest);
//.Replace("\r", sepDest)
//.Replace("\n", sepDest);
return new MarkupString(fixVal);
}
+70 -27
View File
@@ -1,43 +1,86 @@
<div class="card">
<div class="card-header bg-secondary text-light">
<div class="row">
<div class="card-header bg-dark text-light">
<div class="d-flex just">
<div class="col-4">
<h3>Dettaglio modifiche</h3>
<div class="d-flex">
<div class="px-0">
<h4>Dettaglio modifiche</h4>
</div>
</div>
</div>
<div class="col-4">
@if (_currItem.Active)
{
@if (_currItem.DiskStatus != FileData.FileState.Ok)
<div class="d-flex justify-content-around text-nowrap">
@if (_currItem.Rev > 0)
{
<div class="row">
<div class="col-6">
<button type="button" class="btn btn-primary w-100" value="Cancel" @onclick="RejectChange">Mantieni Archivio <i class="fas fa-arrow-right"></i></button>
<div class="px-0">
@if (CompareRev)
{
<div class="input-group">
<span class="input-group-text">SX</span>
<select @bind="@SelRevSx" class="form-select form-select-sm text-end" style="min-width: 4rem;">
@if (ListRevSx != null)
{
foreach (var item in ListRevSx)
{
<option value="@item">@item</option>
}
}
</select>
<span class="input-group-text">DX</span>
<select @bind="@SelRevDx" class="form-select form-select-sm text-end" style="min-width: 4rem;">
@if (ListRevDx != null)
{
foreach (var item in ListRevDx)
{
<option value="@item">@item</option>
}
}
</select>
<button class="btn btn-secondary" title="Chiudi Confronto" @onclick="DoCompare"><i class="fa-solid fa-clock-rotate-left"></i></button>
</div>
}
else
{
<button class="btn btn-primary" title="Confronta release differenti" @onclick="DoCompare"><i class="fa-solid fa-clock-rotate-left"></i> Compara Versioni</button>
}
</div>
}
@if (_currItem.Active)
{
@if (_currItem.DiskStatus != FileData.FileState.Ok)
{
<div class="px-0">
<div class="row">
<div class="col-6">
<button type="button" class="btn btn-primary w-100" value="Cancel" @onclick="RejectChange">Mantieni Archivio <i class="fas fa-arrow-right"></i></button>
</div>
<div class="col-6">
<button type="button" class="btn btn-warning w-100" value="Cancel" @onclick="ApproveChange"><i class="fas fa-arrow-left"></i> Accetta Modifiche</button>
</div>
</div>
</div>
<div class="col-6">
<button type="button" class="btn btn-warning w-100" value="Cancel" @onclick="ApproveChange"><i class="fas fa-arrow-left"></i> Accetta Modifiche</button>
}
}
else
{
<div class="px-0">
<div class="row">
<div class="col-6">
<button type="button" class="btn btn-success w-100" value="" @onclick="ExportArchive">Sostituisci da Archivio <i class="fas fa-file-download"></i></button>
</div>
<div class="col-6">
</div>
</div>
</div>
}
}
else
{
<div class="row">
<div class="col-6">
<button type="button" class="btn btn-success w-100" value="" @onclick="ExportArchive">Sostituisci da Archivio <i class="fas fa-file-download"></i></button>
</div>
<div class="col-6">
</div>
</div>
}
</div>
</div>
<div class="col-2">
</div>
<div class="col-2 text-end">
<button type="button" class="btn btn-light w-100" value="Cancel" @onclick="cancelUpdate" title="Chiudi">Chiudi <i class="fas fa-xmark"></i></button>
<div class="col-4 text-end">
<button type="button" class="btn btn-light" value="Cancel" @onclick="cancelUpdate" title="Chiudi">Chiudi <i class="fas fa-xmark"></i></button>
</div>
</div>
</div>
<div class="card-body small py-1">
<DiffView oldText="@_currItem.FileStringContent" newText="@(CurrFileContent(_currItem.Path))"></DiffView>
<DiffView OldTitle="@TitleSx" CompareVers="@CompareRev" OldText="@FileContSx" NewTitle="@TitleDx" NewText="@FileContDx"></DiffView>
</div>
</div>
+115 -8
View File
@@ -30,6 +30,7 @@ namespace MP.Prog.Components
set
{
_currItem = value;
CompareRev = false;
}
}
@@ -47,6 +48,86 @@ namespace MP.Prog.Components
#endregion Public Properties
private bool CompareRev = false;
protected int SelRevSx
{
get => _selRevSx;
set
{
if (_selRevSx != value)
{
_selRevSx = value;
// ricarico file SX
var pUpd = Task.Run(async () =>
{
await LoadRevision();
});
pUpd.Wait();
FixTitles();
}
}
}
protected int SelRevDx
{
get => _selRevDx;
set
{
if (_selRevDx != value)
{
_selRevDx = value;
// ricarico file DX
var pUpd = Task.Run(async () =>
{
await LoadRevision();
});
pUpd.Wait();
FixTitles();
}
}
}
private int _selRevSx = 0;
private int _selRevDx = 0;
private List<int> ListRevSx
{
get
{
List<int> answ = new List<int>() { 0 };
for (int i = 1; i < SelRevDx; i++)
{
answ.Add(i);
}
return answ;
}
}
private List<int> ListRevDx
{
get
{
List<int> answ = new List<int>();
for (int i = 0; i <= _currItem.Rev; i++)
{
answ.Add(i);
}
return answ;
}
}
private async Task DoCompare()
{
CompareRev = !CompareRev;
await LoadRevision();
FixTitles();
}
private void FixTitles()
{
// sistemo titoli
TitleSx = CompareRev ? $"Rev. {SelRevSx}" : "Archivio";
TitleDx = CompareRev ? $"Rev. {SelRevDx}" : "Attuale";
}
#region Public Methods
public string CurrFileContent(string fullPath)
@@ -91,12 +172,6 @@ namespace MP.Prog.Components
#endregion Protected Methods
//private string userName = "";
//protected override void OnInitialized()
//{
// userName = MServ.UserName;
//}
#region Private Methods
private async Task ApproveChange()
@@ -106,7 +181,7 @@ namespace MP.Prog.Components
if (_currItem != null)
{
await FDService.FileApprove(_currItem, MServ.UserName);
await FDService.FileModApprove(_currItem, MServ.UserName);
await DataUpdated.InvokeAsync(1);
}
else
@@ -150,7 +225,7 @@ namespace MP.Prog.Components
await DataReset.InvokeAsync(0);
await FDService.FileExport(_currItem);
await DataReset.InvokeAsync(0);
await FDService.UpdateMachineArchive(_currItem.IdxMacchina, 1, false, false,MServ.UserName);
await FDService.UpdateMachineArchive(_currItem.IdxMacchina, 1, false, false, MServ.UserName, true);
await DataUpdated.InvokeAsync(1);
}
else
@@ -188,6 +263,38 @@ namespace MP.Prog.Components
}
}
private string FileContSx { get; set; } = "";
private string FileContDx { get; set; } = "";
private string TitleSx { get; set; } = "Archivio";
private string TitleDx { get; set; } = "Attuale";
protected override void OnParametersSet()
{
FileContSx = _currItem.FileStringContent;
FileContDx = CurrFileContent(_currItem.Path);
SelRevDx = _currItem.Rev;
FixTitles();
}
private async Task LoadRevision()
{
if (CompareRev)
{
var reqSx = await FDService.FileGetByKeyRev(_currItem.FileId, SelRevSx);
var reqDx = await FDService.FileGetByKeyRev(_currItem.FileId, SelRevDx);
FileContSx = reqSx.FileStringContent;
FileContDx = reqDx.FileStringContent;
}
else
{
FileContSx = _currItem.FileStringContent;
FileContDx = CurrFileContent(_currItem.Path);
}
FixTitles();
}
#endregion Private Methods
}
}
+1 -4
View File
@@ -6,10 +6,7 @@
<div class="input-group input-group-sm">
<DebounceInput id="sVal" class="form-control" placeholder="@("Ricerca (min " + _minChar + " char)")" autocomplete="off" @ref="debInput" @bind-Value="@_searchVal" @bind-Value:event="OnInput" DebounceTime="@_debMsec" MinLength="@_minChar" OnValueChanged="e => { searchVal = e; }" ForceNotifyByEnter="true" ForceNotifyOnBlur="true" />
<div class="input-group-append">
<button @onclick="reset" class="btn btn-success">reset</button>
</div>
<button @onclick="reset" class="btn btn-success"><i class="fa-solid fa-xmark"></i></button>
</div>
@code {
+1 -1
View File
@@ -14,7 +14,7 @@
</span>
</div>
<input type="text" class="form-control form-control-sm" placeholder="Ricerca Tag" @bind-value="@SearchTag" />
<select @bind="@SelTag" class="form-control form-control-sm">
<select @bind="@SelTag" class="form-select form-select-sm">
@if (TagList != null)
{
foreach (var item in TagList)
+167
View File
@@ -0,0 +1,167 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MP.FileData.Controllers;
using MP.Prog.Data;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MP.Prog.Controllers
{
[AllowAnonymous]
[Route("api/[controller]")]
[ApiController]
public class FileChangeController : ControllerBase
{
#region Public Constructors
/// <summary>
/// Init generico
/// </summary>
/// <param name="FileArchDService"></param>
public FileChangeController(FileArchDataService FileArchDService)
{
FADService = FileArchDService;
Log.Info("Avviata classe FileChangeController");
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Esegue controllo + approvazione modifiche x directory GET api/FileChange/approve/[id]?[numDayPrev]
/// </summary>
/// <param name="id">Directory / IdxMacchina | 0/ALL = tutte</param>
/// <param name="numDayPrev">Verifica file modificati da numDayPrev gg</param>
/// <returns>Elenco dei file approvati</returns>
[HttpGet("approve/{id}")]
public async Task<List<string>> Approve(string id, int numDayPrev)
{
List<string> result = new List<string>();
// verifico se una o tutte le macchine
List<string> listDir = new List<string>();
if (id == "ALL" || id == "0")
{
var machList = await FADService.ArchMaccGetAll();
listDir = machList
.Where(x => !string.IsNullOrEmpty(x.BasePath))
.Select(x => x.IdxMacchina).ToList();
}
else
{
listDir.Add(id);
}
// ciclo su tutte le macchine e faccio approvazione...
foreach (var currDir in listDir)
{
// eseguo singolo controllo, utente = "" (è auto-approvazione)
await FADService.UpdateMachineArchive(currDir, numDayPrev, false, false, "", true);
List<string> listAppr = await ForceApproveMod(currDir, numDayPrev);
result.AddRange(listAppr);
}
return result;
}
/// <summary>
/// Esegue controllo + approvazione modifiche x directory GET api/FileChange/Approve/[id]?[numDayPrev]
/// </summary>
/// <param name="id">Directory / IdxMacchina | 0/ALL = tutte</param>
/// <param name="numDayPrev">Verifica file modificati da numDayPrev gg</param>
/// <returns>Elenco dei file approvati</returns>
[HttpGet("check/{id}")]
public async Task<List<string>> Check(string id, int numDayPrev)
{
List<string> result = new List<string>();
// verifico se una o tutte le macchine
List<string> listDir = new List<string>();
if (id == "ALL" || id == "0")
{
var machList = await FADService.ArchMaccGetAll();
listDir = machList
.Where(x => !string.IsNullOrEmpty(x.BasePath))
.Select(x => x.IdxMacchina).ToList();
}
else
{
listDir.Add(id);
}
// ciclo su tutte le macchine e faccio verifica modificati...
foreach (var currDir in listDir)
{
// eseguo singolo controllo, utente = "" , SENZA auto-approvazione
int numCheks = await FADService.UpdateMachineArchive(currDir, numDayPrev, false, false, "", false);
result.Add($"{currDir} | {numCheks} files");
}
return result;
}
/// <summary>
/// Verifica Health servizi FileChange GET api/FileChange
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult<string> Get()
{
return "OK";
}
#endregion Public Methods
#region Protected Properties
/// <summary>
/// Dataservice x accesso DB
/// </summary>
protected FileArchDataService FADService { get; set; }
#endregion Protected Properties
#region Private Fields
/// <summary>
/// Classe per logging
/// </summary>
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
#region Private Methods
/// <summary>
/// Effettua approvazione file modificati, restituendo elenco
/// </summary>
/// <param name="currDir"></param>
/// <param name="numDayPrev"></param>
/// <returns></returns>
private async Task<List<string>> ForceApproveMod(string currDir, int numDayPrev)
{
List<string> answ = new List<string>();
// preparo filtro
SelectData currFilt = new SelectData()
{
IdxMacchina = currDir,
OnlyActive = true,
OnlyMod = true
};
// recupero elenco files modificati x cartella
var listChanged = await FADService.FileGetFilt(currFilt);
// ciclo ogni file modificato
foreach (var cFile in listChanged)
{
// approvo il file modificato con utente anonimo (auto-Approve)
bool fatto = await FADService.FileModApprove(cFile, "");
if (fatto)
{
answ.Add(cFile.Path);
}
}
return answ;
}
#endregion Private Methods
}
}
+63
View File
@@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MP.FileData.Controllers;
using MP.Prog.Data;
using NLog;
using System.Threading.Tasks;
namespace MP.Prog.Controllers
{
[AllowAnonymous]
[Route("api/[controller]")]
[ApiController]
public class HealthController : ControllerBase
{
#region Public Constructors
/// <summary>
/// Init generico
/// </summary>
/// <param name="FileArchDService"></param>
public HealthController(FileArchDataService FileArchDService)
{
FADService = FileArchDService;
Log.Info("Avviata classe HealthController");
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Verifica Health servizi app
/// GET api/health
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult<string> Get()
{
return "OK";
}
#endregion Public Methods
#region Protected Properties
/// <summary>
/// Dataservice x accesso DB
/// </summary>
protected FileArchDataService FADService { get; set; }
#endregion Protected Properties
#region Private Fields
/// <summary>
/// Classe per logging
/// </summary>
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
}
}
+35 -9
View File
@@ -158,7 +158,7 @@ namespace MP.Prog.Data
int numCount = 0;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
numCount = dbController.FileCountFilt(CurrFilter.IdxMacchina, CurrFilter.OnlyActive, CurrFilter.OnlyMod, CurrFilter.OnlyNoTag, CurrFilter.FileName, CurrFilter.Tag, CurrFilter.SearchVal);
numCount = dbController.FileCountFilt(CurrFilter.IdxMacchina, CurrFilter.OnlyActive, CurrFilter.OnlyMod, CurrFilter.OnlyNoTag, CurrFilter.FileName, CurrFilter.UserName, CurrFilter.Tag, CurrFilter.SearchVal);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB per FileCountFilt: {ts.TotalMilliseconds} ms");
@@ -169,14 +169,24 @@ namespace MP.Prog.Data
{
return Task.FromResult(dbController.FileGetByKey(FileId));
}
/// <summary>
/// Restituisce il file dato un ID + revisione
/// cerca a pari nome, macchina + REV specifica)
/// </summary>
/// <param name="FileId">Id del file originale</param>
/// <param name="Rev">Rev specifica richiesta</param>
/// <returns></returns>
public Task<FileModel> FileGetByKeyRev(int FileId, int Rev)
{
return Task.FromResult(dbController.FileGetByKeyRev(FileId, Rev));
}
public async Task<List<FileModel>> FileGetFilt(SelectData CurrFilter)
{
List<FileModel> dbResult = new List<FileModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.FileGetFilt(CurrFilter.IdxMacchina, CurrFilter.OnlyActive, CurrFilter.OnlyMod, CurrFilter.OnlyNoTag, CurrFilter.FileName, CurrFilter.Tag, CurrFilter.SearchVal, CurrFilter.NumSkip, CurrFilter.PageSize).ToList();
//dbResult = dbController.FileGetFilt(CurrFilter.IdxMacchina, CurrFilter.OnlyActive, CurrFilter.OnlyMod, CurrFilter.FirstRecord, CurrFilter.PageSize * 10, CurrFilter.SearchVal).ToList();
dbResult = dbController.FileGetFilt(CurrFilter.IdxMacchina, CurrFilter.OnlyActive, CurrFilter.OnlyMod, CurrFilter.OnlyNoTag, CurrFilter.FileName, CurrFilter.UserName, CurrFilter.Tag, CurrFilter.SearchVal, CurrFilter.NumSkip, CurrFilter.PageSize).ToList();
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB per FileGetFilt: {ts.TotalMilliseconds} ms");
@@ -261,17 +271,18 @@ namespace MP.Prog.Data
/// <param name="numDayPre">Numero giorni x ricerca all'indietro da data corrente / 0 = nessun limite</param>
/// <param name="forceTag">Indica se forzare il tag</param>
/// <param name="UserName">Utente attivo</param>
/// <param name="DoApprove">indica se vada approvata la modifica o lasciato "in sospeso"</param>
/// <returns></returns>
public async Task<int> UpdateAllArchive(int numDayPre, bool forceTag, string UserName)
public async Task<int> UpdateAllArchive(int numDayPre, bool forceTag, string UserName, bool DoApprove)
{
int checkDone = 0;
var listaMacchine = await ArchMaccGetAll();
foreach (var item in listaMacchine.Where(x => !string.IsNullOrEmpty(x.BasePath)).ToList())
{
checkDone += await UpdateMachineArchive(item.IdxMacchina, numDayPre, forceTag, false, UserName);
checkDone += await UpdateMachineArchive(item.IdxMacchina, numDayPre, forceTag, false, UserName, DoApprove);
}
return await Task.FromResult(checkDone);
return checkDone;
}
/// <summary>
@@ -284,8 +295,9 @@ namespace MP.Prog.Data
/// <param name="forceTag">Forza la riverifica dei tags (x update da setup)</param>
/// <param name="fullLog">Scrittura log verboso macchina</param>
/// <param name="UserName">Utente attivo</param>
/// <param name="DoApprove">indica se vada approvata la modifica o lasciato "in sospeso"</param>
/// <returns></returns>
public async Task<int> UpdateMachineArchive(string idxMacchina, int numDayPre, bool forceTag, bool fullLog, string UserName)
public async Task<int> UpdateMachineArchive(string idxMacchina, int numDayPre, bool forceTag, bool fullLog, string UserName, bool DoApprove)
{
int checkDone = 0;
Stopwatch stopWatch = new Stopwatch();
@@ -345,7 +357,7 @@ namespace MP.Prog.Data
Log.Trace($"Conf rule generato:{Environment.NewLine}{rawRule}");
}
}
checkDone = dbController.CheckFileArchived(macchina.IdxMacchina, macchina.BasePath, numDayPre, "*.*", forceTag, currRule, UserName);
checkDone = dbController.CheckFileArchived(macchina.IdxMacchina, macchina.BasePath, numDayPre, "*.*", forceTag, currRule, UserName, DoApprove);
}
}
}
@@ -373,7 +385,7 @@ namespace MP.Prog.Data
/// <param name="currItem"></param>
/// <param name="UserName"></param>
/// <returns></returns>
internal async Task<bool> FileApprove(FileModel currItem, string UserName)
internal async Task<bool> FileModApprove(FileModel currItem, string UserName)
{
bool answ = dbController.FileModApprove(currItem, UserName);
// svuoto cache!
@@ -381,6 +393,20 @@ namespace MP.Prog.Data
return answ;
}
/// <summary>
/// Forza registrazione della sola approvazione utente + data
/// </summary>
/// <param name="currItem"></param>
/// <param name="UserName"></param>
/// <returns></returns>
internal async Task<bool> FileSetUserApp(FileModel currItem, string UserName)
{
bool answ = dbController.FileSetUserApp(currItem, UserName);
// svuoto cache!
await ResetArchiveCache();
return answ;
}
/// <summary>
/// Eliminazione file (x rifiuto modifica)
/// </summary>
+6 -1
View File
@@ -14,6 +14,8 @@ namespace MP.Prog.Data
public string FileName { get; set; } = "";
public string UserName { get; set; } = "";
/// <summary>
/// Primo record x selezione paginata, tipicamente primo della "decina" della pagina corrente
/// </summary>
@@ -67,7 +69,8 @@ namespace MP.Prog.Data
{
DateEnd = endRounded,
DateStart = endRounded.AddDays(-numDayPrev),
SearchVal = ""
SearchVal = "",
UserName=""
};
return answ;
}
@@ -91,6 +94,8 @@ namespace MP.Prog.Data
return false;
if (FileName != item.FileName)
return false;
if (UserName != item.UserName)
return false;
if (IdxMacchina != item.IdxMacchina)
return false;
if (Tag != item.Tag)
+5 -1
View File
@@ -3,7 +3,8 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>MP.Prog</RootNamespace>
<Version>6.16.2410.2211</Version>
<Version>6.16.2410.2318</Version>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -27,6 +28,9 @@
<PackageReference Include="NLog" Version="5.3.3" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.12" />
<PackageReference Include="StackExchange.Redis" Version="2.8.12" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.9.0" />
</ItemGroup>
<ItemGroup>
+67 -46
View File
@@ -6,9 +6,15 @@
<div class="px-0">
<h4>Elenco Programmi</h4>
<div class="form-group mb-0">
<button id="btnForceCheck" class="btn btn-warning btn-sm" @onclick="() => ForceCheck(30)" title="Analisi Modifiche ultimi 30gg">
<button class="btn btn-outline-warning btn-sm" @onclick="() => ForceCheck(30)" title="Analisi Modifiche ultimi 30gg">
<i class="fas fa-sync-alt"></i> Update (30gg) <i class="far fa-folder-open"></i>
</button>
@if (OnlyMod && totalCount >= 0)
{
<button class="btn btn-success btn-sm ms-1" @onclick="MassAppr" title="Approva tutte le modifiche visualizzate">
<i class="fas fa-sync-alt"></i> Approva Mod. vis. <i class="fa-solid fa-clipboard-check"></i>
</button>
}
</div>
</div>
<div class="px-0 text-end">
@@ -50,32 +56,32 @@
</div>
</div>
</div>
<div class="d-flex mt-1">
<div class="d-flex mt-1 justify-content-between">
<div class="px-0 text-truncate">
@if (!string.IsNullOrEmpty(SelFileName))
{
<span class="badge badge-secondary" title="@SelFileName">@TextReduce(SelFileName, 20)</span>
<button class="btn btn-sm btn-secondary py-0 me-1" title="@SelFileName" @onclick="FilterPathRemove">@TextReduce(SelFileName, 25) <i class="fa-solid fa-xmark"></i></button>
}
@if (!string.IsNullOrEmpty(SelUserName))
{
<button class="btn btn-sm btn-dark text-light py-0" title="@SelUserName" @onclick="FilterUserNameRemove">@TextReduce(SelUserName, 20) <i class="fa-solid fa-xmark"></i></button>
}
</div>
<div class="px-0 w-100">
<div class="px-0">
<div class="input-group input-group-sm">
<span class="input-group-text">Tags</span>
<button type="button" class="btn btn-sm btn-primary" @onclick="() => OpenDialog()" title="Aggiunta Tag">
<i class="fas fa-search"></i>
</button>
<div class="form-control form-control-sm">
<div class="form-control form-control-sm" style="min-width: 6rem;">
@if (!string.IsNullOrEmpty(SelTag))
{
<span class="badge text-bg-info">@SelTag</span>
}
else
{
<span> &nbsp; ... &nbsp; </span>
}
</div>
<div class="input-group-append">
<button type="button" class="btn btn-sm btn-secondary" @onclick="() => ResetTag()" title="Reset Tag">
<i class="fas fa-sync"></i>
<i class="fas fa-xmark"></i>
</button>
</div>
</div>
@@ -108,13 +114,20 @@
<table class="table table-sm table-striped table-responsive-lg">
<thead>
<tr>
<th></th>
<th>
@if (currRecord != null)
{
<button class="btn btn-sm btn-primary" @onclick="ResetData" title="Chiudi">
<i class="fas fa-times"></i>
</button>
}
</th>
<th>File</th>
<th>Rev / State / Size</th>
<th class="text-center">Rev</th>
<th class="text-center">Size / User</th>
<th>Macchina</th>
<th>Tags</th>
<th class="text-end">Modificato</th>
@*<th>Controllo</th>*@
<th></th>
</tr>
</thead>
@@ -123,54 +136,55 @@
{
<tr class="@checkSelect(record.FileId)">
<td class="text-nowrap">
@if (currRecord == null)
{
<button class="btn btn-sm btn-info" @onclick="() => Edit(record)" title="Edit record #@record.FileId">
<i class="oi oi-pencil"></i>
</button>
}
else
{
<button class="btn btn-sm btn-secondary disabled">
<i class="oi oi-pencil"></i>
</button>
}
<button class="btn btn-sm btn-info" @onclick="() => Edit(record)" title="Edit record #@record.FileId"><i class="fa-solid fa-magnifying-glass"></i></button>
</td>
<td>
<div class="@(cssActive(record.Active))">
<div>@record.Name</div>
<button class="btn btn-sm btn-outline-secondary px-1 py-0" @onclick="() => FilterPath(record.Path)" title="Filtra per FilePath">
<div class="small text-truncate">@record.Path</div>
<button class="btn btn-sm btn-outline-secondary px-1 py-0" @onclick="() => FilterPath(record.Path)" title="@record.Path">
<i class="fa-regular fa-file-lines"></i>
<span class="small text-truncate">@TextReduce(record.Path, 40)</span>
</button>
</div>
</td>
<td>
<div class="d-flex justify-content-xl-between">
<div class="px-0">
@record.Rev
</div>
<div class="px-0">
<span title="@record.DiskStatus | Ultimo controllo: @record.LastCheck.ToString("yyyy.MM.dd HH:mm:ss")">
<span class="@(cssStatusByCod(record.DiskStatus))">
@record.DiskStatus
<td class="text-center">
@record.Rev
<div>
<span title="@record.DiskStatus | Ultimo controllo: @record.LastCheck.ToString("yyyy.MM.dd HH:mm:ss")">
<span class="@(cssStatusByCod(record.DiskStatus))">
@record.DiskStatus
</span>
</span>
</span>
</div>
<div class="px-0">
@CalcSize(record.Size)
</div>
</div>
</td>
<td class="text-center px-1" style="width: 8rem;">
<div class="text-end px-3">
@CalcSize(record.Size)
</div>
<div class="text-end small">
@if (!string.IsNullOrEmpty(record.UserAppr))
{
<button class="btn btn-sm btn-outline-dark px-1 py-0" title="Filtra Utente" @onclick="() => FilterUserName(record.UserAppr)">
<i class="fas fa-user-alt"></i>
<span class="small">@record.UserAppr</span>
</button>
}
else
{
<div class="d-flex justify-content-xl-between small">
<div class="px-0"><i class="fas fa-user-alt"></i></div>
<div class="px-0">@record.UserAppr</div>
<button class="btn btn-sm btn-primary border border-info border-2 rounded px-2 py-0" title="Forza approvazione Release" @onclick="() => FileSetUserApp(record)">
<i class="fas fa-user-alt"></i>
Approva
<i class="fas fa-floppy-disk"></i>
</button>
}
</div>
</td>
<td>
<div>@record.Macchina.Nome</div>
<div class="small">@record.Macchina.Descrizione</div>
</td>
<td style="max-width: 20rem;">
<td class="small" style="max-width: 30rem;">
@foreach (var item in record.Tags)
{
<button class="btn btn-sm btn-outline-info px-1 py-0 me-1" @onclick="() => FilterTag(item.TagId)" title="Filtra Tag">
@@ -181,6 +195,13 @@
<td class="text-end">
<div>@record.LastMod.ToString("yyyy.MM.dd")</div>
<div class="small">@record.LastMod.ToString("ddd HH:mm.ss")</div>
</td>
<td class="text-end">
@if (string.IsNullOrEmpty(record.UserAppr))
{
<button class="btn btn-sm btn-danger " @onclick="() => DeleteRec(record)" title="Elimina Record"><i class="fa-solid fa-trash"></i></button>
}
</td>
</tr>
}
@@ -191,6 +212,6 @@
}
</div>
<div class="card-footer py-0 px-1">
<EgwCoreLib.Razor.DataPager PageSize="numRecord" currPage="currPage" numRecordChanged="PagerReloadNum" numPageChanged="PagerReloadPage" totalCount="totalCount" showLoading="isLoading"></EgwCoreLib.Razor.DataPager>
<EgwCoreLib.Razor.DataPager PageSize="numRecord" currPage="currPage" numRecordChanged="SetRecPage" numPageChanged="SetNumPage" totalCount="totalCount" showLoading="isLoading"></EgwCoreLib.Razor.DataPager>
</div>
</div>
+175 -36
View File
@@ -106,15 +106,6 @@ namespace MP.Prog.Pages
#region Protected Methods
protected async Task AsyncReload()
{
isLoading = true;
currRecord = null;
ListRecords = null;
await ReloadData();
isLoading = false;
}
/// <summary>
/// Restituisce size calcolata
/// </summary>
@@ -125,6 +116,25 @@ namespace MP.Prog.Pages
return MeasureUtils.SizeSuffix(origSize, 1);
}
/// <summary>
/// Elimina record (non approvato) corrente e riattiva precedente
/// </summary>
/// <returns></returns>
protected async Task DeleteRec(FileModel CurrRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler eliminare il record e riattivare la revisione antecedente? Il file potrebbe risultare modificato"))
return;
// elimino
await FDService.FileDelete(CurrRec);
//// verifico eventuali modifiche
//var numCheck = await FDService.UpdateMachineArchive(CurrRec.IdxMacchina, 30, false, false, "");
isLoading = true;
await ReloadData();
isLoading = false;
}
protected async Task Edit(FileModel selRecord)
{
if (!selRecord.Active)
@@ -137,6 +147,26 @@ namespace MP.Prog.Pages
currRecord = await FDService.FileGetByKey(selRecord.FileId);
}
/// <summary>
/// forza approvazione utente corrente
/// </summary>
/// <returns></returns>
protected async Task FileSetUserApp(FileModel CurrRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler approvare il salvataggio automatico della nuova revisione?"))
return;
await FDService.FileSetUserApp(CurrRec, MServ.UserName);
isLoading = true;
await ReloadData();
isLoading = false;
}
/// <summary>
/// Esegue filtraggio sul Path di ricerca
/// </summary>
/// <param name="searchVal"></param>
/// <returns></returns>
protected async Task FilterPath(string searchVal)
{
SelFileName = searchVal;
@@ -146,6 +176,21 @@ namespace MP.Prog.Pages
isLoading = false;
}
/// <summary>
/// Elimina il filtro x path file
/// </summary>
/// <returns></returns>
protected async Task FilterPathRemove()
{
await FilterPath("");
OnlyActive = true;
}
/// <summary>
/// Imposta filtro x Tags
/// </summary>
/// <param name="searchVal"></param>
/// <returns></returns>
protected async Task FilterTag(string searchVal)
{
SelTag = searchVal;
@@ -154,19 +199,71 @@ namespace MP.Prog.Pages
isLoading = false;
}
/// <summary>
/// Imposta filtro x utente
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
protected async Task FilterUserName(string userName)
{
SelUserName = userName;
currPage = 1;
//await ReloadAllData();
await Task.Delay(1);
isLoading = false;
}
/// <summary>
/// Resetta filtro x utente
/// </summary>
/// <returns></returns>
protected async Task FilterUserNameRemove()
{
await FilterUserName("");
}
/// <summary>
/// Esegue comparazione tra dati DB e filesystem (modificati entro numDays)
/// </summary>
/// <param name="numDays"></param>
/// <returns></returns>
protected async Task ForceCheck(int numDays)
{
currRecord = null;
ListRecords = null;
// importante altrimenti NON mostra update UI
await Task.Delay(1);
//MServ.File_Filter = SelectData.Init(5, 10);
var numCheck = await FDService.UpdateAllArchive(numDays, false, MServ.UserName);
var numCheck = await FDService.UpdateAllArchive(numDays, false, MServ.UserName, false);
await ReloadAllData();
await Task.Delay(1);
await RefreshDisplayLoading();
}
/// <summary>
/// effettua approvazione di ttute le modifiche visualizzate
/// </summary>
/// <returns></returns>
protected async Task MassAppr()
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler procedere approvando una nuova revisione di ogni file modificato visualizzato?"))
return;
//verifico che sia cliccato solo modificati...
if (OnlyMod)
{
foreach (var item in ListRecords)
{
await FDService.FileModApprove(item, MServ.UserName);
}
}
// rileggo!
ResetData();
await ResetFilter();
await Task.Delay(1);
await ReloadData();
isLoading = false;
}
protected override async Task OnInitializedAsync()
{
SearchTag = defTag;
@@ -179,20 +276,6 @@ namespace MP.Prog.Pages
isLoading = false;
}
protected async Task PagerReloadNum(int newNum)
{
numRecord = newNum;
await ReloadData();
isLoading = false;
}
protected async Task PagerReloadPage(int newNum)
{
currPage = newNum;
await ReloadData();
isLoading = false;
}
protected async Task RefreshDisplayLoading()
{
await Task.Delay(1);
@@ -209,16 +292,23 @@ namespace MP.Prog.Pages
await ReloadData();
}
protected async Task ReloadAsync()
{
isLoading = true;
currRecord = null;
ListRecords = null;
await ReloadData();
isLoading = false;
}
protected async Task ReloadData()
{
isLoading = true;
// importante altrimenti NON mostra update UI
await Task.Delay(1);
totalCount = await FDService.FileCountFilt(MServ.File_Filter);
//SearchRecords = await FDService.FileGetFilt(MServ.File_Filter);
//// faccio paginazione SOLO NELLA DECINA attuale... (quindi non tutte le pagine ma solo subset)
//ListRecords = SearchRecords.Skip(numRecord * (currPage % 10 - 1)).Take(numRecord).ToList();
// valutare recupero info + redis + pagiazione EX POST...
ListRecords = await FDService.FileGetFilt(MServ.File_Filter);
await Task.Delay(1);
}
@@ -258,6 +348,30 @@ namespace MP.Prog.Pages
currRecord = selRecord;
}
/// <summary>
/// Imposta pagina corrente
/// </summary>
/// <param name="newNum"></param>
/// <returns></returns>
protected async Task SetNumPage(int newNum)
{
currPage = newNum;
await ReloadData();
isLoading = false;
}
/// <summary>
/// Imposta num record x pagina
/// </summary>
/// <param name="newNum"></param>
/// <returns></returns>
protected async Task SetRecPage(int newNum)
{
numRecord = newNum;
await ReloadData();
isLoading = false;
}
protected string TextReduce(string textOriginal, int maxChar)
{
string answ = textOriginal;
@@ -336,7 +450,7 @@ namespace MP.Prog.Pages
MServ.File_Filter.OnlyActive = value;
var pUpd = Task.Run(async () =>
{
await AsyncReload();
await ReloadAsync();
});
pUpd.Wait();
}
@@ -361,7 +475,7 @@ namespace MP.Prog.Pages
MServ.File_Filter.OnlyMod = value;
var pUpd = Task.Run(async () =>
{
await AsyncReload();
await ReloadAsync();
});
pUpd.Wait();
}
@@ -386,7 +500,7 @@ namespace MP.Prog.Pages
MServ.File_Filter.OnlyNoTag = value;
var pUpd = Task.Run(async () =>
{
await AsyncReload();
await ReloadAsync();
});
pUpd.Wait();
}
@@ -411,7 +525,7 @@ namespace MP.Prog.Pages
MServ.File_Filter.SearchVal = value;
var pUpd = Task.Run(async () =>
{
await AsyncReload();
await ReloadAsync();
});
pUpd.Wait();
}
@@ -436,7 +550,7 @@ namespace MP.Prog.Pages
MServ.File_Filter.FileName = value;
var pUpd = Task.Run(async () =>
{
await AsyncReload();
await ReloadAsync();
});
pUpd.Wait();
}
@@ -461,7 +575,7 @@ namespace MP.Prog.Pages
MServ.File_Filter.IdxMacchina = value;
var pUpd = Task.Run(async () =>
{
await AsyncReload();
await ReloadAsync();
});
pUpd.Wait();
}
@@ -486,7 +600,32 @@ namespace MP.Prog.Pages
MServ.File_Filter.Tag = value;
var pUpd = Task.Run(async () =>
{
await AsyncReload();
await ReloadAsync();
});
pUpd.Wait();
}
}
}
private string SelUserName
{
get
{
string answ = "";
if (MServ.File_Filter != null)
{
answ = MServ.File_Filter.UserName;
}
return answ;
}
set
{
if (!MServ.File_Filter.UserName.Equals(value))
{
MServ.File_Filter.UserName = value;
var pUpd = Task.Run(async () =>
{
await ReloadAsync();
});
pUpd.Wait();
}
@@ -532,7 +671,7 @@ namespace MP.Prog.Pages
{
DeleteDialogOpen = false;
currPage = 1;
await AsyncReload();
await ReloadAsync();
//StateHasChanged();
}
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo gestione Programmi MAPO</i>
<h4>Versione: 6.16.2410.2211</h4>
<h4>Versione: 6.16.2410.2318</h4>
<br />
Note di rilascio:
<ul>
+1 -1
View File
@@ -1 +1 @@
6.16.2410.2211
6.16.2410.2318
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2410.2211</version>
<version>6.16.2410.2318</version>
<url>https://nexus.steamware.net/repository/SWS/MP-PROG/stable/LAST/MP.Prog.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-PROG/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+30 -2
View File
@@ -8,13 +8,17 @@ using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using MP.Prog.Data;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace MP.Prog
@@ -41,10 +45,24 @@ namespace MP.Prog
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// aggiunt base URL x routing corretto
app.UsePathBase(Configuration.GetValue<string>("ServerConf:BaseUrl"));
if (env.IsDevelopment() || env.IsStaging())
{
app.UseDeveloperExceptionPage();
// valido solo in sviluppo
app.UseSwagger(c =>
{
c.RouteTemplate = "/swagger/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("v1/swagger.json", "MP-PROG.Api");
});
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
@@ -91,7 +109,7 @@ namespace MP.Prog
app.UseEndpoints(endpoints =>
{
//endpoints.MapControllers();
endpoints.MapControllers();
endpoints.MapBlazorHub();
//endpoints.MapHealthChecksUI();
//endpoints.MapHealthChecks("/health", new HealthCheckOptions
@@ -107,6 +125,14 @@ namespace MP.Prog
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "MP-PROG.Api", Version = "v1" });
// Set the comments path for the Swagger JSON and UI.
var xmlPath = Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml");
c.IncludeXmlComments(xmlPath);
});
// Aggiunta auth windows
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
@@ -147,6 +173,8 @@ namespace MP.Prog
// avvio oggetto shared x redis...
var redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis);
services.AddControllers()
.AddJsonOptions(c => c.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve);
services.AddLocalization();
+1 -1
View File
@@ -1,5 +1,5 @@
{
//"DetailedErrors": true,
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
+1 -1
View File
@@ -1,5 +1,5 @@
<div class="text-end">
@CurrRecord.LastDuration.ToString("N3") sec <button class="btn btn-sm @btnCss" @onclick="toggleDetail"><i class="far @iconCss"></i></button>
<b>@($"{CurrRecord.LastDuration:N3}")</b> <sub>sec</sub> <button class="btn btn-sm @btnCss" @onclick="toggleDetail" title="Ultima esecuzione"><i class="far @iconCss"></i></button>
</div>
@if (showDetail)
{
+15 -10
View File
@@ -1,5 +1,5 @@
<div class="card text-bg-light">
<div class="card text-bg-light shadow">
<div class="card-header table-primary p-1">
<div class="d-flex justify-content-between">
<div class="px-2">
@@ -54,15 +54,20 @@
<div class="small">@($"{record.DtEnd:yyyy-MM.dd ddd}")</div>
</td>
<td class="text-right">
<div>
@if (@record.IsError)
{
<i class="far fa-thumbs-down text-danger"></i>
}
else
{
<i class="far fa-thumbs-up text-success"></i>
}
<div class="d-flex justify-content-between">
<div class="px-0">
@if (@record.IsError)
{
<i class="far fa-thumbs-down text-danger"></i>
}
else
{
<i class="far fa-thumbs-up text-success"></i>
}
</div>
<div class="px-0">
<b>@($"{record.Duration:N3}")</b> <sub>sec</sub>
</div>
</div>
<div class="small">@record.Result</div>
</td>
+2 -2
View File
@@ -4,8 +4,8 @@
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>MP.Stats</RootNamespace>
<UserSecretsId>826e877c-ba70-4253-84cb-d0b1cafd4440</UserSecretsId>
<Version>6.16.2409.0409</Version>
<Version>6.16.2409.0409</Version>
<Version>6.16.2410.2318</Version>
<Version>6.16.2410.2318</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="row">
<div class="@mainCss">
<div class="card">
<div class="card shadow">
<div class="card-header table-primary p-1">
<div class="d-flex justify-content-between">
<div class="px-2">
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo statistiche MAPO</i>
<h4>Versione: 6.16.2409.0409</h4>
<h4>Versione: 6.16.2410.2318</h4>
<br />
Note di rilascio:
<ul>
+1 -1
View File
@@ -1 +1 @@
6.16.2409.0409
6.16.2410.2318
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2409.0409</version>
<version>6.16.2410.2318</version>
<url>https://nexus.steamware.net/repository/SWS/MP-STATS/stable/LAST/MP.Stats.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-STATS/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>