Planned activities: Keeping track of original date (#4822)

* Planned activities: Keeping track of original date

* Adding original date to planned activities
* Resetting the original date when copying planned activities
* Showing original planned date in calendar tooltips
* Ensuring metrics get recalculated when moving, shifting, copying
  activities
* Ensuring all tags are written when moving or shifting activities
This commit is contained in:
Joachim Kohlhammer
2026-02-06 15:46:02 +01:00
committed by GitHub
parent 31d8708376
commit a0fe8afa32
6 changed files with 109 additions and 4 deletions

View File

@@ -1006,12 +1006,18 @@ CalendarWindow::getActivities
activity.isRelocatable = rideItem->planned;
activity.hasTrainMode = rideItem->planned && sport == "Bike" && ! buildWorkoutFilter(rideItem).isEmpty();
activity.dirty = rideItem->isDirty();
if (rideItem->planned) {
activity.originalPlanLabel = buildOriginalLabel(rideItem);
}
RideItem *linkedRide = context->athlete->rideCache->getLinkedActivity(rideItem);
if (linkedRide != nullptr) {
activity.linkedReference = linkedRide->fileName;
activity.linkedPrimary = getPrimary(linkedRide);
activity.linkedStartDT = linkedRide->dateTime;
if (linkedRide->planned) {
activity.originalPlanLabel = buildOriginalLabel(linkedRide);
}
}
activities[rideItem->dateTime.date()] << activity;
@@ -1328,6 +1334,51 @@ CalendarWindow::findFreeSlot
}
QString
CalendarWindow::buildOriginalLabel
(RideItem const * const item) const
{
QDate originalPlan = QDate::fromString(item->getText("Original Date", ""), "yyyy/MM/dd");
if (! originalPlan.isValid() || originalPlan == item->dateTime.date()) {
return "";
}
QLocale locale;
QString unitLabel;
int days = originalPlan.daysTo(item->dateTime.date());
QChar sign = days > 0 ? '+' : '-';
ShowDaysAsUnit unit = showDaysAs(days);
int c = 0;
if (unit == ShowDaysAsUnit::Days) {
c = std::abs(days);
if (c == 1) {
unitLabel = tr("day");
} else {
unitLabel = tr("days");
}
} else if (unit == ShowDaysAsUnit::Weeks) {
c = daysToWeeks(days);
if (c == 1) {
unitLabel = tr("week");
} else {
unitLabel = tr("weeks");
}
} else if (unit == ShowDaysAsUnit::Months) {
c = daysToMonths(days);
if (c == 1) {
unitLabel = tr("month");
} else {
unitLabel = tr("months");
}
}
return QString("%1 (%2%3 %4)")
.arg(locale.toString(originalPlan, QLocale::NarrowFormat))
.arg(sign)
.arg(c)
.arg(unitLabel);
}
void
CalendarWindow::updateActivities
()

View File

@@ -161,6 +161,7 @@ class CalendarWindow : public GcChartWindow
QString getPrimary(RideItem const * const rideItem) const;
QTime findFreeSlot(RideItem *sourceItem, QDate newDate, QTime time);
QTime findFreeSlot(QList<std::pair<QTime, int>> busySlots, QTime targetTime, int requiredDurationSeconds) const;
QString buildOriginalLabel(RideItem const * const item) const;
private slots:
void updateActivities();

View File

@@ -439,6 +439,7 @@ RideCache::removeRide(const QString& filenameToDelete) {
context->notifyRideSelected(context->ride);
}
refresh();
// model estimates (lazy refresh)
estimator->refresh();
@@ -1187,21 +1188,38 @@ RideCache::moveActivity
return result;
}
QDate originalDate = QDate::fromString(ride->getTag("Original Date", ""), "yyyy/MM/dd");
if (! originalDate.isValid()) {
ride->setTag("Original Date", oldDateTime.date().toString("yyyy/MM/dd"));
}
item->setStartTime(newDateTime);
ride->setTag("Year", newDateTime.toString("yyyy"));
ride->setTag("Month", newDateTime.toString("MMMM"));
ride->setTag("Weekday", newDateTime.toString("ddd"));
ride->setTag("Filename", newFileName);
item->metadata_.insert("Calendar Text", GlobalContext::context()->rideMetadata->calendarText(item));
item->close();
QString renameError;
if (! renameRideFiles(oldFileName, newFileName, item->planned, renameError)) {
item->dateTime = oldDateTime;
item->fileName = oldFileName;
result.error = tr("Failed to rename files: %1").arg(renameError);
item->close();
return result;
}
QString newPath = (item->planned ? plannedDirectory : directory).canonicalPath() + "/" + newFileName;
QFile outFile(newPath);
if (! RideFileFactory::instance().writeRideFile(context, ride, outFile, QFileInfo(newFileName).suffix())) {
renameRideFiles(newFileName, oldFileName, item->planned, renameError);
item->dateTime = oldDateTime;
item->fileName = oldFileName;
result.error = tr("Failed to save activity file after rename");
item->close();
return result;
}
item->close();
int index = rides_.indexOf(item);
if (index >= 0) {
model_->startRemove(index);
@@ -1236,6 +1254,7 @@ RideCache::moveActivity
if (context->ride == item) {
context->notifyRideSelected(item);
}
refresh();
estimator->refresh();
result.success = true;
@@ -1302,7 +1321,8 @@ RideCache::copyPlannedActivity
std::sort(rides_.begin(), rides_.end(), rideCacheLessThan);
model_->endReset();
newItem->refresh();
refresh();
estimator->refresh();
result.success = true;
result.affectedCount = 1;
@@ -1541,18 +1561,33 @@ RideCache::shiftPlannedActivities
continue;
}
QDate originalDate = QDate::fromString(ride->getTag("Original Date", ""), "yyyy/MM/dd");
if (! originalDate.isValid()) {
ride->setTag("Original Date", item->dateTime.date().toString("yyyy/MM/dd"));
}
item->setStartTime(newDateTime);
ride->setTag("Year", newDateTime.toString("yyyy"));
ride->setTag("Month", newDateTime.toString("MMMM"));
ride->setTag("Weekday", newDateTime.toString("ddd"));
ride->setTag("Filename", newFileName);
item->metadata_.insert("Calendar Text", GlobalContext::context()->rideMetadata->calendarText(item));
item->close();
QString renameError;
if (! renameRideFiles(oldFileName, newFileName, true, renameError)) {
failedFiles << oldFileName;
item->close();
continue;
}
QString newPath = plannedDirectory.canonicalPath() + "/" + newFileName;
QFile outFile(newPath);
if (! RideFileFactory::instance().writeRideFile(context, ride, outFile, QFileInfo(newFileName).suffix())) {
renameRideFiles(newFileName, oldFileName, true, renameError);
failedFiles << oldFileName;
item->close();
continue;
}
item->close();
item->setFileName(plannedDirectory.canonicalPath(), newFileName);
updateFromWorkout(item, true);
item->isstale = true;
@@ -1840,6 +1875,7 @@ RideCache::copyPlannedRideFile
newRide->setTag("Year", newDateTime.toString("yyyy"));
newRide->setTag("Month", newDateTime.toString("MMMM"));
newRide->setTag("Weekday", newDateTime.toString("ddd"));
newRide->setTag("Original Date", newDateTime.date().toString("yyyy/MM/dd"));
if (! newRide->getTag("Linked Filename", "").isEmpty()) {
newRide->removeTag("Linked Filename");

View File

@@ -69,6 +69,7 @@ struct CalendarEntry {
bool dirty = false;
QDate spanStart = QDate();
QDate spanEnd = QDate();
QString originalPlanLabel = QString();
QString linkedReference = QString();
QString linkedPrimary = QString();

View File

@@ -1773,10 +1773,18 @@ toolTipDayEntry(const QPoint &pos, QAbstractItemView *view, const CalendarDay &d
}
tooltip += QString("<tr><td><b>%1:</b></td><td>%2</td></tr>").arg(QObject::tr("When")).arg(time);
}
if (calEntry.type == ENTRY_TYPE_PLANNED_ACTIVITY && ! calEntry.originalPlanLabel.isEmpty()) {
tooltip += QString("<tr><td><b>%1:</b></td><td>%2</td></tr>")
.arg(QObject::tr("Originally"))
.arg(calEntry.originalPlanLabel);
}
if (! calEntry.linkedReference.isEmpty()) {
QString countertype;
countertype = QObject::tr("Linked");
tooltip += QString("<tr><td style='padding-top: %1px'><b>%2:</b></td><td style='padding-top: %1px'>%3</td></tr>").arg(6 * dpiYFactor).arg(countertype).arg(calEntry.linkedPrimary);
tooltip += QString("<tr><td style='padding-top: %1px'><b>%2:</b></td><td style='padding-top: %1px'>%3</td></tr>")
.arg(6 * dpiYFactor)
.arg(countertype)
.arg(calEntry.linkedPrimary);
if (calEntry.linkedStartDT.isValid()) {
QString linkedWhen;
QLocale locale;
@@ -1786,6 +1794,11 @@ toolTipDayEntry(const QPoint &pos, QAbstractItemView *view, const CalendarDay &d
linkedWhen = locale.toString(calEntry.linkedStartDT, QLocale::NarrowFormat);
}
tooltip += QString("<tr><td><b>%1:</b></td><td>%2</td></tr>").arg(QObject::tr("On")).arg(linkedWhen);
if (calEntry.type == ENTRY_TYPE_ACTUAL_ACTIVITY && ! calEntry.originalPlanLabel.isEmpty()) {
tooltip += QString("<tr><td><b>%1:</b></td><td>%2</td></tr>")
.arg(QObject::tr("Originally"))
.arg(calEntry.originalPlanLabel);
}
}
}
tooltip += "</table>";

View File

@@ -111,6 +111,9 @@ ManualActivityWizard::done
rideFile.setRecIntSecs(0.00);
rideFile.setDeviceType("Manual");
rideFile.setFileFormat("GoldenCheetah Json");
if (plan) {
rideFile.setTag("Original Date", field("activityDate").toDate().toString("yyyy/MM/dd"));
}
field2TagString(rideFile, "sport", "Sport");
field2TagString(rideFile, "subSport", "SubSport");