Fix and Enhanced Statistics Monthly Report

Fix Corner Display cases. Added total days used for monthly report.
This commit is contained in:
LoudSnorer 2023-12-08 17:49:00 -05:00
parent 8c5206b9a9
commit 2e27b3710e
2 changed files with 74 additions and 55 deletions

View File

@ -47,6 +47,7 @@ QString htmlMachineSettings = ""; // Device (formerly Rx) changes
QString htmlMachines = ""; // Devices used in this profile QString htmlMachines = ""; // Devices used in this profile
QString htmlReportFooter = ""; // Page footer QString htmlReportFooter = ""; // Page footer
SummaryInfo summaryInfo;
QSet<QDate> noDaysInPeriod; QSet<QDate> noDaysInPeriod;
QString alternatingColor(int& counter) { QString alternatingColor(int& counter) {
counter++; counter++;
@ -199,20 +200,26 @@ bool rxAHILessThan(const RXItem * rx1, const RXItem * rx2)
return (double(rx1->ahi) / rx1->hours) < (double(rx2->ahi) / rx2->hours); return (double(rx1->ahi) / rx1->hours) < (double(rx2->ahi) / rx2->hours);
} }
void Statistics::updateDisabledInfo() void Statistics::adjustRange(QDate& start , QDate& last) {
{ start = qMax(start,p_profile->FirstDay());
QDate lastcpap = p_profile->LastGoodDay(MT_CPAP); last = qMin(last ,p_profile->LastDay() );
QDate firstcpap = p_profile->FirstGoodDay(MT_CPAP); if (p_profile->general->statReportMode() == STAT_MODE_RANGE) {
if (lastcpap > p_profile->general->statReportDate() ) { start = qMax(start,p_profile->general->statReportRangeStart());
lastcpap = p_profile->general->statReportDate(); last = qMin(last ,p_profile->general->statReportRangeEnd() );
} else {
last = qMin(last ,p_profile->general->statReportDate() );
} }
disabledInfo.update( lastcpap, firstcpap ); start = qMin(start,last);
summaryInfo.update(start,last);
} }
void DisabledInfo::update(QDate latestDate, QDate earliestDate) void SummaryInfo::update(QDate earliestDate , QDate latestDate)
{ {
clear(); if ( (!latestDate.isValid()) || (!earliestDate.isValid()) ) return;
if ( (!latestDate.isValid()) || (!earliestDate.isValid()) || (p_profile->cpap->clinicalMode()) ) return; if ( (latestDate == last()) && (earliestDate == start()) ) return;
clear(earliestDate,latestDate);
_start = earliestDate;
_last = latestDate;
qint64 complianceHours = 3600000.0 * p_profile->cpap->complianceHours(); // conbvert to ms qint64 complianceHours = 3600000.0 * p_profile->cpap->complianceHours(); // conbvert to ms
totalDays = 1+earliestDate.daysTo(latestDate); totalDays = 1+earliestDate.daysTo(latestDate);
for (QDate date = latestDate ; date >= earliestDate ; date=date.addDays(-1) ) { for (QDate date = latestDate ; date >= earliestDate ; date=date.addDays(-1) ) {
@ -258,8 +265,9 @@ void DisabledInfo::update(QDate latestDate, QDate earliestDate)
totalDurationOfDisabledSessions/=60000 ; totalDurationOfDisabledSessions/=60000 ;
}; };
QString DisabledInfo::display(int type) QString SummaryInfo::display(QString typeStr)
{ {
int type=typeStr.toInt();
/* /*
Permissive mode: some sessions are excluded from this report, as follows: Permissive mode: some sessions are excluded from this report, as follows:
Total disabled sessions: xx, found in yy days. Total disabled sessions: xx, found in yy days.
@ -268,19 +276,23 @@ Duration of longest disabled session: aa minutes, Total duration of all disabled
*/ */
switch (type) { switch (type) {
default : default :
case 0: return QString();
//return QString(QObject::tr("Permissive mode is set (Preferences/Clinical), disabled sessions are excluded from this report"));
//return QString(QObject::tr("Permissive mode allows disabled sessions"));
return QString(QObject::tr("Permissive Mode"));
case 1: case 1:
return QString(QObject::tr("Permissive Mode"));
case 2:
if (numDisabledsessions>0) { if (numDisabledsessions>0) {
return QString(QObject::tr("Total disabled sessions: %1, found in %2 days") .arg(numDisabledsessions) .arg(numDaysWithDisabledsessions)); return QString(QObject::tr("Total disabled sessions: %1, found in %2 days") .arg(numDisabledsessions) .arg(numDaysWithDisabledsessions));
} else { } else {
return QString(QObject::tr("Total disabled sessions: %1") .arg(numDisabledsessions) ); return QString(QObject::tr("Total disabled sessions: %1") .arg(numDisabledsessions) );
} }
case 2: case 3:
return QString(QObject::tr( "Duration of longest disabled session: %1 minutes, Total duration of all disabled sessions: %2 minutes.") return QString(QObject::tr( "Duration of longest disabled session: %1 minutes, Total duration of all disabled sessions: %2 minutes.")
.arg(maxDurationOfaDisabledsession, 0, 'f', 1) .arg(totalDurationOfDisabledSessions, 0, 'f', 1)); .arg(maxDurationOfaDisabledsession, 0, 'f', 1) .arg(totalDurationOfDisabledSessions, 0, 'f', 1));
case 4:
return QString(QObject::tr( "The reportng period is %1 days between %2 and %3")
.arg(1+_start.daysTo(_last))
.arg(_start.toString(MedDateFormat))
.arg(_last.toString(MedDateFormat)));
} }
} }
@ -679,13 +691,17 @@ Statistics::Statistics(QObject *parent) :
{ {
rows.push_back(StatisticsRow(tr("CPAP Statistics"), SC_HEADING, MT_CPAP)); rows.push_back(StatisticsRow(tr("CPAP Statistics"), SC_HEADING, MT_CPAP));
if (!p_profile->cpap->clinicalMode()) { if (!p_profile->cpap->clinicalMode()) {
updateDisabledInfo(); rows.push_back(StatisticsRow("1",SC_WARNING ,MT_CPAP));
rows.push_back(StatisticsRow(disabledInfo.display(0),SC_WARNING ,MT_CPAP)); rows.push_back(StatisticsRow("2",SC_WARNING,MT_CPAP));
rows.push_back(StatisticsRow(disabledInfo.display(1),SC_WARNING2,MT_CPAP)); if (summaryInfo.size()>0)
if (disabledInfo.size()>0) { { // this row display detailed information about disabled sessions.
rows.push_back(StatisticsRow(disabledInfo.display(2),SC_WARNING2,MT_CPAP)); rows.push_back(StatisticsRow("3",SC_WARNING,MT_CPAP));
} }
} }
// this row display the reporting period. It is required for the Monthlt reports .
// but is also included for Standard and data range
rows.push_back(StatisticsRow("4",SC_MESSAGE , MT_CPAP));
rows.push_back(StatisticsRow("", SC_DAYS, MT_CPAP)); rows.push_back(StatisticsRow("", SC_DAYS, MT_CPAP));
rows.push_back(StatisticsRow("", SC_COLUMNHEADERS, MT_CPAP)); rows.push_back(StatisticsRow("", SC_COLUMNHEADERS, MT_CPAP));
rows.push_back(StatisticsRow(tr("CPAP Usage"), SC_SUBHEADING, MT_CPAP)); rows.push_back(StatisticsRow(tr("CPAP Usage"), SC_SUBHEADING, MT_CPAP));
@ -1148,9 +1164,7 @@ QString Statistics::GenerateMachineList()
//qDebug() << "Device" << m->brand() << "series" << m->series() << "model" << m->model() << "model number" << m->modelnumber(); //qDebug() << "Device" << m->brand() << "series" << m->series() << "model" << m->model() << "model number" << m->modelnumber();
QDate d1 = m->FirstDay(); QDate d1 = m->FirstDay();
QDate d2 = m->LastDay(); QDate d2 = m->LastDay();
if (d2 > p_profile->general->statReportDate() ) { adjustRange(d1,d2);
d2 = p_profile->general->statReportDate();
}
QString mn = m->modelnumber(); QString mn = m->modelnumber();
html += QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td></tr>") html += QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td></tr>")
.arg(m->brand()) .arg(m->brand())
@ -1289,6 +1303,7 @@ QString Statistics::getRDIorAHIText() {
// Create the HTML for CPAP and Oximetry usage // Create the HTML for CPAP and Oximetry usage
QString Statistics::GenerateCPAPUsage() QString Statistics::GenerateCPAPUsage()
{ {
summaryInfo.clear(p_profile->FirstDay(),p_profile->LastDay());
QList<Machine *> cpap_machines = p_profile->GetMachines(MT_CPAP); QList<Machine *> cpap_machines = p_profile->GetMachines(MT_CPAP);
QList<Machine *> oximeters = p_profile->GetMachines(MT_OXIMETER); QList<Machine *> oximeters = p_profile->GetMachines(MT_OXIMETER);
QList<Machine *> mach; QList<Machine *> mach;
@ -1317,9 +1332,7 @@ QString Statistics::GenerateCPAPUsage()
// Find first and last days with valid CPAP data // Find first and last days with valid CPAP data
QDate lastcpap = p_profile->LastGoodDay(MT_CPAP); QDate lastcpap = p_profile->LastGoodDay(MT_CPAP);
QDate firstcpap = p_profile->FirstGoodDay(MT_CPAP); QDate firstcpap = p_profile->FirstGoodDay(MT_CPAP);
if (lastcpap > p_profile->general->statReportDate() ) { adjustRange(firstcpap,lastcpap);
lastcpap = p_profile->general->statReportDate();
}
QString ahitxt = getRDIorAHIText(); QString ahitxt = getRDIorAHIText();
@ -1332,21 +1345,26 @@ QString Statistics::GenerateCPAPUsage()
int number_periods = 0; int number_periods = 0;
noDaysInPeriod.clear(); noDaysInPeriod.clear();
if (p_profile->general->statReportMode() == STAT_MODE_MONTHLY) { if (p_profile->general->statReportMode() == STAT_MODE_MONTHLY) {
int firstMonth = firstcpap.month(); QDate startMonth = lastcpap.addMonths(-12);
int lastMonth = lastcpap.month(); // Go to the the start of the next months
int years = lastcpap.year() - firstcpap.year(); firstcpap = startMonth.addDays(( 1 + (startMonth.daysInMonth()-startMonth.day()) ));
lastMonth += (12 * years); // handle time extending to next year adjustRange(firstcpap,lastcpap);
number_periods = lastMonth - firstMonth + 1;
int lastMonth = lastcpap.month() + (12*(lastcpap.year() -firstcpap.year() ) );
number_periods = 1+ (lastMonth - firstcpap.month() );
if (number_periods < 1) { if (number_periods < 1) {
qDebug() << "*** Begin" << firstcpap << "beginMonth" << firstMonth << "lastMonth" << lastMonth << "periods" << number_periods;
number_periods = 1; number_periods = 1;
} }
qDebug() << "Number of months for stats (trim to 12 max)" << number_periods; //qDebug() << "Number of months for stats (trim to 12 max)" << number_periods;
// But not more than one year // But not more than one year
if (number_periods > 12) { if (number_periods > 12) {
// should never get here.
number_periods = 12; number_periods = 12;
} }
} else if (p_profile->general->statReportMode() == STAT_MODE_STANDARD) {
firstcpap = lastcpap.addYears(-1).addDays(1);
adjustRange(firstcpap,lastcpap);
} }
QDate last = lastcpap, first = lastcpap; QDate last = lastcpap, first = lastcpap;
@ -1361,11 +1379,8 @@ QString Statistics::GenerateCPAPUsage()
QString name; QString name;
if (row.calc == SC_HEADING) { // All sections begin with a heading if (row.calc == SC_HEADING) { // All sections begin with a heading
last = p_profile->LastGoodDay(row.type); first = summaryInfo.first();
first = p_profile->FirstGoodDay(row.type); last = summaryInfo.last();
if (last > p_profile->general->statReportDate() ) {
last = p_profile->general->statReportDate();
}
// Clear the periods (columns) // Clear the periods (columns)
periods.clear(); periods.clear();
@ -1396,7 +1411,7 @@ QString Statistics::GenerateCPAPUsage()
if (p_profile->general->statReportMode() == STAT_MODE_RANGE) { if (p_profile->general->statReportMode() == STAT_MODE_RANGE) {
first = p_profile->general->statReportRangeStart(); first = p_profile->general->statReportRangeStart();
last = p_profile->general->statReportRangeEnd(); last = p_profile->general->statReportRangeEnd();
if (first > last) { first=last; }; adjustRange(first,last);
} }
// note add days or addmonths returns the start of the next day or the next month. // note add days or addmonths returns the start of the next day or the next month.
// must shorten one day for each. Month executed in Period method // must shorten one day for each. Month executed in Period method
@ -1488,14 +1503,14 @@ QString Statistics::GenerateCPAPUsage()
} else if (row.calc == SC_UNDEFINED) { } else if (row.calc == SC_UNDEFINED) {
continue; continue;
} else if (row.calc == SC_WARNING) { } else if (row.calc == SC_WARNING) {
//html+=QString("<tr bgcolor='%1'><td colspan=%2 align=center><font size='+1'>%3</font></td></tr>"). QString text = summaryInfo.display(row.src);
// arg(warning_color).arg(periods.size()+1).arg(row.src);
html+=QString("<tr bgcolor='%1'><th colspan=%2 align=center><font size='+0'><i>%3</i></font></th></tr>"). html+=QString("<tr bgcolor='%1'><th colspan=%2 align=center><font size='+0'><i>%3</i></font></th></tr>").
arg(warning_color).arg(periods.size()+1).arg(row.src); arg(warning_color).arg(periods.size()+1).arg(text);
continue; continue;
} else if (row.calc == SC_WARNING2) { } else if (row.calc == SC_MESSAGE) {
html+=QString("<tr bgcolor='%1'><th colspan=%2 align=center><font size='+0'><i>%3</i></font></th></tr>"). QString text = summaryInfo.display(row.src);
arg(warning_color).arg(periods.size()+1).arg(row.src); html+=QString("<tr><td colspan=%1 align=center>%2</th></tr>").
arg(periods.size()+1).arg(text);
continue; continue;
} else { } else {
ChannelID id = schema::channel[row.src].id(); ChannelID id = schema::channel[row.src].id();

View File

@ -22,12 +22,15 @@
#include "SleepLib/machine.h" #include "SleepLib/machine.h"
class DisabledInfo class SummaryInfo
{ {
public: public:
QString display(int); QString display(QString);
void update(QDate latest, QDate earliest) ; void update(QDate latest, QDate earliest) ;
int size() {return numDisabledsessions;}; int size() {return numDisabledsessions;};
QDate first() {return _start;};
QDate start() {return _start;};
QDate last() {return _last;};
private: private:
int totalDays ; int totalDays ;
int daysNoData ; int daysNoData ;
@ -38,8 +41,10 @@ private:
int numDaysDisabledSessionChangedCompliance ; int numDaysDisabledSessionChangedCompliance ;
double maxDurationOfaDisabledsession ; double maxDurationOfaDisabledsession ;
double totalDurationOfDisabledSessions ; double totalDurationOfDisabledSessions ;
QDate _start;
QDate _last;
public: public:
void clear () { void clear (QDate s,QDate l) {
totalDays = 0; totalDays = 0;
daysNoData = 0; daysNoData = 0;
daysOutOfCompliance = 0; daysOutOfCompliance = 0;
@ -49,8 +54,9 @@ public:
maxDurationOfaDisabledsession = 0; maxDurationOfaDisabledsession = 0;
numDaysDisabledSessionChangedCompliance = 0; numDaysDisabledSessionChangedCompliance = 0;
totalDurationOfDisabledSessions = 0; totalDurationOfDisabledSessions = 0;
_start = QDate(s);
_last = QDate(l);
}; };
}; };
@ -58,7 +64,7 @@ public:
//! \brief Type of calculation on one statistics row //! \brief Type of calculation on one statistics row
enum StatCalcType { enum StatCalcType {
SC_UNDEFINED=0, SC_COLUMNHEADERS, SC_HEADING, SC_SUBHEADING, SC_MEDIAN, SC_AVG, SC_WAVG, SC_90P, SC_MIN, SC_MAX, SC_CPH, SC_SPH, SC_AHI_RDI , SC_HOURS, SC_COMPLIANCE, SC_DAYS, SC_ABOVE, SC_BELOW , SC_WARNING , SC_WARNING2 , SC_UNDEFINED=0, SC_COLUMNHEADERS, SC_HEADING, SC_SUBHEADING, SC_MEDIAN, SC_AVG, SC_WAVG, SC_90P, SC_MIN, SC_MAX, SC_CPH, SC_SPH, SC_AHI_RDI , SC_HOURS, SC_COMPLIANCE, SC_DAYS, SC_ABOVE, SC_BELOW , SC_WARNING , SC_MESSAGE ,
SC_SELECTED_DAYS , SC_DAYS_W_DATA , SC_DAYS_WO_DATA , SC_COMPLIANCE_DAYS , SC_USED_DAY_COMPLIANCE_PERCENT , SC_NON_COMPLIANCE_DAYS , SC_MEDIAN_HOURS , SC_MEDIAN_AHI , SC_AHI_ONLY SC_SELECTED_DAYS , SC_DAYS_W_DATA , SC_DAYS_WO_DATA , SC_COMPLIANCE_DAYS , SC_USED_DAY_COMPLIANCE_PERCENT , SC_NON_COMPLIANCE_DAYS , SC_MEDIAN_HOURS , SC_MEDIAN_AHI , SC_AHI_ONLY
}; };
@ -207,10 +213,10 @@ class Statistics : public QObject
static void printReport(QWidget *parent = nullptr); static void printReport(QWidget *parent = nullptr);
void updateDisabledInfo();
static void updateReportDate(); static void updateReportDate();
void adjustRange(QDate& start , QDate& last);
protected: protected:
void loadRXChanges(); void loadRXChanges();
void saveRXChanges(); void saveRXChanges();
@ -236,8 +242,6 @@ class Statistics : public QObject
QList<QDate> record_best_ahi; QList<QDate> record_best_ahi;
QList<QDate> record_worst_ahi; QList<QDate> record_worst_ahi;
DisabledInfo disabledInfo;
signals: signals: