Add graphs generated from journey data
authorDavid Llewellyn-Jones <david@flypig.co.uk>
Sun, 22 Jul 2018 14:47:15 +0000 (15:47 +0100)
committerDavid Llewellyn-Jones <david@flypig.co.uk>
Sun, 22 Jul 2018 14:47:15 +0000 (15:47 +0100)
Average journey time for each weekday.
Average number of cyclists passed for each weekday.

harbour-pedalo.pro
qml/pages/Stats.qml
src/harbour-pedalo.cpp
src/stats.cpp
src/stats.h
src/statsmodel.cpp
src/statsmodel.h
src/statsweekdayave.cpp [new file with mode: 0644]
src/statsweekdayave.h [new file with mode: 0644]
src/statsweekdaycongestion.cpp [new file with mode: 0644]
src/statsweekdaycongestion.h [new file with mode: 0644]

index 7c12a39..7c01192 100644 (file)
@@ -35,7 +35,9 @@ SOURCES += src/harbour-pedalo.cpp \
     src/imageprovider.cpp \
     src/graph.cpp \
     src/statsmodel.cpp \
-    src/stats.cpp
+    src/stats.cpp \
+    src/statsweekdayave.cpp \
+    src/statsweekdaycongestion.cpp
 
 DISTFILES += qml/harbour-pedalo.qml \
     qml/cover/CoverPage.qml \
@@ -79,4 +81,6 @@ HEADERS += \
     src/imageprovider.h \
     src/graph.h \
     src/statsmodel.h \
-    src/stats.h
+    src/stats.h \
+    src/statsweekdayave.h \
+    src/statsweekdaycongestion.h
index e082b0f..8e13f97 100644 (file)
@@ -78,9 +78,16 @@ Page {
             height: (isPortrait ? statsPage.height / 2.0 : statsPage.height) - Theme.paddingLarge
             itemWidth: width
             clip: true
+            anchors.left: isPortrait ? statsColumn.left : statsColumn.right
+            anchors.leftMargin: 0
 
             y: (isPortrait ? (statsPage.height / 2.0) : statsColumn.y)
 
+            onModelChanged: {
+                console.log("Model changed");
+                model.updateAll();
+            }
+
             model: statsmodel
             delegate: Rectangle {
                 width: graphsView.itemWidth
@@ -89,25 +96,25 @@ Page {
 
                 SectionHeader {
                     id: sectionHeaderItem
-                    text: "item " + index
+                    text: title
                 }
 
                 Graph {
                     id: graph
-                    width: parent.width - 2 * Theme.horizontalPageMargin
+                    width: parent.width - Theme.horizontalPageMargin
                     anchors.top: sectionHeaderItem.bottom
                     height: (isPortrait ? (statsPage.height / 2.0) - Theme.paddingLarge : statsPage.height - Theme.paddingLarge - headerItem.height) - sectionHeaderItem.height
-                    anchors.horizontalCenter: parent.horizontalCenter
+                    anchors.left: parent.left
                     model: values
                     labelsx: labels
                     //labelsy: ["0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"]
-                    unitsy: "%"
+                    unitsy: units
                     primary: Theme.primaryColor
                     secondary: Theme.highlightColor
                     highlight: Theme.highlightColor
-                    miny: 0.0
-                    maxy: 1.0
-                    stepy: 0.1
+                    miny: minval
+                    maxy: maxval
+                    stepy: step
                     gap: 0.1
                     fontsize: Theme.fontSizeExtraSmall
                     /*
index abbe2be..35fc77f 100644 (file)
@@ -12,6 +12,8 @@
 #include "settings.h"
 #include "imageprovider.h"
 #include "graph.h"
+#include "statsweekdayave.h"
+#include "statsweekdaycongestion.h"
 
 #include "harbour-pedalo.h"
 
@@ -45,19 +47,12 @@ int main(int argc, char *argv[])
     Settings::getInstance().loadSettings();
 
     StatsModel statsmodel;
-    Stats stats;
-    QList<float> data{0.1, 0.1, 0.2};
-    QStringList labels{"A", "B", "C"};
-    stats.setValues(data);
-    stats.setLabels(labels);
-    statsmodel.addStats(stats);
-
-    data = QList<float>{0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.5};
-    labels = QStringList{"M", "T", "W", "Th", "F", "S", "Su"};
-    stats.setValues(data);
-    stats.setLabels(labels);
-    statsmodel.addStats(stats);
 
+    StatsWeekdayAve statsweekdayave(&journeys);
+    statsmodel.addStats(statsweekdayave);
+
+    StatsWeekdayCongestion statsweekdaycongestion(&journeys);
+    statsmodel.addStats(statsweekdaycongestion);
 
     QFile file;
     file.setFileName(Settings::getConfigDir() + "/journeys.csv");
index c858ccb..1a80ab2 100644 (file)
@@ -1,10 +1,23 @@
 #include "stats.h"
 
-Stats::Stats()
+Stats::Stats() :
+    title("Empty graph"),
+    units("%"),
+    minval(0.0),
+    maxval(1.0),
+    step(0.1)
 {
 
 }
 
+void Stats::update() {
+    // Do nothing
+}
+
+QString Stats::getTitle() const {
+    return title;
+}
+
 QStringList Stats::getLabels() const {
     return labels;
 }
@@ -13,6 +26,26 @@ QList<float> Stats::getValues() const {
     return values;
 }
 
+QString Stats::getUnits() const {
+    return units;
+}
+
+float Stats::getMinVal() const {
+    return minval;
+}
+
+float Stats::getMaxVal() const {
+    return maxval;
+}
+
+float Stats::getStep() const {
+    return step;
+}
+
+void Stats::setTitle(QString &value) {
+    title = value;
+}
+
 void Stats::setLabels(QStringList &value) {
     labels = value;
 }
@@ -21,3 +54,18 @@ void Stats::setValues(QList<float> &value) {
     values = value;
 }
 
+void Stats::setUnits(QString &value) {
+    units = value;
+}
+
+void Stats::setMinVal(float value) {
+    minval = value;
+}
+
+void Stats::setMaxVal(float value) {
+    maxval = value;
+}
+
+void Stats::setStep(float value) {
+    step = value;
+}
index 12fb41e..73accfd 100644 (file)
@@ -8,15 +8,32 @@ class Stats
 public:
     Stats();
 
-    QStringList getLabels() const;
-    QList<float> getValues() const;
+    virtual void update();
 
-    void setLabels(QStringList &value);
-    void setValues(QList<float> &value);
+    virtual QString getTitle() const;
+    virtual QStringList getLabels() const;
+    virtual QList<float> getValues() const;
+    virtual QString getUnits() const;
+    virtual float getMinVal() const;
+    virtual float getMaxVal() const;
+    virtual float getStep() const;
 
-private:
+    virtual void setTitle(QString &value);
+    virtual void setLabels(QStringList &value);
+    virtual void setValues(QList<float> &value);
+    virtual void setUnits(QString &value);
+    virtual void setMinVal(float value);
+    virtual void setMaxVal(float value);
+    virtual void setStep(float value);
+
+protected:
+    QString title;
     QStringList labels;
     QList<float> values;
+    QString units;
+    float minval;
+    float maxval;
+    float step;
 };
 
 #endif // STATS_H
index 52090c4..b6a2ef8 100644 (file)
@@ -1,12 +1,17 @@
 #include "statsmodel.h"
 
 StatsModel::StatsModel(QObject *parent) : QAbstractListModel(parent) {
+    roles[TitleRole] = "title";
     roles[ValuesRole] = "values";
     roles[LabelsRole] = "labels";
+    roles[UnitsRole] = "units";
+    roles[MinValRole] = "minval";
+    roles[MaxValRole] = "maxval";
+    roles[StepRole] = "step";
 }
 
-void StatsModel::addStats(const Stats &stats) {
-    this->stats.append(stats);
+void StatsModel::addStats(Stats &stats) {
+    this->stats.append(&stats);
 }
 
 QHash<int, QByteArray> StatsModel::roleNames() const {
@@ -22,11 +27,21 @@ QVariant StatsModel::data(const QModelIndex & index, int role) const {
     if (index.row() < 0 || index.row() > stats.count())
         return QVariant();
 
-    const Stats &stat = stats[index.row()];
-    if (role == ValuesRole)
-        return QVariant::fromValue<QList<float>>(stat.getValues());
+    const Stats *stat = stats[index.row()];
+    if (role == TitleRole)
+        return stat->getTitle();
+    else if (role == ValuesRole)
+        return QVariant::fromValue<QList<float>>(stat->getValues());
     else if (role == LabelsRole)
-        return stat.getLabels();
+        return stat->getLabels();
+    else if (role == UnitsRole)
+        return stat->getUnits();
+    else if (role == MinValRole)
+        return stat->getMinVal();
+    else if (role == MaxValRole)
+        return stat->getMaxVal();
+    else if (role == StepRole)
+        return stat->getStep();
     return QVariant();
 }
 
@@ -34,3 +49,8 @@ void StatsModel::clear() {
     stats.clear();
 }
 
+void StatsModel::updateAll() {
+    foreach (Stats * stat, stats) {
+        stat->update();
+    }
+}
index 2c9991a..662cf79 100644 (file)
@@ -11,15 +11,20 @@ class StatsModel : public QAbstractListModel
     Q_OBJECT
 public:
     enum StatsRoles {
-        ValuesRole = Qt::UserRole + 1,
-        LabelsRole
+        TitleRole = Qt::UserRole + 1,
+        ValuesRole,
+        LabelsRole,
+        UnitsRole,
+        MinValRole,
+        MaxValRole,
+        StepRole
     };
 
     QHash<int, QByteArray> roleNames() const;
 
     StatsModel(QObject *parent = 0);
 
-    void addStats(const Stats &stats);
+    void addStats(Stats &stats);
 
     int rowCount(const QModelIndex & parent = QModelIndex()) const;
 
@@ -27,9 +32,11 @@ public:
 
     void clear();
 
+    Q_INVOKABLE void updateAll();
+
 private:
     QHash<int, QByteArray> roles;
-    QList<Stats> stats;
+    QList<Stats *> stats;
 };
 
 #endif // STATSMODEL_H
diff --git a/src/statsweekdayave.cpp b/src/statsweekdayave.cpp
new file mode 100644 (file)
index 0000000..63e595e
--- /dev/null
@@ -0,0 +1,52 @@
+#include <QDebug>
+
+#include "statsweekdayave.h"
+
+StatsWeekdayAve::StatsWeekdayAve(JourneyModel * journeys) :
+    journeys(journeys)
+{
+    title = "Average journey time (mins)";
+    units = "";
+    labels = QStringList{"M", "T", "W", "Th", "F", "S", "Su"};
+}
+
+void StatsWeekdayAve::update() {
+    double duration[7];
+    unsigned int count[7];
+    int pos;
+
+    qDebug() << "Calculating values";
+    values.clear();
+
+    for (pos = 0; pos < 7; pos++) {
+        duration[pos] = 0.0;
+        count[pos] = 0u;
+    }
+
+    foreach (Journey const &journey, journeys->getData()) {
+        QDate date = journey.getStartDate();
+        int dayofweek = date.dayOfWeek() - 1;
+        if (dayofweek >= 0) {
+            duration[dayofweek] += journey.getDuration();
+            count[dayofweek]++;
+        }
+    }
+
+    maxval = 0.0;
+    for (pos = 0; pos < 7; pos++) {
+        float result = 0.0f;
+        if (count[pos] > 0) {
+            result = ((duration[pos] / 60.0) / (double)count[pos]);
+        }
+        if (result > maxval) {
+            maxval = result;
+        }
+        values << result;
+    }
+
+    step = qRound(maxval / 5.0);
+
+    qDebug() << "Calculated values";
+}
+
+
diff --git a/src/statsweekdayave.h b/src/statsweekdayave.h
new file mode 100644 (file)
index 0000000..1a36d32
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef STATSWEEKDAYAVE_H
+#define STATSWEEKDAYAVE_H
+
+#include "journeymodel.h"
+
+#include "stats.h"
+
+class StatsWeekdayAve : public Stats
+{
+public:
+    StatsWeekdayAve(JourneyModel * journeys);
+
+    void update();
+private:
+    JourneyModel * journeys;
+};
+
+#endif // STATSWEEKDAYAVE_H
diff --git a/src/statsweekdaycongestion.cpp b/src/statsweekdaycongestion.cpp
new file mode 100644 (file)
index 0000000..685e175
--- /dev/null
@@ -0,0 +1,50 @@
+#include <QDebug>
+
+#include "statsweekdaycongestion.h"
+
+StatsWeekdayCongestion::StatsWeekdayCongestion(JourneyModel * journeys) :
+    journeys(journeys)
+{
+    title = "Congested days (cycles passed)";
+    units = "";
+    labels = QStringList{"M", "T", "W", "Th", "F", "S", "Su"};
+}
+
+void StatsWeekdayCongestion::update() {
+    quint32 passed[7];
+    unsigned int count[7];
+    int pos;
+
+    qDebug() << "Calculating values";
+    values.clear();
+
+    for (pos = 0; pos < 7; pos++) {
+        passed[pos] = 0u;
+        count[pos] = 0u;
+    }
+
+    foreach (Journey const &journey, journeys->getData()) {
+        QDate date = journey.getStartDate();
+        int dayofweek = date.dayOfWeek() - 1;
+        if (dayofweek >= 0) {
+            passed[dayofweek] += journey.getOvertook() + journey.getOvertakenBy();
+            count[dayofweek]++;
+        }
+    }
+
+    maxval = 0.0;
+    for (pos = 0; pos < 7; pos++) {
+        float result = 0.0f;
+        if (count[pos] > 0) {
+            result = ((double)passed[pos] / (double)count[pos]);
+        }
+        if (result > maxval) {
+            maxval = result;
+        }
+        values << result;
+    }
+
+    step = qRound(maxval / 5.0);
+
+    qDebug() << "Calculated values";
+}
diff --git a/src/statsweekdaycongestion.h b/src/statsweekdaycongestion.h
new file mode 100644 (file)
index 0000000..2b0dda2
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef STATSWEEKDAYCONGESTION_H
+#define STATSWEEKDAYCONGESTION_H
+
+#include "journeymodel.h"
+
+#include "stats.h"
+
+class StatsWeekdayCongestion : public Stats
+{
+public:
+    StatsWeekdayCongestion(JourneyModel * journeys);
+
+    void update();
+
+private:
+    JourneyModel * journeys;
+};
+
+#endif // STATSWEEKDAYCONGESTION_H