Example #1
0
 def __lt__(self, other):
     item0 = self.text(self.sortColumn)
     item1 = other.text(self.sortColumn)
     if isFloat(item0) and isFloat(item1):
         return float(item0) < float(item1)
     elif checkMonthYearFloat(item0) and checkMonthYearFloat(item1):
         value0 = monthYearToFloat(item0)
         value1 = monthYearToFloat(item1)
         return value0 < value1
     elif checkHourMinSecFloat(item0) and checkHourMinSecFloat(item1):
         value0 = hourMinSecToFloat(item0)
         value1 = hourMinSecToFloat(item1)
         return value0 < value1
     else:
         return item0 < item1
Example #2
0
 def _makeAvgSpeedColumn(self, df):
     ## Avg. speed was not always included in csv file
     ## If user does not have this column, create it
     if 'Avg. speed (km/h)' not in df.columns:
         times = np.array([hourMinSecToFloat(t) for t in df['Time']])
         df['Avg. speed (km/h)'] = df['Distance (km)'] / times
     return df
Example #3
0
 def update(self, values):
     """ Update items in the underlying DataFrame. 
     
         `values` should be a dict; the keys should be indices and values 
         should be dicts of column:value. If the value currently at the 
         given column and index is different from that supplied in the 
         dictionary, it will be updated.
         
         If changes are made, `dataChanged` is emitted.
         
         Example `values` structure:
             {10: {'Distance (km)':25, 'Calories':375}}
     """
     changed = []
     for index, dct in values.items():
         for col, value in dct.items():
             if self.df.at[index, col] != value:
                 self.df.at[index, col] = value
                 changed.append(index)
     if changed:
         for index in changed:
             # update the avg speed for the changed indices
             # (simpler to do this for all changed indices than also track whether
             # distance and/or time have changed)
             distance = self.df['Distance (km)'][index]
             time = hourMinSecToFloat(self.df['Time'][index])
             self.df.at[index, 'Avg. speed (km/h)'] = distance / time
         self.dataChanged.emit(changed)
Example #4
0
    def newData(self):
        dfs = self.data.splitMonths(returnType="CycleData")

        totals = [(monthYear, [
            monthData.summaryString(*args)
            for args in self.mainWindow.summary.summaryArgs
        ]) for monthYear, monthData in dfs]
        idx = self._matchColumn(
            self.column,
            [item[0] for item in self.mainWindow.summary.summaryArgs])

        try:
            totals.sort(key=lambda tup: float(tup[1][idx]), reverse=True)
        except ValueError:
            totals.sort(key=lambda tup: hourMinSecToFloat(tup[1][idx]),
                        reverse=True)
        monthYear, summaries = totals[0]
        self.time, self.distance, _, self.calories, *vals = summaries

        self.setText()

        if monthYear != self.monthYear:
            self.monthYear = monthYear
            msg = self.makeMessage(monthYear)
            return msg
        else:
            return None
Example #5
0
    def test_combine_rows(self, setup, qtbot):
        rng = np.random.default_rng()
        row = rng.integers(0, len(self.df))

        while True:
            replace = rng.integers(0, len(self.df), size=3)
            if row not in replace:
                break

        names = self.df.columns

        expected = {name: self.df.iloc[row][name] for name in names}
        expected['Time'] = hourMinSecToFloat(parseDuration(expected['Time']))

        for idx in replace:
            self.df.at[idx, 'Date'] = expected['Date']
            self.df.at[idx, 'Gear'] = expected['Gear']
            expected['Distance (km)'] += self.df.at[idx, 'Distance (km)']
            expected['Calories'] += self.df.at[idx, 'Calories']
            expected['Time'] += hourMinSecToFloat(
                parseDuration(self.df.at[idx, 'Time']))
        expected[
            'Avg. speed (km/h)'] = expected['Distance (km)'] / expected['Time']
        expected['Time'] = floatToHourMinSec(expected['Time'])

        data = CycleData(self.df)
        date = expected['Date'].strftime("%d %b %Y")

        with qtbot.waitSignal(data.dataChanged):
            data.combineRows(date)

        df = data.df[data.df['Date'] == expected['Date']]
        assert len(df) == 1

        for name in names:
            try:
                float(df.iloc[0][name])
            except:
                assert df.iloc[0][name] == expected[name]
            else:
                assert np.isclose(df.iloc[0][name], expected[name])
Example #6
0
def knownData():
    dates = [f"2021-04-{i:02}" for i in range(26, 31)]
    dates += [f"2021-05-{i:02}" for i in range(1, 6)] 
    dct = {'Date':dates,
            'Time':["00:53:27", "00:43:04", "00:42:40", "00:43:09", "00:42:28",
                    "00:43:19", "00:42:21", "00:43:04", "00:42:11", "00:43:25"],
            'Distance (km)':[30.1, 25.14, 25.08, 25.41, 25.1, 25.08, 25.13, 
                            25.21, 25.08, 25.12],
            'Gear':[6]*10}
    dct['Calories'] = [d*14.956 for d in dct['Distance (km)']]
    times = np.array([hourMinSecToFloat(t) for t in dct['Time']])
    dct['Avg. speed (km/h)'] = dct['Distance (km)'] / times
    return dct
Example #7
0
    def combineRows(self, date):
        """ Combine all rows in the dataframe with the given data. """
        i0, *idx = self.df[self.df['Date'] == parseDate(
            date, pd_timestamp=True)].index

        combinable = ['Time', 'Distance (km)', 'Calories', 'Avg. speed (km/h)']

        for i in idx:
            for name in combinable:
                if name == 'Time':
                    t0 = hourMinSecToFloat(
                        parseDuration(self.df.iloc[i0][name]))
                    t1 = hourMinSecToFloat(parseDuration(
                        self.df.iloc[i][name]))
                    newValue = floatToHourMinSec(t0 + t1)
                    self.df.at[i0, name] = newValue
                elif name == 'Avg. speed (km/h)':
                    self.df.at[i0, name] = self.df['Distance (km)'][
                        i0] / hourMinSecToFloat(self.df['Time'][i0])
                else:
                    self.df.at[i0, name] += self.df.iloc[i][name]

        self.df.drop(idx, inplace=True)
        self.dataChanged.emit(i0)
Example #8
0
    def append(self, dct):
        """ Append values in dict to DataFrame. """
        if not isinstance(dct, dict):
            msg = f"Can only append dict to CycleData, not {type(dct).__name__}"
            raise TypeError(msg)

        times = np.array([hourMinSecToFloat(t) for t in dct['Time']])
        dct['Avg. speed (km/h)'] = dct['Distance (km)'] / times

        tmpDf = pd.DataFrame.from_dict(dct)
        tmpDf = pd.concat([self.df, tmpDf], ignore_index=True)
        index = tmpDf[~tmpDf.isin(self.df)].dropna().index
        self.df = tmpDf
        self.df.sort_values('Date', inplace=True)
        self.dataChanged.emit(index)
Example #9
0
 def test_set_summary_criteria(self, setupKnownData, qtbot, variables):
     self.prefDialog.pagesWidget.setCurrentIndex(1)
     pbPref = self.prefDialog.pagesWidget.widget(1)
     
     aliases = {'Distance':'Distance (km)', 'Speed':'Avg. speed\n(km/h)'}
     funcs = {'sum':sum, 'min':min, 'max':max, 'mean':np.mean}
     
     for name, comboBox in pbPref.summaryComboBoxes.items():
         num = comboBox.currentIndex()
         while num == comboBox.currentIndex():
             num = random.randrange(0, comboBox.count())
         comboBox.setCurrentIndex(num)
         
         with qtbot.waitSignal(self.viewer.viewerUpdated, timeout=variables.longWait):
             pbPref.apply()
             
         measure = comboBox.currentText()
         
         viewerName = aliases.get(name.capitalize(), name.capitalize())
         viewerNameNoNewline = re.sub(r"\n", " ", viewerName)
         col = self.viewer.headerLabels.index(viewerName)
         
         # known data is from April and May 2021
         groups = self.data.df.groupby(self.data.df['Date'] <= pd.Timestamp(year=2021, month=4, day=30))
         qtbot.wait(variables.shortWait)
         
         idx = 0
         for _, df in groups:
             
             data = df[viewerNameNoNewline]
             
             if name == 'time':
                 data = np.array([hourMinSecToFloat(t) for t in data])
                 
             expected = funcs[measure](data)
             
             if name == 'time':
                 expected = floatToHourMinSec(expected)
             
             expected = self.data.fmtFuncs[viewerNameNoNewline](expected)
             
             assert self.viewer.topLevelItems[idx].text(col) == expected
             idx += 1
