From 54452ab79ac01a42a85692b24c75045cd4871204 Mon Sep 17 00:00:00 2001
From: Mark Watkins <jedimark@users.sourceforge.net>
Date: Tue, 6 May 2014 21:54:41 +1000
Subject: [PATCH] Added a new monthly statistics mode, and a switch to toggle

---
 sleepyhead/SleepLib/profiles.h |   5 +-
 sleepyhead/mainwindow.cpp      |  26 ++++++++
 sleepyhead/mainwindow.h        |   4 ++
 sleepyhead/mainwindow.ui       |  82 ++++++++++++++++++++++--
 sleepyhead/statistics.cpp      | 110 ++++++++++++++++++++++-----------
 5 files changed, 185 insertions(+), 42 deletions(-)

diff --git a/sleepyhead/SleepLib/profiles.h b/sleepyhead/SleepLib/profiles.h
index dc8b6287..cad18a71 100644
--- a/sleepyhead/SleepLib/profiles.h
+++ b/sleepyhead/SleepLib/profiles.h
@@ -259,6 +259,7 @@ const QString STR_US_PrefCalcPercentile = "PrefCalcPercentile";
 const QString STR_US_PrefCalcMax = "PrefCalcMax";
 const QString STR_US_TooltipTimeout = "TooltipTimeout";
 const QString STR_US_ScrollDampening = "ScrollDampening";
+const QString STR_US_StatReportMode = "StatReportMode";
 
 // Parent class for subclasses that manipulate the profile.
 class ProfileSettings
@@ -640,6 +641,7 @@ class UserSettings : public ProfileSettings
         initPref(STR_US_PrefCalcMax, (int)0);
         initPref(STR_US_TooltipTimeout, (int)2500);
         initPref(STR_US_ScrollDampening, (int)50);
+        initPref(STR_US_StatReportMode, 0);
     }
 
     UnitSystem unitSystem() const { return (UnitSystem)getPref(STR_US_UnitSystem).toInt(); }
@@ -655,7 +657,7 @@ class UserSettings : public ProfileSettings
     int prefCalcMax() const { return getPref(STR_US_PrefCalcMax).toInt(); }
     int tooltipTimeout() const { return getPref(STR_US_TooltipTimeout).toInt(); }
     int scrollDampening() const { return getPref(STR_US_ScrollDampening).toInt(); }
-
+    int statReportMode() const { return getPref(STR_US_StatReportMode).toInt(); }
 
     void setUnitSystem(UnitSystem us) { setPref(STR_US_UnitSystem, (int)us); }
     void setEventWindowSize(double size) { setPref(STR_US_EventWindowSize, size); }
@@ -670,6 +672,7 @@ class UserSettings : public ProfileSettings
     void setPrefCalcMax(int i) { setPref(STR_US_PrefCalcMax, i); }
     void setTooltipTimeout(int i) { setPref(STR_US_TooltipTimeout, i); }
     void setScrollDampening(int i) { setPref(STR_US_ScrollDampening, i); }
+    void setStatReportMode(int i) { setPref(STR_US_StatReportMode, i); }
 };
 
 namespace Profiles {
diff --git a/sleepyhead/mainwindow.cpp b/sleepyhead/mainwindow.cpp
index aa879ca7..ced30d52 100644
--- a/sleepyhead/mainwindow.cpp
+++ b/sleepyhead/mainwindow.cpp
@@ -159,6 +159,16 @@ MainWindow::MainWindow(QWidget *parent) :
 
     ui->actionDebug->setChecked(PROFILE.general->showDebug());
 
+    switch(PROFILE.general->statReportMode()) {
+        case 0:
+            ui->reportModeStandard->setChecked(true);
+            break;
+        case 1:
+            ui->reportModeMonthly->setChecked(true);
+            break;
+        default:
+            PROFILE.general->setStatReportMode(0);
+    }
     if (!PROFILE.general->showDebug()) {
         ui->logText->hide();
     }
@@ -1961,3 +1971,19 @@ void MainWindow::on_statisticsView_linkClicked(const QUrl &arg1)
     //qDebug() << arg1;
     on_recordsBox_linkClicked(arg1);
 }
+
+void MainWindow::on_reportModeMonthly_clicked()
+{
+    if (PROFILE.general->statReportMode() != 1) {
+        PROFILE.general->setStatReportMode(1);
+        GenerateStatistics();
+    }
+}
+
+void MainWindow::on_reportModeStandard_clicked()
+{
+    if (PROFILE.general->statReportMode() != 0) {
+        PROFILE.general->setStatReportMode(0);
+        GenerateStatistics();
+    }
+}
diff --git a/sleepyhead/mainwindow.h b/sleepyhead/mainwindow.h
index 59b072c4..f3c2700e 100644
--- a/sleepyhead/mainwindow.h
+++ b/sleepyhead/mainwindow.h
@@ -307,6 +307,10 @@ class MainWindow : public QMainWindow
 
     void on_statisticsView_linkClicked(const QUrl &arg1);
 
+    void on_reportModeMonthly_clicked();
+
+    void on_reportModeStandard_clicked();
+
 private:
     QString getWelcomeHTML();
     void FreeSessions();
diff --git a/sleepyhead/mainwindow.ui b/sleepyhead/mainwindow.ui
index e1cdd71f..033791e6 100644
--- a/sleepyhead/mainwindow.ui
+++ b/sleepyhead/mainwindow.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>863</width>
-    <height>637</height>
+    <width>975</width>
+    <height>735</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -155,6 +155,76 @@ color: yellow;</string>
             </property>
            </widget>
           </item>
+          <item>
+           <widget class="QFrame" name="frame_3">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="frameShape">
+             <enum>QFrame::StyledPanel</enum>
+            </property>
+            <property name="frameShadow">
+             <enum>QFrame::Raised</enum>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_2">
+             <property name="spacing">
+              <number>8</number>
+             </property>
+             <property name="leftMargin">
+              <number>8</number>
+             </property>
+             <property name="topMargin">
+              <number>4</number>
+             </property>
+             <property name="rightMargin">
+              <number>4</number>
+             </property>
+             <property name="bottomMargin">
+              <number>4</number>
+             </property>
+             <item>
+              <widget class="QLabel" name="reportModeLabel">
+               <property name="text">
+                <string>Report Mode</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QRadioButton" name="reportModeStandard">
+               <property name="text">
+                <string>Standard</string>
+               </property>
+               <property name="checked">
+                <bool>true</bool>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QRadioButton" name="reportModeMonthly">
+               <property name="text">
+                <string>Monthly</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <spacer name="horizontalSpacer">
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="sizeHint" stdset="0">
+                <size>
+                 <width>40</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+            </layout>
+           </widget>
+          </item>
          </layout>
         </widget>
         <widget class="QWidget" name="helpTab">
@@ -505,7 +575,7 @@ QToolBox::tab:selected  {
           <x>0</x>
           <y>0</y>
           <width>180</width>
-          <height>516</height>
+          <height>596</height>
          </rect>
         </property>
         <property name="palette">
@@ -919,7 +989,7 @@ border: 2px solid #56789a; border-radius: 30px;
           <x>0</x>
           <y>0</y>
           <width>180</width>
-          <height>498</height>
+          <height>596</height>
          </rect>
         </property>
         <property name="palette">
@@ -2067,7 +2137,7 @@ border-radius: 10px;
           <x>0</x>
           <y>0</y>
           <width>180</width>
-          <height>498</height>
+          <height>596</height>
          </rect>
         </property>
         <property name="mouseTracking">
@@ -2128,7 +2198,7 @@ border-radius: 10px;
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>863</width>
+     <width>975</width>
      <height>22</height>
     </rect>
    </property>
diff --git a/sleepyhead/statistics.cpp b/sleepyhead/statistics.cpp
index 3aef0624..357b06b0 100644
--- a/sleepyhead/statistics.cpp
+++ b/sleepyhead/statistics.cpp
@@ -46,6 +46,7 @@ Statistics::Statistics(QObject *parent) :
                 { "ClearAirway",   SC_CPH,     MT_CPAP },
                 { "FlowLimit",  SC_CPH,     MT_CPAP },
                 { "RERA",       SC_CPH,     MT_CPAP },
+                { "CSR", SC_SPH, MT_CPAP },
 
                 { tr("Leak Statistics"),  SC_SUBHEADING, MT_CPAP },
                 { "Leak",       SC_WAVG,    MT_CPAP },
@@ -452,6 +453,24 @@ bool operator <(const UsageData &c1, const UsageData &c2)
     //return c1.value < c2.value;
 }
 
+struct Period {
+    Period() {
+    }
+    Period(QDate start, QDate end, QString header) {
+        this->start = start;
+        this->end = end;
+        this->header = header;
+    }
+    Period(const Period & copy) {
+        start=copy.start;
+        end=copy.end;
+        header=copy.header;
+    }
+    QDate start;
+    QDate end;
+    QString header;
+};
+
 QString Statistics::GenerateHTML()
 {
 
@@ -517,8 +536,14 @@ QString Statistics::GenerateHTML()
     html += "<div align=center>";
     html += QString("<table cellpadding=2 cellspacing=0 border=1 width=90%>");
 
+    int number_periods = 0;
+    if (p_profile->general->statReportMode() == 1) {
+        number_periods = 12;
+    }
 
-    QDate last = lastcpap, first = lastcpap, week = lastcpap, month = lastcpap, sixmonth = lastcpap, year = lastcpap;
+    QDate last = lastcpap, first = lastcpap;
+
+    QList<Period> periods;
 
     bool skipsection = false;;
     for (auto i = rows.begin(); i != rows.end(); ++i) {
@@ -529,20 +554,38 @@ QString Statistics::GenerateHTML()
             last = p_profile->LastGoodDay(row.type);
             first  = p_profile->FirstGoodDay(row.type);
 
-            week = last.addDays(-6);
-            month = last.addDays(-29);
-            sixmonth = last.addMonths(-6);
-            year = last.addMonths(-12);
+            periods.clear();
+            if (number_periods == 0) {
+                periods.push_back(Period(last,last,tr("Most Recent")));
+                periods.push_back(Period(qMax(last.addDays(-6), first), last, tr("Last Week")));
+                periods.push_back(Period(qMax(last.addDays(-29),first), last, tr("Last 30 Days")));
+                periods.push_back(Period(qMax(last.addMonths(-6), first), last, tr("Last 6 Months")));
+                periods.push_back(Period(qMax(last.addMonths(-12), first), last, tr("Last Year")));
+            } else {
+                QDate l=last,s=last;
 
-            if (week < first) { week = first; }
-            if (month < first) { month = first; }
-            if (sixmonth < first) { sixmonth = first; }
-            if (year < first) { year = first; }
+                periods.push_back(Period(last,last,tr("Last Session")));
+
+                bool done=false;
+                for (int j=0; j < number_periods; j++) {
+                    s=QDate(l.year(), l.month(), 1);
+                    if (s < first) {
+                        done = true;
+                        s = first;
+                    }
+                    if (p_profile->countDays(row.type, s, l)>0) {
+                        periods.push_back(Period(s, l, s.toString("MMMM")));
+                    }
+                    l= s.addDays(-1);
+                    if (done || (l < first)) break;
+                }
+            }
 
             int days = PROFILE.countDays(row.type, first, last);
             skipsection = (days == 0);
             if (days > 0) {
-                html+=QString("<tr bgcolor='"+heading_color+"'><td colspan=6 align=center><font size=+3>%1</font></td></tr>\n").arg(row.src);
+                html+=QString("<tr bgcolor='%1'><td colspan=%2 align=center><font size=+3>%3</font></td></tr>\n").
+                        arg(heading_color).arg(periods.size()+1).arg(row.src);
             }
             continue;
         }
@@ -555,9 +598,11 @@ QString Statistics::GenerateHTML()
         } else if ((row.calc == SC_HOURS) || (row.calc == SC_COMPLIANCE)) {
             name = row.src;
         } else if (row.calc == SC_COLUMNHEADERS) {
-            html += QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
-                    .arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(
-                        tr("Last 6 months")).arg(tr("Last Year"));
+            html += QString("<tr><td><b>%1</b></td>").arg(tr("Details"));
+            for (int j=0; j < periods.size(); j++) {
+                html += QString("<td><b>%1</b></td>").arg(periods.at(j).header);
+            }
+            html += "</tr>\n";
             continue;
         } else if (row.calc == SC_DAYS) {
             QDate first=p_profile->FirstGoodDay(row.type);
@@ -566,28 +611,26 @@ QString Statistics::GenerateHTML()
             int value=p_profile->countDays(row.type, first, last);
 
             if (value == 0) {
-                html+="<tr><td colspan=6 align=center>"+
-                        QString(tr("No %1 data available.")).arg(machine)
-                        +"</td></tr>";
+                html+=QString("<tr><td colspan=%1 align=center>%2</td></tr>\n").arg(periods.size()+1).
+                        arg(QString(tr("No %1 data available.")).arg(machine));
             } else if (value == 1) {
-                html+="<tr><td colspan=6 align=center>"+
-                        QString("%1 day of %2 Data on %3")
+                html+=QString("<tr><td colspan=%1 align=center>%2</td></tr>\n").arg(periods.size()+1).
+                        arg(QString("%1 day of %2 Data on %3")
                             .arg(value)
                             .arg(machine)
-                            .arg(last.toString())
-                        +"</td></tr>\n";
+                            .arg(last.toString()));
             } else {
-                html+="<tr><td colspan=6 align=center>"+
-                        QString("%1 days of %2 Data, between %3 and %4")
+                html+=QString("<tr><td colspan=%1 align=center>%2</td></tr>\n").arg(periods.size()+1).
+                        arg(QString("%1 days of %2 Data, between %3 and %4")
                             .arg(value)
                             .arg(machine)
                             .arg(first.toString())
-                            .arg(last.toString())
-                        +"</td></tr>\n";
+                            .arg(last.toString()));
             }
             continue;
         } else if (row.calc == SC_SUBHEADING) {  // subheading..
-            html+=QString("<tr bgcolor='"+subheading_color+"'><td colspan=6 align=center><b>%1</b></td></tr>\n").arg(row.src);
+            html+=QString("<tr bgcolor='%1'><td colspan=%2 align=center><b>%3</b></td></tr>\n").
+                    arg(subheading_color).arg(periods.size()+1).arg(row.src);
             continue;
         } else if (row.calc == SC_UNDEFINED) {
             continue;
@@ -598,14 +641,12 @@ QString Statistics::GenerateHTML()
             }
             name = calcnames[row.calc].arg(row.src);
         }
-
-        html += QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>\n")
-        .arg(name)
-        .arg(row.value(last,last))
-        .arg(row.value(week,last))
-        .arg(row.value(month,last))
-        .arg(row.value(sixmonth,last))
-        .arg(row.value(year,last));
+        html += QString("<tr><td>%1</td>").arg(name);
+        for (int j=0; j < periods.size(); j++) {
+            html += QString("<td>%2</td>")
+                .arg(row.value(periods.at(j).start,periods.at(j).end));
+        }
+        html += "</tr>\n";
     }
 
     html += "</table>";
@@ -1152,8 +1193,7 @@ QString Statistics::GenerateHTML()
 QString StatisticsRow::value(QDate start, QDate end)
 {
     const int decimals=2;
-    QString value = "???";
-    qDebug() << "Calculating " << src << calc << "for" << start << end;
+    QString value = "";
     float days = PROFILE.countDays(type, start, end);
 
     // Handle special data sources first