# API: Schedules and conflict resolution This document outlines handling conflict resolution using the API. Find an overview of the API spec at [api.aura.radio](https://api.aura.radio). To learn about conflict resolution check out [Timeslot Collision Detection](../../user/programme-planning.md#timeslot-collision-detection) in the User Guide. ## Overview Creating timeslots is only possible by creating/updating a schedule. When creating/updating a schedule by giving the schedule data: 1. Projected timeslots are matched against existing timeslots 2. API returns an array of objects containing 2.1. the schedule's data 2.2. projected timeslots (array of objects), including an array of found collisions (objects) and possible solutions (array) 3. To resolve, POST/PUT the `"schedule"` object and the `"solutions"` objects to `/api/v1/shows/{show_pk}/schedules/{schedule_pk}/` ## Create/update schedule * To create: POST `/api/v1/shows/{show_pk}/schedules/` * To update: PUT `/api/v1/shows/{show_pk}/schedules/{schedule_pk}/` To start the conflict resolution POST/PUT the schedule object with key `"schedule"` containing: | Variable | Type | Meaning | |---------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | byWeekday[^1] | int | Weekday number: Mon = 0, Sun = 6 | | rrule[^1], [^2] | int | Recurrence rule | | firstDate[^1] | date | First date of schedule (e.g. "2017-01-01") | | startTime[^1] | time | Start time of schedule (e.g. "16:00:00") | | endTime[^1] | time | End time of schedule (e.g. "17:00:00") | | lastDate[^3] | date | Last date of schedule (e.g. "2017-12-31") | | isRepetition | boolean | Whether the schedule is a repetition (default: false) | | defaultPlaylistId | int | A tank ID in case the timeslot's playlistId is empty: What is aired if a single timeslot has no content? (default: null) | | show[^1] | int | Show the schedule belongs to | | addDaysNo | int | Add a number of days to the generated dates. This can be useful for repetitions, like "On the following day" (default: null) | | addBusinessDaysOnly | boolean | Whether to add *addDaysNo* but skipping the weekends. E.g. if weekday is Friday, the date returned will be the next Monday (default: false) | | dryrun | boolean | Whether to simulate the database changes. If true, no database changes will occur, instead a summary is returned of what _would_ happen if dryrun was false. (default: false) | [^1]: These parameters are required. [^2]: The full set of recurrence rules supported by default is loaded from the `rrule.json` fixture. [^3]: If `lastDate` is not provided, timeslots will be generated until the end of the year if `AUTO_SET_LAST_DATE_TO_END_OF_YEAR` is `True`, otherwise timeslots will be generated until `AUTO_SET_LAST_DATE_TO_DAYS_IN_FUTURE` after the `startDate` **and** `endDate` will remain unset. ## Return After sending the schedule's data, the response will be in the form of: ```json { /* Projected timeslots to create. Array may contain multiple objects */ "projected": [ { "hash": "201801161430002018011616000041", "start": "2018-01-16 14:30:00", "end": "2018-01-16 16:00:00", /* Collisions found to the projected timeslot. Array may contain multiple objects */ "collisions": [ { "id": 607, "start": "2018-01-16 14:00:00", "end": "2018-01-16 15:00:00", "playlistId": null, "show": 2, "showName": "FROzine", "isRepetition": false, "schedule": 42, "memo": "", "noteId": 1 /* A note assigned to the timeslot. May not exist */ } ], "error": "An error message if something went wrong. If not existing or empty, there's no problem", /* Possible solutions to solve the conflict */ "solutionChoices": [ "ours-start", "theirs", "ours", "theirs-start" ] }, "solutions": { /* Manually chosen solutions by the user (if there's a key it has to have a value): Key is the hash of the projected timeslot while value must be one of 'solutionChoices' */ "201801161430002018011616000041": "" }, "notes": { /* To reassign an existing note to a projected timeslot, give its hash as key and the note id as value (may not exist) */ "201801161430002018011616000041": 1 }, "playlists": { /* To reassign playlists to a projected timeslot, give its hash as key and the playlist id as value (may not exist) */ "201801161430002018011616000041": 1 }, /* The schedule's data is mandatory for each POST/PUT */ "schedule": { "rrule": 4, "byWeekday": 1, "show": 3, "firstDate": "2018-01-16", "startTime": "14:30:00", "endTime": "16:00:00", "lastDate": "2018-06-28", "isRepetition": false, "defaultPlaylistId": null, "automationId": null, "dryrun": false } } ``` ## Solutions The `"solutionChoices"` array contains possible solutions to the conflict. To solve conflicts, POST the `"schedule"` and `"solutions"` objects to `/api/v1/shows/{show_p}/schedules/` or PUT to `/api/v1/shows/{show_pk}/schedules/{schedule_pk}/` with `"solutions"` containing values of `solutionChoices`. Any other value will produce an error. As long as there's an error, the whole data structure is returned and no database changes will occur. If resolution was successful, database changes take effect and the schedule is returned. A Schedule is only created/updated if at least one timeslot was created during the resolution process. Maximum possible output: ```json "solutions": [ "theirs", "ours", "theirs-start", "ours-start", "theirs-end", "ours-end", "theirs-both", "ours-both" ] ``` ### `"theirs"` (always possible) * Discard projected timeslot * Keep existing timeslot(s) ### `"ours"` (always possible) * Create projected timeslot * Delete existing timeslot(s) ### `"theirs-start"` * Keep existing timeslot * Create projected timeslot with start time of existing end ### `"ours-start"` * Create projected timeslot * Change end of existing timeslot to projected start time ### `"theirs-end"` * Keep existing timeslot * Create projected timeslot with end of existing start time ### `"ours-end"` * Create projected timeslot * Change start of existing timeslot to projected end time ### `"theirs-both"` * Keep existing timeslot * Create two projected timeslots with end of existing start and start of existing end ### `"ours-both"` * Create projected timeslot * Split existing into two: * Set existing end time to projected start * Create another timeslot with start = projected end and end = existing end #### Multiple collisions If there's more than one collision for a projected timeslot, only `"theirs"` and `"ours"` are currently supported as solutions. ## Errors Possible error messages are: ### Fatal errors that require the schedule's data to be corrected and the resolution to restart * `"Until date mustn't be before start"`: Set correct start and until dates. * `"Start and until dates mustn't be the same"` Set correct start and until dates. (Exception: Single timeslots with recurrence rule 'once' may have the same dates) * `"Numbers of conflicts and solutions don't match"`: There probably was a change in the schedule by another person in the meantime. * `"This change on the timeslot is not allowed."` When adding: There was a change in the schedule's data during conflict resolution. When updating: Fields `start`, `end`, `byWeekday` or `rrule` have changed, which is not allowed. ### Conflict-related errors which can be resolved for each conflict: * `"No solution given"`: The solutions value was empty or does not exist. Provide a value of `solutionChoices` * `"Given solution is not accepted for this conflict."`: The solution has a value which is not part of `solutionChoices`. Provide a value of `solutionChoices` (at least `"ours"` or `"theirs"`)