Merge branch 'release/AddPresentStat02'

This commit is contained in:
Samuele Locatelli
2024-03-07 10:38:01 +01:00
13 changed files with 546 additions and 85 deletions
+111
View File
@@ -615,6 +615,117 @@ namespace GPW.CORE.Data.Controllers
return dbResult;
}
/// <summary>
/// Recupera l'elenco dei dettaglio giornaliero attività di tutti i dipendenti, dato giorno riferimento
/// </summary>
/// <param name="dtRif">Data di riferimento</param>
/// <returns></returns>
public List<DailyDataDTO> DailyDetailAllDip(DateTime dtRif)
{
//resetto valoredtRif a giornata (mezzanotte)
dtRif = dtRif.Date;
// init dati necessari
List<DailyDataDTO> dbResult = new List<DailyDataDTO>();
List<TimbratureExplModel> rawTimbExp = new List<TimbratureExplModel>();
List<TimbratureModel> rawTimbr = new List<TimbratureModel>();
List<RegAttivitaModel> rawRegAtt = new List<RegAttivitaModel>();
List<RilievoTempModel> rawRilTemp = new List<RilievoTempModel>();
List<CheckVc19Model> rawCheckC19 = new List<CheckVc19Model>();
// fermate/festività/ferie
List<CalFesteFerieModel> rawFermateAz = new List<CalFesteFerieModel>();
List<RegMalattieModel> rawMalattie = new List<RegMalattieModel>();
List<RegRichiesteModel> rawRichDip = new List<RegRichiesteModel>();
// cerco su DB recuperando set di dati....
using (GPWContext localDbCtx = new GPWContext(_configuration))
{
// recupero intero set dati timbrature e attività....
rawTimbExp = localDbCtx
.DbSetTimbratureExpl
.Where(x => dtRif == x.DataLav)
.ToList();
rawTimbr = localDbCtx
.DbSetTimbrature
.Where(x => dtRif == x.DataOra.Date)
.ToList();
rawRegAtt = localDbCtx
.DbSetRegAttivita
.Where(x => dtRif == x.Inizio.Date)
.Include(a => a.FasiNav).ThenInclude(p => p.ProgettoNav).ThenInclude(c => c.ClienteNav)
.ToList();
rawRilTemp = localDbCtx
.DbSetRilievoTemp
.Where(x => dtRif <= x.DtRilievo.Date)
.ToList();
rawCheckC19 = localDbCtx
.DbSetCheckVc19
.Where(x => dtRif <= x.DtCheck.Date)
.ToList();
// recupero fermate e malattie/permessi del dipendente...
rawFermateAz = localDbCtx
.DbSetCalFesteFerie
.Where(x => x.data == dtRif)
.ToList();
rawMalattie = localDbCtx
.DbSetRegMalattie
.Where(x => x.DtInizio.Date <= dtRif && x.DtInizio.AddDays(x.NumGG) >= dtRif)
.ToList();
rawRichDip = localDbCtx
.DbSetRegRichieste
.Where(x => x.DtStart.Date <= dtRif && x.DtEnd >= dtRif)
.ToList();
// calcolo elenco DIP
List<int> listDip = rawTimbExp.Select(x => x.IdxDipendente).Distinct().ToList();
// x ogni giorno scompongo il set di info...
foreach (var idxDip in listDip)
{
DailyDataDTO currDayDto = new DailyDataDTO()
{
IdxDipendente = idxDip,
DtRif = dtRif,
ListRA = rawRegAtt
.Where(x => x.IdxDipendente == idxDip)
.OrderBy(x => x.Inizio)
.ToList(),
ListTimbr = rawTimbr
.Where(x => x.IdxDipendente == idxDip)
.OrderBy(x => x.DataOra)
.ToList(),
TimbrExpl = rawTimbExp
.Where(x => x.IdxDipendente == idxDip)
.FirstOrDefault(),
ListRilTemp = rawRilTemp
.Where(x => x.IdxDipendente == idxDip)
.ToList(),
ListCheckC19 = rawCheckC19
.Where(x => x.IdxDipendente == idxDip)
.ToList(),
ListFermateAzienda = rawFermateAz,
ListMalattie = rawMalattie
.Where(x => x.IdxDipendente == idxDip)
.ToList(),
ListFerieDip = rawRichDip
.Where(x => x.IdxDipendente == idxDip)
.ToList(),
ListRichiesteDip = rawRichDip
.Where(x => x.IdxDipendente == idxDip)
.ToList()
};
dbResult.Add(currDayDto);
}
}
return dbResult;
}
public bool DbForceMigrate()
{
bool answ = false;
+19 -3
View File
@@ -4,15 +4,31 @@
<div class="p-1" style="width: 3.4rem;">
@if (IsTitle)
{
<b>DAY</b>
@if (MultiUser)
{
<div class="text-uppercase"><b>@DtRif.ToString("ddd")</b></div>
<div class="small">@DtRif.ToString("dd.MM")</div>
}
else
{
<b>DAY</b>
}
}
else
{
if (DayDTO != null)
{
<div class="@dayCardCss text-center text-uppercase">
<div class="text-uppercase"><b>@DayDTO.DtRif.ToString("ddd")</b></div>
<div class="small">@DayDTO.DtRif.ToString("dd.MM")</div>
@if (MultiUser)
{
<div class="small text-truncate" title="@Cognome"><b>@Cognome</b></div>
<div class="small text-truncate" title="@Nome">@Nome</div>
}
else
{
<div class="text-uppercase"><b>@DayDTO.DtRif.ToString("ddd")</b></div>
<div class="small">@DayDTO.DtRif.ToString("dd.MM")</div>
}
@if (hasFest && DayDTO.ListFermateAzienda != null && DayDTO.ListFermateAzienda.Count > 0)
{
@foreach (var fermata in DayDTO.ListFermateAzienda)
+97 -64
View File
@@ -8,68 +8,12 @@ namespace GPW.CORE.WRKLOG.Components
{
#region Public Properties
public string cssBadgeLav
{
get
{
string bCtr = "btn";
string answ = $"{bCtr}-light";
var deltaComm = oreComm - oreLav;
if (Math.Abs(deltaComm) < 0.5)
{
answ = $"{bCtr}-primary bg-opacity-75";
}
else if (Math.Abs(deltaComm) < 1)
{
answ = $"{bCtr}-warning";
}
else
{
answ = $"{bCtr}-danger";
}
return answ;
}
}
public string cssCheck
{
get => OkVC19 ? "text-success" : "text-secondary";
}
public string cssThermo
{
get
{
string answ = "";
// verifico in base a ok temp o meno...
if (OkTemp)
{
// colore in base al valore...
var currTemp = tempRil;
if (currTemp >= (decimal)37.5)
{
answ = "text-danger";
}
else if (currTemp >= 37)
{
answ = "text-warning";
}
else
{
answ = "text-success";
}
}
else
{
answ = "text-secondary";
}
return answ;
}
}
[Parameter]
public DailyDataDTO? DayDTO { get; set; }
[Parameter]
public DateTime DtRif { get; set; } = DateTime.MinValue.Date;
[Parameter]
public bool EnableActionMenu { get; set; } = true;
@@ -91,11 +35,11 @@ namespace GPW.CORE.WRKLOG.Components
[Parameter]
public List<AnagFasiModel> ListFasi { get; set; } = null!;
[Parameter]
public bool MultiUser { get; set; } = false;
[Parameter]
public EventCallback<DailyDataDTO> PeriodSelected { get; set; }
#if false
//public EventCallback<List<TimbratureModel>?> PeriodSelected { get; set; }
#endif
[Parameter]
public EventCallback<DailyDataDTO> ReqDayAgenda { get; set; }
@@ -128,7 +72,7 @@ namespace GPW.CORE.WRKLOG.Components
{
await PeriodSelected.InvokeAsync(DayDTO);
#if false
//await PeriodSelected.InvokeAsync(DayDTO.ListTimbr);
//await PeriodSelected.InvokeAsync(DayDTO.ListTimbr);
#endif
}
}
@@ -179,7 +123,7 @@ namespace GPW.CORE.WRKLOG.Components
{
await PeriodSelected.InvokeAsync(DayDTO);
#if false
//await PeriodSelected.InvokeAsync(DayDTO.ListTimbr);
//await PeriodSelected.InvokeAsync(DayDTO.ListTimbr);
#endif
}
}
@@ -188,6 +132,78 @@ namespace GPW.CORE.WRKLOG.Components
#region Private Properties
private string Cognome
{
get
{
string answ = "NA";
if (DayDTO != null)
{
answ = DayDTO.TimbrExpl.CognomeNome.Split(" ")[0] ?? " NA";
}
return answ;
}
}
private string cssBadgeLav
{
get
{
string bCtr = "btn";
string answ = $"{bCtr}-light";
var deltaComm = oreComm - oreLav;
if (Math.Abs(deltaComm) < 0.5)
{
answ = $"{bCtr}-primary bg-opacity-75";
}
else if (Math.Abs(deltaComm) < 1)
{
answ = $"{bCtr}-warning";
}
else
{
answ = $"{bCtr}-danger";
}
return answ;
}
}
private string cssCheck
{
get => OkVC19 ? "text-success" : "text-secondary";
}
private string cssThermo
{
get
{
string answ = "";
// verifico in base a ok temp o meno...
if (OkTemp)
{
// colore in base al valore...
var currTemp = tempRil;
if (currTemp >= (decimal)37.5)
{
answ = "text-danger";
}
else if (currTemp >= 37)
{
answ = "text-warning";
}
else
{
answ = "text-success";
}
}
else
{
answ = "text-secondary";
}
return answ;
}
}
private string dayCardCss
{
get => hasTimb ? "border border-dark text-dark rounded shadow" : "text-secondary rounded";
@@ -223,6 +239,23 @@ namespace GPW.CORE.WRKLOG.Components
get => DayDTO == null || DayDTO.ListRA == null || DayDTO.ListRA.Count == 0;
}
private string Nome
{
get
{
string answ = "NA";
if (DayDTO != null)
{
answ = DayDTO.TimbrExpl.CognomeNome.Replace($"{Cognome} ", "");
}
return answ;
}
}
#if false
//public EventCallback<List<TimbratureModel>?> PeriodSelected { get; set; }
#endif
private bool OkTemp
{
get
+42
View File
@@ -770,6 +770,48 @@ namespace GPW.CORE.WRKLOG.Data
Log.Debug($"DailyDetails | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco dei dettaglio giornaliero attività di tutti i dipendenti, dato giorno riferimento
/// </summary>
/// <param name="dtRif">Data di riferimento</param>
/// <returns></returns>
public async Task<List<DailyDataDTO>> DailyDetailAllDip(DateTime dtRif)
{
string source = "DB";
List<DailyDataDTO>? dbResult = new List<DailyDataDTO>();
string currKey = $"{rKeyDailyData}:ALL:{dtRif.ToString("yyyy-MM-dd")}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<DailyDataDTO>>(rawData);
if (tempResult == null)
{
dbResult = new List<DailyDataDTO>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.DailyDetailAllDip(dtRif);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, FastCache);
}
if (dbResult == null)
{
dbResult = new List<DailyDataDTO>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"DailyDetailAllDip | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
public string DeriptData(string encData)
{
+1 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>3.0.2403.0517</Version>
<Version>3.0.2403.0710</Version>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
+40
View File
@@ -0,0 +1,40 @@
@page "/Presenze"
@attribute [Authorize]
<div class="card">
<div class="card-header table-primary py-1">
<div class="row">
<h3>Presenze</h3>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
<DayHoriz IsTitle="true" StartHour="@startHour" EndHour="@endHour" ListFasi="@ListFasi" IdxDipSel="@IdxDipendente" MultiUser="true" DtRif="@DtSelected"></DayHoriz>
</div>
@if (ListRecords == null)
{
@for (int i = 0; i < 7; i++)
{
<div class="col-12 my-3">
<div class="placeholder-glow">
<span class="placeholder col-6 mb-1"></span>
<span class="placeholder col-5 mb-1"></span>
<span class="placeholder fs-1 col-6"></span>
<span class="placeholder fs-1 col-5"></span>
</div>
</div>
}
}
else
{
@foreach (var currItem in ListRecords)
{
<div class="col-12">
<DayHoriz DayDTO="@currItem" StartHour="@startHour" EndHour="@endHour" ListFasi="@ListFasi" IdxDipSel="@currItem.IdxDipendente" MultiUser="true"></DayHoriz>
</div>
}
}
</div>
</div>
</div>
+223
View File
@@ -0,0 +1,223 @@
using GPW.CORE.Data.DbModels;
using GPW.CORE.Data.DTO;
using GPW.CORE.WRKLOG.Data;
using Microsoft.AspNetCore.Components;
namespace GPW.CORE.WRKLOG.Pages
{
public partial class Presenze : IDisposable
{
#region Public Methods
public void Dispose()
{
UITimer?.Dispose();
}
#endregion Public Methods
#region Protected Fields
protected DateTime lastRefresh = DateTime.Now;
/// <summary>
/// Timer per refresh dati
///
/// https://stackoverflow.com/questions/63060065/blazor-timer-call-async-api-task-to-update-ui https://www.janduniec.blog/index.php/100/timers-in-blazor/
/// </summary>
protected System.Timers.Timer UITimer = new System.Timers.Timer();
#endregion Protected Fields
#region Protected Properties
[Inject]
protected MessageService AppMServ { get; set; } = null!;
[Inject]
protected GpwDataService GDataServ { get; set; } = null!;
#endregion Protected Properties
#region Private Fields
private int _endHour = 18;
private int _startHour = 8;
private DateTime DtSelected = DateTime.Today;
private int IdxDipendente = 0;
private List<AnagFasiModel> ListFasi = new List<AnagFasiModel>();
private List<DailyDataDTO>? ListRecords = null;
#endregion Private Fields
#region Private Properties
private int endHour
{
get => _endHour;
set
{
// fix --> max 24...
_endHour = value < 25 ? value : 24;
}
}
private bool isLoading { get; set; } = false;
private int startHour
{
get => _startHour;
set
{
// fix --> MIN 0...
_startHour = value > 0 ? value : 0;
}
}
#endregion Private Properties
#region Private Methods
private void checkHourRange()
{
int minHour = 12;
int maxHour = 14;
int currStart = 12;
int currEnd = 12;
int roundMin = 30;
//int extraEnd = 30;
if (ListRecords != null)
{
// per ogni giornata
foreach (var giornata in ListRecords)
{
if (giornata.ListTimbr != null)
{
var minRecTimb = giornata.ListTimbr
.Where(x => x.Entrata == true)
.OrderBy(x => x.DataOra)
.FirstOrDefault();
if (minRecTimb != null)
{
if (minHour > minRecTimb.DataOra.AddMinutes(roundMin / 3).Hour)
{
minHour = minRecTimb.DataOra.AddMinutes(roundMin / 3).Hour;
}
}
var maxRecTimb = giornata.ListTimbr
.Where(x => x.Entrata == false)
.OrderByDescending(x => x.DataOra)
.FirstOrDefault();
if (maxRecTimb != null)
{
if (maxHour < maxRecTimb.DataOra.AddMinutes(-roundMin / 3).Hour)
{
maxHour = maxRecTimb.DataOra.AddMinutes(-roundMin / 3).Hour + 1;
}
}
}
if (giornata.ListRA != null)
{
var minRecRa = giornata.ListRA
.OrderBy(x => x.Inizio)
.FirstOrDefault();
if (minRecRa != null)
{
// verifico se cambia giorno inizio/fine --> seleziono la mezzanotte
currStart = minRecRa.Inizio.AddMinutes(-roundMin).Date.Equals(minRecRa.Fine.Date) ? minRecRa.Inizio.AddMinutes(-roundMin).Hour : 0;
if (minHour > currStart)
{
minHour = currStart;
}
}
var maxRecRa = giornata.ListRA
.OrderByDescending(x => x.Fine)
.FirstOrDefault();
if (maxRecRa != null)
{
// verifico se cambia giorno inizio/fine --> seleziono la mezzanotte
currEnd = maxRecRa.Inizio.Date.Equals(maxRecRa.Fine.AddMinutes(roundMin).Date) ? maxRecRa.Fine.AddMinutes(roundMin).Hour + 1 : 24;
if (maxHour < currEnd)
{
maxHour = currEnd;
}
}
}
}
}
startHour = minHour;// - 1;
checkOrarioDip();
// minimo 6 h... x cui "allungo" verso fine
if (maxHour - minHour < AppMServ.orarioDip)
{
minHour--;
maxHour = minHour + AppMServ.orarioDip;
}
endHour = maxHour;// + 1;
}
protected override async Task OnParametersSetAsync()
{
ListRecords = null;
await InvokeAsync(StateHasChanged);
await Task.Delay(1);
await TryReload();
// avvio un timer infinito x refresh pagina COMUNQUE ogni 60 sec anche non succedesse nulla...
UITimer = new System.Timers.Timer
{
Interval = 1000 * 60,
AutoReset = true,
Enabled = true
};
UITimer.Elapsed += async (s, ea) => await TryReload();
}
protected async Task TryReload()
{
// effettuo reload SOLO SE no ho editing in corso...
await ReloadData();
await InvokeAsync(StateHasChanged);
}
private void checkOrarioDip()
{
// calcolo minimo da cod orario dipendente
if (AppMServ.orarioDip == 0)
{
AnagOrariModel? schemaOrario = null;
// recupero da DB schema settimanale...
var pUpd = Task.Run(async () =>
{
schemaOrario = await GDataServ.AnagOrarioByDip(AppMServ.IdxDipendente);
});
pUpd.Wait();
if (schemaOrario != null)
{
AppMServ.orarioDip = (int)(Math.Ceiling(schemaOrario.oreOrdSett / 5));
}
}
}
private async Task ReloadData()
{
isLoading = true;
UITimer.Stop();
ListRecords = await GDataServ.DailyDetailAllDip(DtSelected);
await Task.Delay(1);
checkHourRange();
await Task.Delay(1);
lastRefresh = DateTime.Now;
UITimer.Start();
isLoading = false;
}
#endregion Private Methods
}
}
-3
View File
@@ -577,8 +577,5 @@ namespace GPW.CORE.WRKLOG.Pages
#endregion Private Methods
#if false
//protected void PeriodoSelect(List<TimbratureModel> newList)
#endif
}
}
+9 -11
View File
@@ -26,18 +26,16 @@
@if (isAdmin)
{
<div class="px-1">
@if (!string.IsNullOrEmpty(lblDisplay))
{
<div class="alert alert-info mb-0 py-1">
<NavLink class="btn btn-info btn-sm mb-0 py-1" href="Presenze">
@if (!string.IsNullOrEmpty(lblDisplay))
{
@lblDisplay
</div>
}
else
{
<div class="alert alert-info mb-0 py-1">
Presenti: @numPres di @numTot
</div>
}
}
else
{
<span>Presenti: @numPres di @numTot</span>
}
</NavLink>
</div>
}
</div>
@@ -3,6 +3,7 @@ using GPW.CORE.Data.DbModels;
using GPW.CORE.WRKLOG.Data;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Routing;
using Newtonsoft.Json;
namespace GPW.CORE.WRKLOG.Shared
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>GPW - Gestione Presenze Web</i>
<h4>Versione: 3.0.2403.0517</h4>
<h4>Versione: 3.0.2403.0710</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
3.0.2403.0517
3.0.2403.0710
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>3.0.2403.0517</version>
<version>3.0.2403.0710</version>
<url>http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html</changelog>
<mandatory>false</mandatory>