Example #10
0
 def test_pb_month_criterion(self, setup, qtbot):
     self.prefDialog.pagesWidget.setCurrentIndex(1)
     pbPref = self.prefDialog.pagesWidget.widget(1)
     
     indices = list(range(pbPref.bestMonthCriteria.count()))
     random.shuffle(indices)
     if indices[0] == pbPref.bestMonthCriteria.currentIndex():
         val = indices.pop(0)
         indices.append(val)
         
     keys = {"Distance":"Distance (km)", "Avg. speed":"Avg. speed (km/h)"}
     
     for idx in indices:
         pbPref.bestMonthCriteria.setCurrentIndex(idx)
         
         button = self.prefDialog.buttonBox.button(QDialogButtonBox.Apply)
         with qtbot.waitSignal(button.clicked, timeout=10000):
             qtbot.mouseClick(button, Qt.LeftButton)
             
         criterion = pbPref.bestMonthCriteria.currentText()
         column = keys.get(criterion, criterion)
         
         months = self.data.splitMonths()
         if column in ["Distance (km)", "Calories"]:
             values = [(monthYear, sum(df[column])) for monthYear, df in months]
         elif column == "Time":
             values = [(monthYear, sum([hourMinSecToFloat(parseDuration(t)) for t in df[column]])) for monthYear, df in months]
         elif column == "Avg. speed (km/h)":
             values = [(monthYear, max(df[column])) for monthYear, df in months]
         else:
             values = [(monthYear, np.around(np.mean(df[column]))) for monthYear, df in months]
             
         values.sort(key=lambda item: item[1], reverse=True)
         best = values[0]
         
         assert self.app.pb.bestMonth.monthYear == best[0]
Example #11
0
def test_float_to_hms(value):
    s = floatToHourMinSec(value)
    value2 = hourMinSecToFloat(s)
    assert np.isclose(value, value2,
                      atol=1 / 3600)  # tolerance within one second
Example #12
0
    def test_edit_remove_rows(self, setupKnownData, qtbot, monkeypatch):

        edit = ['2021-04-26', '2021-04-28', '2021-05-04']
        remove = ['2021-04-30', '2021-05-03']

        selectDates = [
            datetime.strptime(d, '%Y-%m-%d').strftime("%d %b %Y") for d in edit
        ]
        selectDates += [
            datetime.strptime(d, '%Y-%m-%d').strftime("%d %b %Y")
            for d in remove
        ]

        for topLevelItem in self.widget.topLevelItems:
            for idx in range(topLevelItem.childCount()):
                item = topLevelItem.child(idx)
                if item.text(0) in selectDates:
                    item.setSelected(True)

        dialogEdit = {
            0: {
                'Calories': 450.2,
                'Date': pd.Timestamp('2021-04-16 00:00:00'),
                'Distance (km)': 30.1,
                'Gear': 6,
                'Time': '00:53:27'
            },
            2: {
                'Calories': 375.1,
                'Date': pd.Timestamp('2021-04-28 00:00:00'),
                'Distance (km)': 42.3,
                'Gear': 6,
                'Time': '01:00:05'
            },
            8: {
                'Calories': 375.1,
                'Date': pd.Timestamp('2021-05-04 00:00:00'),
                'Distance (km)': 25.08,
                'Gear': 6,
                'Time': '00:42:11'
            }
        }

        dialogRemove = [7, 4]
        monkeypatch.setattr(EditItemDialog, "exec_",
                            lambda *args: QDialog.Accepted)
        monkeypatch.setattr(EditItemDialog, "getValues", lambda *args:
                            (dialogEdit, dialogRemove))

        with qtbot.waitSignals([self.parent.data.dataChanged] * 2):
            self.widget._editItems()

        for d in remove:
            assert pd.Timestamp(datetime.strptime(
                d, '%Y-%m-%d')) not in self.parent.data['Date']

        for dct in dialogEdit.values():
            row = self.parent.data.df.loc[self.parent.data.df['Date'] ==
                                          dct['Date']]
            for col in dct.keys():
                assert row[col].values[0] == dct[col]

        # check that speed has updated for row where time and distance were changed
        dct = dialogEdit[2]
        expected = dct['Distance (km)'] / hourMinSecToFloat(dct['Time'])
        row = self.parent.data.df.loc[self.parent.data.df['Date'] ==
                                      dct['Date']]
        speed = row['Avg. speed (km/h)'].values[0]
        assert np.isclose(speed, expected)