Skip to content

Instantly share code, notes, and snippets.

@executed
Last active August 29, 2025 14:52
Show Gist options
  • Save executed/92833f9a908c154fcd59ba5221f6dfbc to your computer and use it in GitHub Desktop.
Save executed/92833f9a908c154fcd59ba5221f6dfbc to your computer and use it in GitHub Desktop.
vikunja_nextcloud_brain_dump
[[toc]]
# Overview
# Handling Forks and Exceptions in Master Event Series
## Fork in Master
- **Description**:
The master event series is updated with new start and end dates. All subsequent series events are identical to the master.
---
## Fork in the Middle
- **Scenario**:
A fork occurs at a point in the middle of the master series.
- **Behavior**:
- The **previous master**:
- Retains the original `RRULE` (recurrence rule).
- Continues to operate identically for all events up to the fork day.
- References the new master as a *sibling*.
- The **new master**:
- Receives the same `RRULE` but without the `UNTIL` value.
- References the previous master as a *sibling*.
- Controls all events occurring after the fork point.
---
## One-Time Exception in Master
- **Scenario**:
An exception occurs on a specific date in the master event series.
- **Behavior**:
- The **master record**:
- Retains the `RRULE` and remains unchanged for the original series.
- An **additional `VEVENT`** is added:
- Shares the same `ID` as the master.
- Does not include the `RRULE`.
- Uses `RECURRENCE-ID` to refer to the original `DTSTART` of the series.
- Following events:
- Remain as part of the original series (`RRULE`).
- Include an additional one-time exception on the specific date.
---
## One-Time Exception in the Middle
- **Scenario**:
An exception occurs on a specific date in the middle of the event series.
- **Behavior**:
- Same logic as "One-Time Exception in Master" applies.
# Problem with recurrence
- [ ] Resolved
When working with Nextcloud and the ICS format, I need to carefully consider the validity of relying solely or exclusively on date properties such as DTSTART, DTEND, or LAST-MODIFIED. While these properties work well for regular, single-instance events, they become problematic when dealing with recurring calendar events.
For regular events, each event has a unique UUID. The date properties (DTSTART, DTEND) are directly associated with the specific event being queried. There is no ambiguity since the event is a standalone instance.
For recurring events, all occurrences in a recurring series share the same UUID. Any date that falls within the recurring series corresponds to the same UUID, which can lead to ambiguity. The individual ICS entries for dates within the series are effectively identical unless a recurrence exception or "fork" occurs. These changes can happen when:
- A specific occurrence is modified (e.g., time or date adjusted).
- A new timeline is created by breaking the recurrence (a fork).
To handle this properly, we should consider additional properties, such as RRULE (recurrence rule). The RRULE property often specifies an UNTIL date, which indicates:
- A user-defined end date for the recurrence.
- A date when a recurrence exception occurred.
- A point where a fork in the series was created.
In most cases, RRULE and its UNTIL property can provide clarity on the boundaries of a recurrence series. However, this needs to be verified and carefully implemented.
Additionally, relying solely on these properties may still lead to mistakes, so it's worth exploring other parameters in the ICS format to avoid errors in identifying events, especially in complex recurrence scenarios.
# Recurrence Edge Cases
- Frequency setup from TM on basic task.
- Frequency setup from Calendar on basic task.
-
- Frequency change from TM in beginning; No forks;
- Frequency change from TM in middle; No forks;
-
- Frequency change from Calendar in beginning; No forks;
- Frequency change from Calendar in middle; No forks;
-
- Fork from TM in beginning.
- Fork from TM in middle.
-
- Fork from CALDAV in beginning.
- Fork from CALDAV in middle.
-
- Frequency change from TM in beginning; Fork;
- Frequency change from TM in middle; Fork;
-
- Frequency change from Calendar in beginning; Fork;
- Frequency change from Calendar in middle; Fork;
-
- Unsupported Frequency change from Calendar; No forks;
TM's frequency should not take precedence unless FREQ change was latest.
Calendar's stop after N iterations should also be considered not supported.
Comment about unsupported FREQ on Calendar side needs to be put on task in TM.
-
- Unsupported Frequency change from Calendar; Fork;
## Date Moves
### Start Date: No Fork
- TM: Start Date moved to prev./next date & time is the same - change start datetime of CALDAV master event.
- CALDAV: Start Date moved to prev./next date & time is the same - change start datetime of TM task.
### End Date: No Fork
- TM: End Date moved to prev./next date & time is the same.
TODO: I mean end date also is kind of start date of it's own series, but there might be caveats like what if end date becomes before start date?
==^^ Need to continue with Date Moves cases
Also go and check out To-Do questions==
# Event Sync Log (History in DB)
### Schema
- id
- taskManagerTaskID
- timeEntryType
- processed
- tmTaskTitle
- calendarEventID
- tmDateTime
- calendarDateTime
- usesDurationLabel (**really needed?**)
- **duration label**
- logDateUpdate
# Duration Labels in TM
Manage duration of a task/event using TM task labels.
Exact labels that will need to be used:
- duration: 5min
- duration: 15min
- duration: 30min
- duration: 1h
- duration: 2h
- duration: 3h
- duration: 5h
Create all needed duration labels on sync service startup in TM only. Need to skip re-creating if at least one label starts with "duration: ".
### Rules
Set end date of task based on TM/CAL start datetime + time of duration label.
For given task in sync logic loop end task should be set before running anything else so it fits well with existing logic.
When **start** datetime is **shifted** on task/event in TM/CAL we need to re-calculate end datetime based on duration label (start datetime + duration).
What if both start and end datetimes were shifted simultaneously from TM and there's duration label?
Which one gets priority: duration label or manually changed end time.
~~We introduce new flag into our event sync log model - **usesDurationLabel** (default: false).~~
In TM user creates task without start/end datetimes - no event sync log model yet is saved into DB because event is ignored.
TM: user adds start date + duration label - end date is calculated using duration label ~~and usesDurationLabel becomes true on first log saving.~~
TM: start date is updated and dur. label present and end time similar to duration label - sync service re-calculates end date based on duration label.
TM: end date is updated from TM - usesDurationLabel becomes false if end date's delta minutes with start date is ++moderately different++ from duration label, or there's no duration label. If usesDurationLabel is still TRUE after this rule, then we overwrite end date with duration from the duration label (standardization).
TM: no end date and duration label is present - we need to enable the usesDurationLabel flag back which will continue generating end date based on duration label.
TM: start date is there, but no end date and no dur. label - just skip generating end date, we'll let logic downwards either ignore end date, or delete it's event from CAL.
**Why do we need usesDurationLabel at all?**
It seems like presence of duration label and checking the moderate difference between start date and end date is in itself a good flag.
And we start strictly applying duration label logic only when end date is removed or it's close to the defacto delta with start date.
Should we remove duration label from task/event when defacto delta with start date is moderate? I think no, but need to think about it.
==REVIEW this section one more time==
What if both start and end datetimes were shifted simultaneously from CAL?
Which one gets priority: duration label or manually changed end time?
When end datetime is changed manually in TM/CAL and event has duration label then we need to update the duration label to the closest label increasingly.
Example: if Manual duration is 16 min, then duration label should be 30 min, not 15 min.
When new duration label is added, delete all other duration labels.
When duration label is changed and start date exists we need to calculate end datetime based on new duration label (start datetime + duration).
When duration label is added, but no start datetime - ignore.
When duration label is removed, leave dates as they are.
Duration label needs to be also pushed to CAL as CATEGORIES property (ICS) which is comma-separated list of labels.
# Shift uncompleted overlooked to next day
==Re-read and think again about this section.
Then go back to duration label logic.==
**Should happen ++before++ duration label logic???**
## Basic Task
You find yourself forgetting tasks scheduled for previous days because they don't carry over to the **next** day automatically in tools like iOS Calendar or Google Tasks. To address this, you propose a system where uncompleted tasks from the previous day are automatically shifted to the next day until marked as completed.
Key points of your idea:
**Recreating Tasks:**
Tasks should be shifted 24 hours if not completed by their designated time (start date of not-completed task is in the past).
**Timing the Shift:**
A static end-of-day time, such as 6 a.m., can act as the trigger point for this process.
**Adding Labels:**
Tasks shifted to the current day should be labeled, e.g., "Shifted," to indicate they were not completed as originally scheduled.
This helps prioritize these tasks since they were meant to be completed earlier.
User can have a filter to display all tasks like this based on label.
**High-Priority Tasks:**
See [this](#intra-day-reminders-about-urgent-uncompleted-tasks).
**Benefits of the Approach:**
Ensures no task is overlooked just because its start time has passed.
Helps you identify and focus on overdue tasks by distinguishing them with a label.
## Recurrence Series
### Repeat Mode "From Completion Date"
**Examples**: Trash clearance, Shower, Nails cutting, Haircut, etc.
Basic task's **Shift uncompleted to next day** logic when we "**shift start datetime by same time next day**" works fine for recurrence series if TM repeat mode is "from completion date".
Reason is the schedule is not strict and what is important is that task gets done with certain frequency.
**Caveat**: when task repeat frequency is daily (e.g. Shower) skip shifting start datetime or assigning labels.
### Repeat Mode Default/Monthly
**Examples**: Cleaning house on Saturdays, Going to office on Wednesdays, sending tax report every 1th day of month, etc.
Basic task logic does not work here.
Instead of shifting recurrence series task to **nearest same time**, we might need to create a separate basic **nearest same time**.
New task should have same title/description and labels as original recurring task, but it's repeat mode and related parameters must be empty.
New task must have "Shifted" label to it.
New task should reference original recurring task in "Copied From" relation.
Before creating such shifted task for recurring task with start datetime in past, job running at 6am needs to double-check if there are tasks with label "Shifted" && "Copied From" relation to "overdue" recurring task, but no label "Finalized".
If there aren't - create such basic task, means it's first day recurring series is overdue.
If there are completed - complete recurring task and put label "Finalized" on completed "Shifted" tasks associated with it.
If there are un-completed - skip creating task again and let the following similar logic related to basic task overdue handling shift existing basic task to **nearest same time** (means that task wasn't completed when thought, also wasn't next day, also wasn't following day).
## Cleanup Shifted Label
Before running this logic first we need to find all tasks matching:
- not completed
- start datetime in future
- has label "Shifted"
What are these tasks?
- Basic tasks that were once overlooked (got "Shifted" label), but then were manually re-scheduled in the future (not overlooked anymore).
- Recurring task with Repeat Mode "From Completion Date" that once was overdue, but then were manually re-scheduled in the future (not overlooked anymore).
- Recurring task with Repeat Mode "From Completion Date" that once was overdue, but then was completed which itself shifted dates to N days, but haven't removed "Shifted" label.
**Action**: remove "Shifted" label.
# Intra-day reminders about urgent uncompleted tasks
If task has label 'Urgent', sync service should automatically assign extra reminders before and after event to it.
# Deployment
- [x] Done
Need to add docker and configuration stuff here.
Latest issue: get 400 on login - seems like cannot connect to DB.
Double check this:
https://community.vikunja.io/t/setup-help-connecting-to-a-mysql-database/209/8
Also try using the mariadb image suggested on off pages, but without utf8 command as it seems to break things and is not really needed until we hop Mary on it.
# Drag task from TM to CAL
- [x] Done
GIF: [here](https://ibb.co/Z69kPSs)
Had to make slight code adjustments to both apps.
### Original Plan
We've tweaked custom Vikunja Docker image to write special dataTransfer object on task drag and drop into nextcloud.
DataTransfer scheme:
```js
const customDataTransfer = {
bucketID: bucket.id,
taskID: draggedElement.dataset.taskId,
taskTitle: draggedElement.dataset.taskTitle,
}
```
Next step is to let Nextcloud parse it on drop event, identify what time slot this was dropped into and create a event based on it.
Nextcloud should copy the TM title and also write some etag that would reference taskID.
Here's code that lets Nextcloud read dragged task from Vikunja:
https://stackoverflow.com/questions/11214053/drag-and-drop-images-from-another-website-to-mine
This way we'll theoretically be able to now tweak nextcloud calendar code to read it and write this data into event's ETAGs or smth like it (alternative - end of description).
Then we'll have separate scheduled job in our sync service that looks for events like this (better if it could filter by ETAGs and by future events) so that we can write event sync log that specific TM task is mapped to specific Nextcloud event.
# To-Do
- [ ] Need to think about duration labels when changes come from CAL.
- [ ] What if duration label is removed from TM/CAL?
- [ ] Need to think about duration labels in recurring events perspective
- [ ] Default Reminders - before start, before end, before due date.
time entry type in TM is still 17 o'clock - it should delete TM entry instead;
- [ ] if I delete NC event e.g. at 17 o'clock but job sees that same time entry type in TM is other than 17 then we delete TM entry;
- [ ] how do we update TM datetimes when they are shifted in NC?
- [ ] if i delete TM task then all 3 events in NC should be deleted
- [ ] When updating CALDAV event it should not be re-created, but updated instead. Reason: events have NC specific data attached to them, like Talk conversations, attendees, etc.
- [ ] When fork happens in CALDAV we should not only create a new task and name it same, but in different way, but also put label on original task "fork" and discontinue it (complete) after it's start & due & end dates are in past.
Fork TM task should be linked to original task as sibling in TM.
Comment needs to be put into both original task and forked task in TM signaling the fork.
- [ ] Notifications logic on Windows (alternative: Thunderbird always running).
- [ ] Roadmap: stop recurring task after N iterations based on user TM comment (e.g. "stop 3").
- [ ] Should start OR due dates be considered required for recurring events sync?
- [ ] Should we treat due date any different when there's no start or end dates?
- [ ] What if user completes the task from TM which changes due date?
- [ ] What if user completes the task from TM which changes due date and there's only due date?
- [ ] Think about all possible not normal event series time shifts. Possibilities:
(start due end), (end start due), (end due start), (due start end), (due end start).
- [ ] Should assign due date to end date if end date is not present? (Just for sake of displaying that task is due in TM).
- [ ] Should assign due date to start date if neither end date nor due dates are present? (Just for sake of displaying that task is due in TM).
- [ ] Double check what happens if end date is removed from CAL - does it get removed from TM too? Think if it makes sense. Additionally I think TM end date needs to be preserved if start date and duration label are still present. Otherwise remove end date completely from either TM or CAL.
- [x] ~~Deploy Vikunja on Windows in Docker~~
- [x] Deploy Vikunja on Linux in Docker
- [ ] Add Logback
- [ ] connection exception handling
- [x] Drag tasks from Vikunja to Nextcloud. (described [here](#drag-task-from-tm-to-cal))
- [ ] Vikunja should ask if you want to refresh task view when there was background change from API to avoid background change overwrites from UI. Founder said it's on the roadmap ([here](https://community.vikunja.io/t/automatically-set-task-end-date-based-on-start-date-and-a-label/2512))
- [x] see how we track that events like start/end/due were deleted on Nextcloud - when creating nextcloud events we already double-check if there is event sync log that states that NC event of given time entry was deleted - there's no such check - we know that calendar event was deleted by seeing that there was a history event log, but there's no calendar event now;
- [x] if I delete NC event e.g. at 17 o'clock then sync job should not recreate it if same
- [ ] when i add label called tomorrow, or 3 days, or next week, or critical - i want the sync service to treat it as due date and choose reminders and priority accordingly
- [ ] Copy link to current page.
- [ ] TM main task view should have draggable item so that user can drag to CAL not only from Kanban view, but also from detailed task & table views.
- [ ] when TM task is dragged to Nextcloud calendar again - sync service should delete all previous CAL events with certain X-EXT-ID and add a new one just like it should have added it for the first time. Vikunja
- [ ] create CAL events based on reminders
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment