From 8291088085f92317c605af7621c315a78f3b3c9f Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 20 Sep 2024 17:31:03 -0400 Subject: [PATCH 01/19] rough rosters and employee assignment spec --- docs/spec/examples.md | 17 ++++++++++ docs/spec/index.md | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/docs/spec/examples.md b/docs/spec/examples.md index d20a6f2..4e6bf40 100644 --- a/docs/spec/examples.md +++ b/docs/spec/examples.md @@ -168,3 +168,20 @@ weekday,10000,20,BLOCK-A,deadhead ,,garage,08:50:00,stop-1,09:00:00 weekday,10000,30,BLOCK-A,run-as-directed,,stop-1,09:00:00,stop-1,12:00:00 weekday,10000,30,BLOCK-A,deadhead ,,stop-1,12:00:00,garage,12:10:00 ``` + +## Crew/Roster + +TODO I haven't written examples yet, but here are ones I think would be good: + +- UK scheduling (employees rotate per week) +- North American scheduling (pick one roster for the whole rating. I don't think there'll be a big difference between roster-style and cafeteria-style in the data? ) +- A holiday +- Scheduled track work one day, times/service_id are slightly changed, but substantially the same. + - `roster_dates.txt`: `roster_id,date,old_service_id,2;...new_service_id,1` +- Vacation that's part of the roster. + - `roster_dates.txt`: remove old one. assign spare_roster_id (which has no regular assignment) to it on that date. Maybe `spare_roster_id` still needs to be in `rosters.txt` for comprehensive listing reasons, for start/end date, and for (doesn't exist yet) metadata like label +- Vacation that's not part of the roster, and done by assigning a different employee to that run that date. + - `employee_dates`, remove the employee from the roster, add the spare employee. +- Producer that doesn't use rosters, just uses `employee_dates` for everything. + +(There's enough of them that they should be on a separate page.) diff --git a/docs/spec/index.md b/docs/spec/index.md index b9fef7f..fae156b 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -22,6 +22,8 @@ There are two types of files used in the TODS standard: | routes_supplement.txt | Supplement | Supplements and modifies GTFS [routes.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#routestxt) with internal route identifiers and other non-public route identification. | | run_events.txt | TODS-Specific | Lists all trips and other scheduled activities to be performed by a member of personnel during a run. | +TODO add new roster files to this list + _The use of the Supplement standard to modify other GTFS files is not yet formally adopted into the specification and remains subject to change. Other files may be formally adopted in the future._ ## Supplement Files @@ -139,3 +141,73 @@ Because some events may overlap in time, it may not be possible to choose a sing - Events may have gaps between the end time of one event and the start time of the next. e.g. if an operator's layovers aren't represented by an event. - `start_time` may equal `end_time` for an event that's a single point in time (such as a report time) without any duration. - Recommended sort order: `service_id`, `run_id`, `event_sequence`. + +### `rosters.txt` + +TODO text description of how all 4 files fit together. + +Primary Key: `roster_id` + +| **Field Name** | **Type** | **Required** | **Description** | +| --- | --- | --- | --- | +| `roster_id` | Unique ID | Required | Unique within dataset | +| `start_date` | Date | Required | | +| `end_date` | Date | Required | | +| `monday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. Runs are identified by the pair `(service_id, run_id)`. Required if and only if `monday_run_id` is present. | +| `monday_run_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. If blank, this roster does not work on Mondays. Required if and only if `monday_service_id` is present. | +| `tuesday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. Runs are identified by the pair `(service_id, run_id)`. Required if and only if `monday_run_id` is present. | +| `tuesday_service_id` | | | | +| `tuesday_run_id` | | | | +| `wednesday_service_id` | | | | +| `wednesday_run_id` | | | | +| `thursday_service_id` | | | | +| `thursday_run_id` | | | | +| `friday_service_id` | | | | +| `friday_run_id` | | | | +| `saturday_service_id` | | | | +| `saturday_run_id` | | | | +| `sunday_service_id` | | | | +| `sunday_run_id` | | | | + +### `roster_dates.txt` + +Primary Key: `(roster_id, date, exception_type)` + +| **Field Name** | **Type** | **Required** | **Description** | +| --- | --- | --- | --- | +| `roster_id` | ID referencing `rosters.txt` | Required | (note: should `roster_id` have to be in `rosters.txt` (potentially on 0 days per week), or can you define new rosters in this file, like `calendar_dates.txt` does with service IDs?) | +| `date` | Date | Required | Date when exception occurs. | +| `exception_type` | Enum | Required | `1` - The run is added to this roster for the specified date.
`2` - The roster will not work its regular run on this date. | +| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Required if `run_id` is present. | +| `run_id` | ID referencing `run_events.txt` | Conditionally Required | The run that's either added or removed from this roster. Required if `exception_type` is `1`. Optional if `exception_type` is `2`. If `exception_type` is `2` and `run_id` is not blank, then it must match the Run ID that the roster was scheduled to do on this date according to `rosters.txt` | + +### `employee_rosters.txt` + +Describes which employees are scheduled to which rosters on which dates. + +Primary Key: `(roster_id,start_date)` + +| **Field Name** | **Type** | **Required** | **Description** | +| --- | --- | --- | --- | +| `employee_id` | ID | Required | | +| `roster_id` | ID referencing `rosters.txt` | Required | | +| `start_date` | Date | Required | | +| `end_date` | Date | Required | | + +Each roster can only be assigned to one employee on each date. Employees may be scheduled to more than one roster on the same date. + +### `employee_run_dates.txt` + +Describes which employees are scheduled to which runs on which dates. If `employee_rosters.txt` is used, then describes exceptions to that schedule. + +Primary Key: `*` + +| **Field Name** | **Type** | **Required** | **Description** | +| --- | --- | --- | --- | +| `employee_id` | ID | Required | | +| `date` | Date | Required | | +| `service_id` | ID referencing `run_events.txt` | Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. | +| `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_rosters.txt`, `rosters.txt` and `roster_dates.txt`. | +| `exception_type` | Enum | Required | `1` - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | + +Each run can only be assigned to one employee on each date. Employees may be scheduled to more than one run on the same date. From f393e8427c316bd61a0d0ac2d6ad03cb266d0484 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 20 Sep 2024 18:20:12 -0400 Subject: [PATCH 02/19] employee_run_dates.txt:exception_type can be omitted --- docs/spec/index.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index fae156b..dee4c6c 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -177,7 +177,7 @@ Primary Key: `(roster_id, date, exception_type)` | --- | --- | --- | --- | | `roster_id` | ID referencing `rosters.txt` | Required | (note: should `roster_id` have to be in `rosters.txt` (potentially on 0 days per week), or can you define new rosters in this file, like `calendar_dates.txt` does with service IDs?) | | `date` | Date | Required | Date when exception occurs. | -| `exception_type` | Enum | Required | `1` - The run is added to this roster for the specified date.
`2` - The roster will not work its regular run on this date. | +| `exception_type` | Enum | Required | `1` - The run is added to this roster for the specified date.
`2` - The roster will not work its regular run on this date. | | `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Required if `run_id` is present. | | `run_id` | ID referencing `run_events.txt` | Conditionally Required | The run that's either added or removed from this roster. Required if `exception_type` is `1`. Optional if `exception_type` is `2`. If `exception_type` is `2` and `run_id` is not blank, then it must match the Run ID that the roster was scheduled to do on this date according to `rosters.txt` | @@ -208,6 +208,8 @@ Primary Key: `*` | `date` | Date | Required | | | `service_id` | ID referencing `run_events.txt` | Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. | | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_rosters.txt`, `rosters.txt` and `roster_dates.txt`. | -| `exception_type` | Enum | Required | `1` - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | +| `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | + +If a feed doesn't represent rosters, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. Each run can only be assigned to one employee on each date. Employees may be scheduled to more than one run on the same date. From d3d1de0755fabaac0dde0af01a63deb16d71f238 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Tue, 25 Feb 2025 11:22:58 -0500 Subject: [PATCH 03/19] more edits to roster spec. rename rosters to roster_positions. --- docs/spec/index.md | 58 ++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index dee4c6c..88c551d 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -12,6 +12,8 @@ There are two types of files used in the TODS standard: - **Supplement files**, used to add, modify, and delete information from public GTFS files to model the operational service for internal purposes (with a `_supplement` filename suffix). - **TODS-Specific files**, used to model operational elements not currently defined in the GTFS standard. +All files are optional. + ### Files | **File Name** | **Type** | **Description** | @@ -21,8 +23,10 @@ There are two types of files used in the TODS standard: | stop_times_supplement.txt | Supplement | Supplements and modifies GTFS [stop_times.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#stop_timestxt) with non-public times at which trips stop at locations, `stop_times` entries for non-public trips, and related information. | | routes_supplement.txt | Supplement | Supplements and modifies GTFS [routes.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#routestxt) with internal route identifiers and other non-public route identification. | | run_events.txt | TODS-Specific | Lists all trips and other scheduled activities to be performed by a member of personnel during a run. | - -TODO add new roster files to this list +| roster.txt | TODS-Specific | Lists the runs that a roster position is assigned to work on a typical week. | +| roster_dates.txt | TODS-Specific | Lists the runs that a roster position is assigned to work on specific dates. | +| employee_roster.txt | TODS-Specific | Lists which employee is assigned to which roster position. | +| employee_run_dates.txt | TODS-Specific | Exceptions to roster assignments. Assigns employees directly to runs on specific dates. | _The use of the Supplement standard to modify other GTFS files is not yet formally adopted into the specification and remains subject to change. Other files may be formally adopted in the future._ @@ -142,20 +146,24 @@ Because some events may overlap in time, it may not be possible to choose a sing - `start_time` may equal `end_time` for an event that's a single point in time (such as a report time) without any duration. - Recommended sort order: `service_id`, `run_id`, `event_sequence`. -### `rosters.txt` +### `roster.txt` + +This file defines roster positions, groupings of work across multiple runs on multiple dates that an employee can be assigned to all at once. -TODO text description of how all 4 files fit together. +Exceptions to these dates may be listed in [`roster_dates.txt`](#roster_datestxt). -Primary Key: `roster_id` +Employees are assigned to these roster positions in [`employee_roster.txt](#employee_rostertxt). + +Primary Key: `roster_position_id` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | -| `roster_id` | Unique ID | Required | Unique within dataset | -| `start_date` | Date | Required | | -| `end_date` | Date | Required | | +| `roster_position_id` | Unique ID | Required | Unique within dataset | +| `start_date` | Date | Required | First service day that the roster position works. | +| `end_date` | Date | Required | Last service day tha the roster position works. This day is included in the interval. | | `monday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. Runs are identified by the pair `(service_id, run_id)`. Required if and only if `monday_run_id` is present. | | `monday_run_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. If blank, this roster does not work on Mondays. Required if and only if `monday_service_id` is present. | -| `tuesday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. Runs are identified by the pair `(service_id, run_id)`. Required if and only if `monday_run_id` is present. | +| `tuesday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Tuesdays. Runs are identified by the pair `(service_id, run_id)`. Required if and only if `tuesday_run_id` is present. | | `tuesday_service_id` | | | | | `tuesday_run_id` | | | | | `wednesday_service_id` | | | | @@ -171,34 +179,40 @@ Primary Key: `roster_id` ### `roster_dates.txt` -Primary Key: `(roster_id, date, exception_type)` +Defines exceptions to [`roster.txt`](#rostertxt), similar to how [`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt) defines exceptions to [`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt). + +This can be used to define holidays, vacations that are built into the roster position, or other exceptions. + +This file may be used even when `roster.txt` is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. + +Primary Key: `(roster_position_id, date, exception_type)` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | -| `roster_id` | ID referencing `rosters.txt` | Required | (note: should `roster_id` have to be in `rosters.txt` (potentially on 0 days per week), or can you define new rosters in this file, like `calendar_dates.txt` does with service IDs?) | +| `roster_position_id` | ID referencing `roster.txt` or ID | Required | If `exception_type` is `1`, then the ID does not have to appear in `roster.txt`. This file may define new roster positions. | | `date` | Date | Required | Date when exception occurs. | -| `exception_type` | Enum | Required | `1` - The run is added to this roster for the specified date.
`2` - The roster will not work its regular run on this date. | +| `exception_type` | Enum | Optional | `1` (or blank) - The run is added to this roster for the specified date.
`2` - The roster will not work its regular run on this date. | | `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Required if `run_id` is present. | -| `run_id` | ID referencing `run_events.txt` | Conditionally Required | The run that's either added or removed from this roster. Required if `exception_type` is `1`. Optional if `exception_type` is `2`. If `exception_type` is `2` and `run_id` is not blank, then it must match the Run ID that the roster was scheduled to do on this date according to `rosters.txt` | +| `run_id` | ID referencing `run_events.txt` | Conditionally Required | The run that's either added or removed from this roster. Required if `exception_type` is `1`. Optional if `exception_type` is `2`. If `exception_type` is `2` and `run_id` is not blank, then it must match the Run ID that the roster was scheduled to do on this date according to `roster.txt` | -### `employee_rosters.txt` +### `employee_roster.txt` -Describes which employees are scheduled to which rosters on which dates. +Describes which employees are scheduled to which roster positions on which dates. -Primary Key: `(roster_id,start_date)` +Primary Key: `(roster_position_id,start_date)` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | -| `employee_id` | ID | Required | | -| `roster_id` | ID referencing `rosters.txt` | Required | | +| `roster_position_id` | ID referencing `roster.txt` or `roster_dates.txt` | Required | | | `start_date` | Date | Required | | -| `end_date` | Date | Required | | +| `end_date` | Date | Required | Included in the interval. | +| `employee_id` | ID | Required | | -Each roster can only be assigned to one employee on each date. Employees may be scheduled to more than one roster on the same date. +Each roster position can only be assigned to one employee on each date. Employees may be scheduled to more than one roster position on the same date. ### `employee_run_dates.txt` -Describes which employees are scheduled to which runs on which dates. If `employee_rosters.txt` is used, then describes exceptions to that schedule. +Describes which employees are scheduled to which runs on which dates. If `employee_roster.txt` is used, then describes exceptions to that schedule. Primary Key: `*` @@ -207,7 +221,7 @@ Primary Key: `*` | `employee_id` | ID | Required | | | `date` | Date | Required | | | `service_id` | ID referencing `run_events.txt` | Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. | -| `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_rosters.txt`, `rosters.txt` and `roster_dates.txt`. | +| `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | | `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | If a feed doesn't represent rosters, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. From 66ffdac4a9163feff5cac0854c16bf8f9724853d Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 28 Feb 2025 10:37:27 -0500 Subject: [PATCH 04/19] service_id may be omitted --- docs/spec/index.md | 55 +++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index 88c551d..d34520a 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -161,21 +161,30 @@ Primary Key: `roster_position_id` | `roster_position_id` | Unique ID | Required | Unique within dataset | | `start_date` | Date | Required | First service day that the roster position works. | | `end_date` | Date | Required | Last service day tha the roster position works. This day is included in the interval. | -| `monday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. Runs are identified by the pair `(service_id, run_id)`. Required if and only if `monday_run_id` is present. | -| `monday_run_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. If blank, this roster does not work on Mondays. Required if and only if `monday_service_id` is present. | -| `tuesday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Tuesdays. Runs are identified by the pair `(service_id, run_id)`. Required if and only if `tuesday_run_id` is present. | -| `tuesday_service_id` | | | | -| `tuesday_run_id` | | | | -| `wednesday_service_id` | | | | -| `wednesday_run_id` | | | | -| `thursday_service_id` | | | | -| `thursday_run_id` | | | | -| `friday_service_id` | | | | -| `friday_run_id` | | | | -| `saturday_service_id` | | | | -| `saturday_run_id` | | | | -| `sunday_service_id` | | | | -| `sunday_run_id` | | | | +| `monday_service_id` | ID referencing `run_events.txt` | Conditionally Required | Identifies the run this roster does on Mondays. Runs are identified by the pair `(service_id, run_id)`. Forbidden if `monday_run_id` is blank. Optional and recommended if `monday_run_id` is present. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | +| `monday_run_id` | ID referencing `run_events.txt` | Optional | Identifies the run this roster does on Mondays. If blank, this roster does not work on Mondays. | +| `tuesday_service_id` | ID referencing `run_events.txt` | Conditionally Required | | +| `tuesday_run_id` | ID referencing `run_events.txt` | Optional | | +| `wednesday_service_id` | ID referencing `run_events.txt` | Conditionally Required | | +| `wednesday_run_id` | ID referencing `run_events.txt` | Optional | | +| `thursday_service_id` | ID referencing `run_events.txt` | Conditionally Required | | +| `thursday_run_id` | ID referencing `run_events.txt` | Optional | | +| `friday_service_id` | ID referencing `run_events.txt` | Conditionally Required | | +| `friday_run_id` | ID referencing `run_events.txt` | Optional | | +| `saturday_service_id` | ID referencing `run_events.txt` | Conditionally Required | | +| `saturday_run_id` | ID referencing `run_events.txt` | Optional | | +| `sunday_service_id` | ID referencing `run_events.txt` | Conditionally Required | | +| `sunday_run_id` | ID referencing `run_events.txt` | Optional | | + +#### Service IDs in Rosters + +Run IDs may be non-unique, they may be duplicated across services. E.g. there may be a "Run 100" on both Weekday and Weekend services. So a run as described in [`run_events.txt`](#run_eventstxt) is uniquely identified by a `(service_id, run_id)` pair. It is recommeded that producers include both a Service ID and Run ID when identifying a run in `roster.txt`, [`roster_dates.txt`](#roster_datestxt), and [`employee_run_dates.txt`](#employee_run_datestxt). + +If a Run ID is included but a Service ID isn't, then the roster position or employee is assigned to work on whichever run in [`run_events.txt`](#run_eventstxt) has that Run ID and is on a service that is active that service day, according to GTFS [`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)/[`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt). (If this would be ambiguous because there are mutliple `(service_id, run_id)` pairs it could refer to, then the Service ID is required.) + +This is allowed as a shortcut for producers to reduce the level of duplication in the roster file if a roster position works runs with the same ID on different days. For example, if there's a minor schedule change one day due to track work, that day must be on a different Service ID to give new trip and run times. But if a roster position works "Run 100" on both the regular and modified service, and the roster file leaves the Service ID field blank, then the roster file doesn't need an exception for that day because it refers to "Run 100" on whichever service is happening. + +More in-depth examples and instructions on how to look up which run an employee is doing are given in [the examples](./examples.md). ### `roster_dates.txt` @@ -183,17 +192,19 @@ Defines exceptions to [`roster.txt`](#rostertxt), similar to how [`calendar_date This can be used to define holidays, vacations that are built into the roster position, or other exceptions. -This file may be used even when `roster.txt` is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. +Dates may be added before the `start_date` or after the `end_date` defined in [`roster.txt`](#rostertxt). + +This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. Primary Key: `(roster_position_id, date, exception_type)` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | -| `roster_position_id` | ID referencing `roster.txt` or ID | Required | If `exception_type` is `1`, then the ID does not have to appear in `roster.txt`. This file may define new roster positions. | +| `roster_position_id` | ID referencing [`roster.txt`](#rostertxt) or ID | Required | If `exception_type` is `1`, then the ID does not have to appear in [`roster.txt`](#rostertxt). This file may define new roster positions. | | `date` | Date | Required | Date when exception occurs. | | `exception_type` | Enum | Optional | `1` (or blank) - The run is added to this roster for the specified date.
`2` - The roster will not work its regular run on this date. | -| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Required if `run_id` is present. | -| `run_id` | ID referencing `run_events.txt` | Conditionally Required | The run that's either added or removed from this roster. Required if `exception_type` is `1`. Optional if `exception_type` is `2`. If `exception_type` is `2` and `run_id` is not blank, then it must match the Run ID that the roster was scheduled to do on this date according to `roster.txt` | +| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | +| `run_id` | ID referencing `run_events.txt` | Conditionally Required | The run that's either added or removed from this roster. Required if `exception_type` is `1`. Optional if `exception_type` is `2`. If `exception_type` is `2` and `run_id` is not blank, then it must match the Run ID that the roster was scheduled to do on this date according to [`roster.txt`](#rostertxt). | ### `employee_roster.txt` @@ -212,7 +223,7 @@ Each roster position can only be assigned to one employee on each date. Employee ### `employee_run_dates.txt` -Describes which employees are scheduled to which runs on which dates. If `employee_roster.txt` is used, then describes exceptions to that schedule. +Describes which employees are scheduled to which runs on which dates. If [`employee_roster.txt`](#employee_rostertxt) is used, then describes exceptions to that schedule. Primary Key: `*` @@ -220,10 +231,10 @@ Primary Key: `*` | --- | --- | --- | --- | | `employee_id` | ID | Required | | | `date` | Date | Required | | -| `service_id` | ID referencing `run_events.txt` | Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. | +| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | | `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | -If a feed doesn't represent rosters, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. +If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. Each run can only be assigned to one employee on each date. Employees may be scheduled to more than one run on the same date. From fb4e3b3a03b93ac1d365901904d3419875c7c725 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 28 Feb 2025 10:48:45 -0500 Subject: [PATCH 05/19] fixup uniqueness --- docs/spec/index.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index d34520a..0c9e3ea 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -194,9 +194,11 @@ This can be used to define holidays, vacations that are built into the roster po Dates may be added before the `start_date` or after the `end_date` defined in [`roster.txt`](#rostertxt). +After evaluating [`roster.txt`](#rostertxt) and `roster_dates.txt`, each run can only be assigned to one roster position on each date. A roster position may be scheduled to do multiple runs on the same date. + This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. -Primary Key: `(roster_position_id, date, exception_type)` +Primary Key: `*` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | @@ -210,7 +212,7 @@ Primary Key: `(roster_position_id, date, exception_type)` Describes which employees are scheduled to which roster positions on which dates. -Primary Key: `(roster_position_id,start_date)` +Primary Key: `(roster_position_id, start_date)` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | @@ -231,9 +233,9 @@ Primary Key: `*` | --- | --- | --- | --- | | `employee_id` | ID | Required | | | `date` | Date | Required | | +| `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | | `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | -| `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. From 025ee2e176444b4b8cf17cf03e484809fb117e94 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 28 Feb 2025 10:50:06 -0500 Subject: [PATCH 06/19] add to examples todo --- docs/spec/examples.md | 4 ++++ docs/spec/index.md | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/spec/examples.md b/docs/spec/examples.md index 4e6bf40..cf93758 100644 --- a/docs/spec/examples.md +++ b/docs/spec/examples.md @@ -184,4 +184,8 @@ TODO I haven't written examples yet, but here are ones I think would be good: - `employee_dates`, remove the employee from the roster, add the spare employee. - Producer that doesn't use rosters, just uses `employee_dates` for everything. +Also instructions for: +- Given an date and trip ID, determine which employee is working on that trip. +- Given a date and employee, determine which trips they're doing that day. + (There's enough of them that they should be on a separate page.) diff --git a/docs/spec/index.md b/docs/spec/index.md index 0c9e3ea..5693774 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -184,7 +184,7 @@ If a Run ID is included but a Service ID isn't, then the roster position or empl This is allowed as a shortcut for producers to reduce the level of duplication in the roster file if a roster position works runs with the same ID on different days. For example, if there's a minor schedule change one day due to track work, that day must be on a different Service ID to give new trip and run times. But if a roster position works "Run 100" on both the regular and modified service, and the roster file leaves the Service ID field blank, then the roster file doesn't need an exception for that day because it refers to "Run 100" on whichever service is happening. -More in-depth examples and instructions on how to look up which run an employee is doing are given in [the examples](./examples.md). +More in-depth examples and instructions on how to look up which run an employee is doing are given in [the examples](./examples.md). (TODO link directly to relevant example page.) ### `roster_dates.txt` @@ -196,7 +196,7 @@ Dates may be added before the `start_date` or after the `end_date` defined in [` After evaluating [`roster.txt`](#rostertxt) and `roster_dates.txt`, each run can only be assigned to one roster position on each date. A roster position may be scheduled to do multiple runs on the same date. -This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. +This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. (TODO link to example.) Primary Key: `*` @@ -237,6 +237,6 @@ Primary Key: `*` | `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | -If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. +If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. (TODO link to example.) Each run can only be assigned to one employee on each date. Employees may be scheduled to more than one run on the same date. From f9dbb9ae44bcdea8dd1ddfbb712cef8f0ddac80f Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 28 Feb 2025 10:55:50 -0500 Subject: [PATCH 07/19] typo --- docs/spec/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index 5693774..a95ed54 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -152,7 +152,7 @@ This file defines roster positions, groupings of work across multiple runs on mu Exceptions to these dates may be listed in [`roster_dates.txt`](#roster_datestxt). -Employees are assigned to these roster positions in [`employee_roster.txt](#employee_rostertxt). +Employees are assigned to these roster positions in [`employee_roster.txt`](#employee_rostertxt). Primary Key: `roster_position_id` From 3780c5bed67dcfacf33c81bf544bff265de7b950 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Mon, 3 Mar 2025 10:44:40 -0500 Subject: [PATCH 08/19] move drafts of examples into a new file --- docs/spec/examples.md | 23 ++------------ docs/spec/examples/rostering.md | 53 +++++++++++++++++++++++++++++++++ docs/spec/index.md | 6 ++-- 3 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 docs/spec/examples/rostering.md diff --git a/docs/spec/examples.md b/docs/spec/examples.md index cf93758..96f09ae 100644 --- a/docs/spec/examples.md +++ b/docs/spec/examples.md @@ -169,23 +169,6 @@ weekday,10000,30,BLOCK-A,run-as-directed,,stop-1,09:00:00,stop-1,12:00:00 weekday,10000,30,BLOCK-A,deadhead ,,stop-1,12:00:00,garage,12:10:00 ``` -## Crew/Roster - -TODO I haven't written examples yet, but here are ones I think would be good: - -- UK scheduling (employees rotate per week) -- North American scheduling (pick one roster for the whole rating. I don't think there'll be a big difference between roster-style and cafeteria-style in the data? ) -- A holiday -- Scheduled track work one day, times/service_id are slightly changed, but substantially the same. - - `roster_dates.txt`: `roster_id,date,old_service_id,2;...new_service_id,1` -- Vacation that's part of the roster. - - `roster_dates.txt`: remove old one. assign spare_roster_id (which has no regular assignment) to it on that date. Maybe `spare_roster_id` still needs to be in `rosters.txt` for comprehensive listing reasons, for start/end date, and for (doesn't exist yet) metadata like label -- Vacation that's not part of the roster, and done by assigning a different employee to that run that date. - - `employee_dates`, remove the employee from the roster, add the spare employee. -- Producer that doesn't use rosters, just uses `employee_dates` for everything. - -Also instructions for: -- Given an date and trip ID, determine which employee is working on that trip. -- Given a date and employee, determine which trips they're doing that day. - -(There's enough of them that they should be on a separate page.) +## Roster Assignments + +See examples about how to do crew / roster assignments in [Rostering Examples](./rostering.md). diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md new file mode 100644 index 0000000..a1dbef0 --- /dev/null +++ b/docs/spec/examples/rostering.md @@ -0,0 +1,53 @@ +# Rostering Examples + +A series of examples about how to use TODS to use [roster.txt](/docs/spec.md#rostertxt), [roster_dates.txt](/docs/spec.md#roster_datestxt), [employee_roster.txt](/docs/spec.md#employee_rostertxt), and [employee_run_dates.txt](/docs/spec.md#employee_run_datestxt) to assign employees to roster positions and runs. + +TODO this whole file is still being drafted. The list of which examples to include is complete, but no examples are done being drafted. +TODO check that all links work. + +## Simple rostering example + +(North American scheduling. pick one roster for the whole rating. I don't think there'll be a big difference between roster-style and cafeteria-style in the data?) + +## Given an date and trip ID, look up which employee is working on that trip + +## Given an date and employee, look up which trips they're doing that day + +## Holiday + +## Vacation (part of the roster position) + +(`roster_dates.txt`: remove old one. assign spare_roster_id (which has no regular assignment) to it on that date. Maybe `spare_roster_id` still needs to be in `rosters.txt` for comprehensive listing reasons, for start/end date, and for (doesn't exist yet) metadata like label) + +TODO if it turns out to be identical to Holiday, delete this section and add a note to Holiday that it can be used for vacations too. + +## Vacation (not part of the roster position) + +In this case, the roster position is not changed on the vacation day, and instead a different employee is assigned to the run on this date using [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt) + +(`employee_dates`: remove the employee from the roster, add the spare employee.) + +## Multi-week roster positions + +In this case, each roster position repeats its runs every two weeks, instead of every one week, which is standard at some agencies. + +TODO this is common in continental europe, right? + +In TODS, this can be represented by having the first and second weeks be two different roster positions, and giving an employee a new roster position assignment each week. + +## Rotating assignments. + +In the UK, it's common for employees to rotate among all roster positions over many weeks. In TODS, this can be represented by assigning each employee to a new roster position each week. + +## Minor schedule adjustment + +- Scheduled track work one day, times/service_id are slightly changed, but substantially the same. + - `roster_dates.txt`: `roster_id,date,old_service_id,2;...new_service_id,1` + +## Define each roster position day-by-day + +No weekly rosters / roster.txt. Just roster_dates.txt. + +## No rosters, just `employee_run_dates.txt` + +- Producer that doesn't use rosters, just uses `employee_dates` for everything. diff --git a/docs/spec/index.md b/docs/spec/index.md index a95ed54..61823cd 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -184,7 +184,7 @@ If a Run ID is included but a Service ID isn't, then the roster position or empl This is allowed as a shortcut for producers to reduce the level of duplication in the roster file if a roster position works runs with the same ID on different days. For example, if there's a minor schedule change one day due to track work, that day must be on a different Service ID to give new trip and run times. But if a roster position works "Run 100" on both the regular and modified service, and the roster file leaves the Service ID field blank, then the roster file doesn't need an exception for that day because it refers to "Run 100" on whichever service is happening. -More in-depth examples and instructions on how to look up which run an employee is doing are given in [the examples](./examples.md). (TODO link directly to relevant example page.) +More in-depth examples and instructions on how to look up which run an employee is doing are given in [the examples](./examples/rostering.md#given-an-date-and-employee-look-up-which-trips-theyre-doing-that-day). ### `roster_dates.txt` @@ -196,7 +196,7 @@ Dates may be added before the `start_date` or after the `end_date` defined in [` After evaluating [`roster.txt`](#rostertxt) and `roster_dates.txt`, each run can only be assigned to one roster position on each date. A roster position may be scheduled to do multiple runs on the same date. -This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. (TODO link to example.) +This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#TODO). Primary Key: `*` @@ -237,6 +237,6 @@ Primary Key: `*` | `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | -If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. (TODO link to example.) +If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#TODO). Each run can only be assigned to one employee on each date. Employees may be scheduled to more than one run on the same date. From 991e8b34bc9059e0de8212f7add537fae53ae8d5 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Mon, 3 Mar 2025 10:50:43 -0500 Subject: [PATCH 09/19] lint --- docs/spec/examples/rostering.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index a1dbef0..2d7189f 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -35,14 +35,14 @@ TODO this is common in continental europe, right? In TODS, this can be represented by having the first and second weeks be two different roster positions, and giving an employee a new roster position assignment each week. -## Rotating assignments. +## Rotating assignments In the UK, it's common for employees to rotate among all roster positions over many weeks. In TODS, this can be represented by assigning each employee to a new roster position each week. ## Minor schedule adjustment - Scheduled track work one day, times/service_id are slightly changed, but substantially the same. - - `roster_dates.txt`: `roster_id,date,old_service_id,2;...new_service_id,1` + - `roster_dates.txt`: `roster_id,date,old_service_id,2;...new_service_id,1` ## Define each roster position day-by-day From 24f2502caee1273cfb0b405e0b993cf3e5793074 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 7 Mar 2025 18:29:47 -0500 Subject: [PATCH 10/19] adjust employee_id column order and docs --- docs/spec/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index 61823cd..cf8ace2 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -231,11 +231,11 @@ Primary Key: `*` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | -| `employee_id` | ID | Required | | | `date` | Date | Required | | | `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | | `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | +| `employee_id` | ID | Required | References an agency's external systems. Employee IDs are not used elsewhere in TODS. | If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#TODO). From f3c883b949f7567db0a4faf2e0e6bde587047bf6 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Fri, 7 Mar 2025 18:29:56 -0500 Subject: [PATCH 11/19] write a couple examples --- docs/spec/examples/rostering.md | 76 ++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index 2d7189f..bf4334b 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -5,9 +5,81 @@ A series of examples about how to use TODS to use [roster.txt](/docs/spec.md#ros TODO this whole file is still being drafted. The list of which examples to include is complete, but no examples are done being drafted. TODO check that all links work. -## Simple rostering example +## Simplest example: employee_run_dates.txt only. -(North American scheduling. pick one roster for the whole rating. I don't think there'll be a big difference between roster-style and cafeteria-style in the data?) +The simplest way to assign employees to runs is to use `employee_run_dates.txt`. This will require one row per employee per date. + +In this example, `A` and `B` work Saturday Feb 1 and Wednesday Feb 5 through Friday Feb 7. `C` and `D` work Sunday Feb 2 through Tuesday Feb 4. + +**`calendar.txt`** + +``` +service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date +weekend,0,0,0,0,0,1,1,20250201,20250207 +weekday,1,1,1,1,1,0,0,20250201,20250207 +``` + +February 1, 2025 was a Saturday. + +**`run_events.txt`** + +For this example, the purpose of this file is just to show which runs exist. Real runs would have more interesting data. + +``` +service_id,run_id,event_sequence,event_type,start_location,start_time,end_location,end_time +weekend,101,1,work,station,09:00:00,station,17:00:00 +weekend,102,1,work,station,09:00:00,station,17:00:00 +weekday,103,1,work,station,09:00:00,station,17:00:00 +weekday,104,1,work,station,09:00:00,station,17:00:00 +``` + +**`employee_run_dates.txt`** + +``` +date,service_id,run_id,employee_id +20250201,weekend,101,A +20250201,weekend,102,B +20250202,weekend,101,C +20250202,weekend,102,D +20250203,weekday,103,C +20250203,weekday,104,D +20250204,weekday,103,C +20250204,weekday,104,D +20250205,weekday,103,A +20250205,weekday,104,B +20250206,weekday,103,A +20250206,weekday,104,B +20250207,weekday,103,A +20250207,weekday,104,B +``` + +## Example with Rosters + +This example represents the same schedule as above, but groups multiple days of work into roster positions that employees are assigned to all at once. `A` and `B` work Saturdays, Wednesdays, Thursdays, and Fridays. `C` and `D` work Sundays, Mondays, and Tuesdays. + +`calendar.txt` and `run_events.txt` are the same as the previous example. + +This example does not have any exceptions to the regular schedule, so doesn't need `roster_dates.txt` or `employee_run_dates.txt`. + +**`roster_positions.txt`** + +``` +roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id +wed_to_sat_1,20250201,20250207,,,,,weekday,103,weekday,103,weekday,103,weekend,101,, +wed_to_sat_2,20250201,20250207,,,,,weekday,104,weekday,104,weekday,104,weekend,102,, +sun_to_tue_1,20250201,20250207,weekday,103,weekday,103,,,,,,,,,weekend,101 +sun_to_tue_2,20250201,20250207,weekday,104,weekday,104,,,,,,,,,weekend,102 +``` + +**`employee_roster.txt`** + +``` +roster_position_id,start_date,end_date,employee_id +wed_to_sat_1,20250201,20250207,A +wed_to_sat_2,20250201,20250207,B +sun_to_tue_1,20250201,20250207,C +sun_to_tue_2,20250201,20250207,D +``` ## Given an date and trip ID, look up which employee is working on that trip From 737edcec65914f75d87453c9ba668f5bb54fc719 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Mon, 10 Mar 2025 21:35:49 -0400 Subject: [PATCH 12/19] write a couple more examples --- docs/spec/examples/rostering.md | 362 +++++++++++++++++++++++++++++++- 1 file changed, 353 insertions(+), 9 deletions(-) diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index bf4334b..573151f 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -1,9 +1,10 @@ # Rostering Examples -A series of examples about how to use TODS to use [roster.txt](/docs/spec.md#rostertxt), [roster_dates.txt](/docs/spec.md#roster_datestxt), [employee_roster.txt](/docs/spec.md#employee_rostertxt), and [employee_run_dates.txt](/docs/spec.md#employee_run_datestxt) to assign employees to roster positions and runs. +A series of examples about how to use [roster.txt](/docs/spec.md#rostertxt), [roster_dates.txt](/docs/spec.md#roster_datestxt), [employee_roster.txt](/docs/spec.md#employee_rostertxt), and [employee_run_dates.txt](/docs/spec.md#employee_run_datestxt) to assign employees to roster positions and runs. -TODO this whole file is still being drafted. The list of which examples to include is complete, but no examples are done being drafted. +TODO some examples are still being drafted TODO check that all links work. +TODO check column ordering ## Simplest example: employee_run_dates.txt only. @@ -55,7 +56,7 @@ date,service_id,run_id,employee_id ## Example with Rosters -This example represents the same schedule as above, but groups multiple days of work into roster positions that employees are assigned to all at once. `A` and `B` work Saturdays, Wednesdays, Thursdays, and Fridays. `C` and `D` work Sundays, Mondays, and Tuesdays. +This example represents the same schedule of runs as above, but groups multiple days of work into roster positions that employees are assigned to all at once. Roster positions `A` and `B` work Saturdays, Wednesdays, Thursdays, and Fridays. Positions `C` and `D` work Sundays, Mondays, and Tuesdays. `calendar.txt` and `run_events.txt` are the same as the previous example. @@ -81,23 +82,366 @@ sun_to_tue_1,20250201,20250207,C sun_to_tue_2,20250201,20250207,D ``` -## Given an date and trip ID, look up which employee is working on that trip +## Example algorithm for looking up data -## Given an date and employee, look up which trips they're doing that day +Here are example algorithms (written in pseudocode) for a couple typical lookups a consumer might do in the roster files. These examples show how to connect data across all the roster files. + +This algorithm will cover edge cases and work for both simple and complex cases. If you know you have simpler data, you may be able to use simpler rules. + +### Given a date and trip ID, look up which employees are working on that trip + +``` +# Returns a list of employee IDs +def employees_on_trip(service_date, trip_id): + # List of (service_id, run_id). There may be 0 (run_events.txt is incomplete), 1, or many (if multiple people work on that trip). + runs_on_trip = + SELECT DISTINCT (service_id, run_id) FROM run_events.txt + WHERE run_events.trip_id = ${trip_id} + + employees_on_trip = [] + for each (service_id, run_id) in runs_on_trip: + roster_positions_on_run = roster_positions_on_run(service_id, run_id, service_date) + employees_on_run = employees_on_run(service_id, run_id, roster_positions_on_run, service_date) + employees_on_trip.add(employees_on_run) + + return employees_on_trip + +# Returns a list of roster position IDs +# There could be 0 (if the data is incomplete), 1 (the normal situation), or many (shouldn't happen but isn't prohibited by the spec) +def roster_positions_on_run(service_id, run_id, service_date): + day_of_week = day_of_week_for_date(service_date) + result = [] + # start with roster positions that do this run on a regular week + result.add( + SELECT roster_position_id FROM roster_positions.txt + WHERE start_date <= ${service_date} + AND end_date >= ${service_date} + AND ${day_of_week}_run_id = ${run_id} + AND ("${day_of_week}_service_id" IS NULL OR "${day_of_week}_service_id" = ${service_id}) + ) + # remove roster positions with exception_type=2 in roster_dates.txt + result.remove( + SELECT roster_position_id FROM roster_dates.txt + WHERE date = ${service_date} + AND exception_type = 2 + AND roster_position_id = ${roster_position_id} + ) + # add roster positions with exception_type=1 in roster_dates.txt + result.remove( + SELECT roster_position_id FROM roster_dates.txt + WHERE date = ${service_date} + AND (exception_type IS NULL OR exception_type = 1) + AND roster_position_id = ${roster_position_id} + ) + return result + +# Returns a list of employee IDs +# There could be 0 (if the work is unassigned), 1 (the normal situation), or many (shouldn't happen but isn't prohibited by the spec) +def employees_on_run(service_id, run_id, roster_positions_on_run, service_date): + result = [] + # start with the employees assigned to the run via the roster + result.add( + SELECT employee_id FROM employee_roster.txt + WHERE employee_roster.roster_position_id in ${roster_positions_on_run} + AND employee_roster.start_date <= ${service_date} + AND employee_roster.end_date >= ${service_date} + ) + # remove any employees with exception_type=2 + result.remove( + SELECT employee_id FROM employee_run_dates.txt + WHERE employee_run_dates.date = ${service_date} + AND employee_run_dates.exception_type = 2 + AND employee_run_dates.employee_id in ${employees_on_run} + ) + # add any employees with exception_type=1 for this run + result.add( + SELECT employee_id FROM employee_run_dates.txt + WHERE date = ${service_date} + AND (exception_type IS NULL OR exception_type = 1) + AND run_id = ${run_id} + AND (service_id IS NULL OR service_id = ${service_id}) + ) + return result +``` + +### Given an date and employee ID, look up which trips they're working on that day + +``` +# Returns a list of trip IDs +def trips_for_employee(employee_id, service_date): + # List of roster postions that the employee is doing on this date + # There could be 0 (if the employee doesn't have regular work, such as someone on a spare list) + # or 1 (a normal situation), + # or many (if an employee is assigned to multiple roster positions, which would be unusual but is allowed by the spec) + roster_position_ids = + SELECT roster_position_id FROM employee_roster.txt + WHERE employee_id = ${employee_id} + AND start_date <= ${service_date} + AND end_date >= ${service_date} + + # List of (service_id, run_id) pairs, or (NULL, run_id) if the data omits the service id + runs = [] + # first get runs that come from roster position assignments + for roster_position_id in roster_position_ids: + runs.add(runs_for_roster_position(roster_position_id, service_date)) + # then remove any runs removed by employee_run_dates.txt + runs.remove( + SELECT (service_id, run_id) FROM employee_run_dates.txt + WHERE date = ${service_date} + AND exception_type = 2 + AND employee_id = ${employee_id} + ) + # then add any runs added by employee_run_dates.txt + runs.add( + SELECT (service_id, run_id) FROM employee_run_dates.txt + WHERE date = ${service_date} + AND exception_type = 1 + AND employee_id = ${employee_id} + ) + + # Now we know which runs the employee is working on this date. Look up the trips on those runs in run_events.txt + trip_ids = [] + # note service_id might be NULL here + for (service_id, run_id) in runs: + trip_ids.add(trips_for_run(service_id, run_id, service_date)) + return trip_ids + +# returns List of (service_id, run_id) pairs +# service_id could be NULL if the data omits it +# There could be 0 results if the roster position is not working that day +# 1 result if it is +# or many if the roster position is working multiple runs, which would not be common but is allowed by the roster_dates.txt spec. +def runs_for_roster_position(roster_position_id, service_date): + day_of_week = day_of_week_for_date(service_date) + result = [] + # start with runs from the roster assignment, at most one result + result.add( + SELECT ("${day_of_week}_service_id", "${day_of_week}_run_id") + FROM roster_positions.txt + WHERE roster_position_id = ${roster_position_id} + AND start_date <= ${service_date} + AND end_date >= ${service_date} + ) + # if the roster position has exception_type=2 in run_dates.txt, then ignore the run from roster_positions.txt + if ( + SELECT (service_id, run_id) + FROM roster_dates.txt + WHERE roster_position_id = ${roster_position_id} + AND date = ${service_date} + AND exception_type = 2 + ): + result = [] + # add any runs with exception_type=1 in run_dates.txt + result.add( + SELECT (service_id, run_id) + FROM roster_dates.txt + WHERE roster_position_id = ${roster_position_id} + AND date = ${service_date} + AND exception_type = 1 + ) + return result + +# input service_id might be NULL +# Returns list of trip IDs +def trips_for_run(service_id, run_id, service_date): + # If the roster data omits service_ids, allow matching to a run on any service_id that's active on this date + # Look up which services are active from the GTFS calendar (after applying any TODS supplement files), based on the service_date and day_of_week. + # In real code you'd probably want to calculate this once for all queries + active_services = ... + + if service_id == NULL: + SELECT trip_id FROM run_events.txt + WHERE run_id = ${run_id} + AND service_id in ${active_services} + else: + SELECT trip_id FROM run_events.txt + WHERE run_id = ${run_id} + AND service_id = ${service_id} +``` ## Holiday +In this example, the roster includes holidays, which are defined as exceptions in `roster_dates.txt`. + +This agency has two workers, who work M-F. There's no service on weekends. On each holiday, the agency runs half the service, and each worker gets one holiday off. + +The holidays are built into the roster positions, so there's no need for `employee_run_dates.txt`. + +**`calendar.txt`** + +``` +service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date +weekday,1,1,1,1,1,0,0,20240701,20240714 +``` + +July 1, 2024 was a Monday. + +**`calendar_dates.txt`** + +``` +service_id,date,exception_type +weekday,20240702,2 +holiday,20240702,1 +weekday,20240711,2 +holiday,20240711,1 +``` + +Holidays are Tuesday, July 2, and Thursday, July 11. + +**`run_events.txt`** + +For this example, the purpose of this file is just to show which runs exist. Real runs would have more interesting data. + +``` +service_id,run_id,event_sequence,event_type,start_location,start_time,end_location,end_time +weekday,101,1,work,station,09:00:00,station,17:00:00 +weekday,102,1,work,station,09:00:00,station,17:00:00 +holiday,999,1,work,station,09:00:00,station,17:00:00 +``` + +**`roster_positions.txt`** + +``` +roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id +POSITION-A,weekday,101,weekday,101,weekday,101,weekday,101,weekday,101,,,, +POSITION-B,weekday,102,weekday,102,weekday,102,weekday,102,weekday,102,,,, +``` + +**`roster_dates.txt`** + +``` +roster_position_id,date,exception_type,service_id,run_id +POSITION-A,20240702,2,, +POSITION-B,20240702,2,, +POSITION-A,20240702,1,holiday,999 +POSITION-A,20240711,2,, +POSITION-B,20240711,2,, +POSITION-B,20240711,1,holiday,999 +``` + +**`employee_roster.txt`** + +``` +roster_position_id,start_date,end_date,employee_id +POSITION-A,20240701,20240714,EMPLOYEE-A +POSITION-B,20240701,20240714,EMPLOYEE-B +``` + ## Vacation (part of the roster position) -(`roster_dates.txt`: remove old one. assign spare_roster_id (which has no regular assignment) to it on that date. Maybe `spare_roster_id` still needs to be in `rosters.txt` for comprehensive listing reasons, for start/end date, and for (doesn't exist yet) metadata like label) +In this example, this agency runs service 4 days a week, with two employees who each regularly work two days per week. When each employee goes on vacation, their work is covered by a third employee on a third roster position. + +These vacations are built in to the roster. + +The third employee has no regular work, so appears in `roster_dates.txt` but not `roster_positions.txt`. + +**`calendar.txt`** + +``` +service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date +weekday,1,1,0,1,1,0,0,20240701,20240721 +``` + +July 1, 2024 was a Monday. + +**`run_events.txt`** + +In this simple example, there's only one employee working per day, and they only do one run_event per day. + +``` +service_id,run_id,event_sequence,event_type,start_location,start_time,end_location,end_time +weekday,100,1,work,station,09:00:00,station,17:00:00 +``` + +**`roster_positions.txt`** + +One works Monday and Tuesady, the other works Thursday and Friday. + +``` +roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id +POSITION-A,weekday,100,weekday,100,,,,,,,,,, +POSITION-B,,,,,,,weekday,100,weekday,100,,,, +``` + +**`roster_dates.txt`** + +When each roster position goes on vacation for two days, the third subsitute roster position fills in. + +``` +roster_position_id,date,exception_type,service_id,run_id +POSITION-A,20240708,2,, +POSITION-C,20240708,1,weekday,100 +POSITION-A,20240709,2,, +POSITION-C,20240709,1,weekday,100 +POSITION-B,20240718,2,, +POSITION-C,20240718,1,weekday,100 +POSITION-B,20240719,2,, +POSITION-C,20240719,1,weekday,100 +``` + +**`employee_roster.txt`** + +``` +roster_position_id,start_date,end_date,employee_id +POSITION-A,20240701,20240721,EMPLOYEE-A +POSITION-B,20240701,20240721,EMPLOYEE-B +POSITION-C,20240701,20240721,EMPLOYEE-C +``` + +The vacations are built into the roster positions, so the employees stay assigned to the roster position the whole time. There's no need for `employee_run_dates.txt`. -TODO if it turns out to be identical to Holiday, delete this section and add a note to Holiday that it can be used for vacations too. ## Vacation (not part of the roster position) -In this case, the roster position is not changed on the vacation day, and instead a different employee is assigned to the run on this date using [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt) +In this case, the same employees work on the same days as in the previous example. But this time the roster position continue their regular assignments on the vacation day, and the employees get their vacations by being unassigned from their roster position in `employee_run_dates.txt`. + +`calendar.txt` and `run_events.txt` are the same as above. + +**`roster_positions.txt`** + +`roster_positions.txt` is the same as in the previous example, because the regular week is the same. + +One works Monday and Tuesady, the other works Thursday and Friday. + +``` +roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id +POSITION-A,weekday,100,weekday,100,,,,,,,,,, +POSITION-B,,,,,,,weekday,100,weekday,100,,,, +``` + +In this example, there is no `roster_dates.txt` file, because the roster positions continue reguarly. There is no 3rd roster position. + +**`employee_roster.txt`** -(`employee_dates`: remove the employee from the roster, add the spare employee.) +``` +roster_position_id,start_date,end_date,employee_id +POSITION-A,20240701,20240721,EMPLOYEE-A +POSITION-B,20240701,20240721,EMPLOYEE-B +``` + +`EMPLOYEE-C` has no roster position that they are regularly assigned to. + +**`employee_run_dates.txt`** + +POSITION-A,20240708,2,, +POSITION-C,20240708,1,weekday,100 +POSITION-A,20240709,2,, +POSITION-C,20240709,1,weekday,100 +POSITION-B,20240718,2,, +POSITION-C,20240718,1,weekday,100 +POSITION-B,20240719,2,, +POSITION-C,20240719,1,weekday,100 +``` +date,exception_type,service_id,run_id,employee_id +20240708,2,,EMPLOYEE-A +20240708,1,weekday,100,EMPLOYEE-C +20240709,2,,EMPLOYEE-A +20240709,1,weekday,100,EMPLOYEE-C +20240718,2,,EMPLOYEE-B +20240718,1,weekday,100,EMPLOYEE-C +20240719,2,,EMPLOYEE-B +20240719,1,weekday,100,EMPLOYEE-C +``` ## Multi-week roster positions From 9c9b4b9c03d3a6b92dcb797f98c0d6ce7ba6984c Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Wed, 12 Mar 2025 17:29:29 -0400 Subject: [PATCH 13/19] finish drafing examples --- docs/spec/examples/rostering.md | 147 ++++++++++++++++++++++++++++---- docs/spec/index.md | 2 +- 2 files changed, 131 insertions(+), 18 deletions(-) diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index 573151f..a3dad9d 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -2,7 +2,6 @@ A series of examples about how to use [roster.txt](/docs/spec.md#rostertxt), [roster_dates.txt](/docs/spec.md#roster_datestxt), [employee_roster.txt](/docs/spec.md#employee_rostertxt), and [employee_run_dates.txt](/docs/spec.md#employee_run_datestxt) to assign employees to roster positions and runs. -TODO some examples are still being drafted TODO check that all links work. TODO check column ordering @@ -303,8 +302,8 @@ holiday,999,1,work,station,09:00:00,station,17:00:00 ``` roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id -POSITION-A,weekday,101,weekday,101,weekday,101,weekday,101,weekday,101,,,, -POSITION-B,weekday,102,weekday,102,weekday,102,weekday,102,weekday,102,,,, +POSITION-A,20240701,20240714,weekday,101,weekday,101,weekday,101,weekday,101,weekday,101,,,, +POSITION-B,20240701,20240714,weekday,102,weekday,102,weekday,102,weekday,102,weekday,102,,,, ``` **`roster_dates.txt`** @@ -359,8 +358,8 @@ One works Monday and Tuesady, the other works Thursday and Friday. ``` roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id -POSITION-A,weekday,100,weekday,100,,,,,,,,,, -POSITION-B,,,,,,,weekday,100,weekday,100,,,, +POSITION-A,20240701,20240721,weekday,100,weekday,100,,,,,,,,,, +POSITION-B,20240701,20240721,,,,,,,weekday,100,weekday,100,,,, ``` **`roster_dates.txt`** @@ -405,8 +404,8 @@ One works Monday and Tuesady, the other works Thursday and Friday. ``` roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id -POSITION-A,weekday,100,weekday,100,,,,,,,,,, -POSITION-B,,,,,,,weekday,100,weekday,100,,,, +POSITION-A,20240701,20240721,weekday,100,weekday,100,,,,,,,,,, +POSITION-B,20240701,20240721,,,,,,,weekday,100,weekday,100,,,, ``` In this example, there is no `roster_dates.txt` file, because the roster positions continue reguarly. There is no 3rd roster position. @@ -445,25 +444,139 @@ date,exception_type,service_id,run_id,employee_id ## Multi-week roster positions -In this case, each roster position repeats its runs every two weeks, instead of every one week, which is standard at some agencies. +At some agencies (especially in Europe), roster positions repeat every two weeks. In TODS, this can be represented by having the first and second weeks be two different roster positions, and using `employee_roster.txt` to assign employees to each roster position each week. -TODO this is common in continental europe, right? +In this example, there's one run per day on weekdays only. Position A works Mondays and Tuesdays. Position B works Thursdays and Fridays. They trade off on Wednesdays, so each position works an average of 2½ days per week. -In TODS, this can be represented by having the first and second weeks be two different roster positions, and giving an employee a new roster position assignment each week. +**`calendar.txt`** + +``` +service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date +weekday,1,1,1,1,1,0,0,20240701,20240728 +``` + +**`roster_positions.txt`** + + +``` +roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id +POSITION-A-WEEK-1,20240701,20240728,weekday,100,weekday,100,weekday,100,,,,,,,, +POSITION-A-WEEK-2,20240701,20240728,weekday,100,weekday,100,,,,,,,,,, +POSITION-B-WEEK-1,20240701,20240728,,,,,,,weekday,100,weekday,100,,,, +POSITION-B-WEEK-2,20240701,20240728,,,,,weekday,100,weekday,100,weekday,100,,,, +``` + +**`employee_roster.txt`** + +``` +roster_position_id,start_date,end_date,employee_id +POSITION-A-WEEK1,20240701,20240707,EMPLOYEE-A +POSITION-B-WEEK1,20240701,20240707,EMPLOYEE-B +POSITION-A-WEEK2,20240707,20240714,EMPLOYEE-A +POSITION-B-WEEK2,20240707,20240714,EMPLOYEE-B +POSITION-A-WEEK1,20240715,20240721,EMPLOYEE-A +POSITION-B-WEEK1,20240715,20240721,EMPLOYEE-B +POSITION-A-WEEK2,20240722,20240728,EMPLOYEE-A +POSITION-B-WEEK2,20240722,20240728,EMPLOYEE-B +``` ## Rotating assignments -In the UK, it's common for employees to rotate among all roster positions over many weeks. In TODS, this can be represented by assigning each employee to a new roster position each week. +At some agencies (especially in the UK), employees rotate among all roster positions over many weeks. In TODS, this can be represented by assigning each employee to a new roster position each week. + +In this example, there are 5 employees and 5 roster positions. Over a calendar of 3 weeks, each employee will work 3 of the 5 positions. + +**`calendar.txt`** + +``` +service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date +weekday,1,1,1,1,1,0,0,20240701,20240721 +``` + +**`roster_positions.txt`** + + +``` +roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id +POSITION-1,20240701,20240721,weekday,101,weekday,101,weekday,101,weekday,101,weekday,101,,,, +POSITION-2,20240701,20240721,weekday,102,weekday,102,weekday,102,weekday,102,weekday,102,,,, +POSITION-3,20240701,20240721,weekday,103,weekday,103,weekday,103,weekday,103,weekday,103,,,, +POSITION-4,20240701,20240721,weekday,104,weekday,104,weekday,104,weekday,104,weekday,104,,,, +POSITION-5,20240701,20240721,weekday,105,weekday,105,weekday,105,weekday,105,weekday,105,,,, +``` + +**`employee_roster.txt`** + +``` +roster_position_id,start_date,end_date,employee_id +POSITION-1,20240701,20240707,EMPLOYEE-A +POSITION-2,20240701,20240707,EMPLOYEE-B +POSITION-3,20240701,20240707,EMPLOYEE-C +POSITION-4,20240701,20240707,EMPLOYEE-D +POSITION-5,20240701,20240707,EMPLOYEE-E +POSITION-1,20240708,20240714,EMPLOYEE-E +POSITION-2,20240708,20240714,EMPLOYEE-A +POSITION-3,20240708,20240714,EMPLOYEE-B +POSITION-4,20240708,20240714,EMPLOYEE-C +POSITION-5,20240708,20240714,EMPLOYEE-D +POSITION-1,20240715,20240721,EMPLOYEE-D +POSITION-2,20240715,20240721,EMPLOYEE-E +POSITION-3,20240715,20240721,EMPLOYEE-A +POSITION-4,20240715,20240721,EMPLOYEE-B +POSITION-5,20240715,20240721,EMPLOYEE-C +``` ## Minor schedule adjustment -- Scheduled track work one day, times/service_id are slightly changed, but substantially the same. - - `roster_dates.txt`: `roster_id,date,old_service_id,2;...new_service_id,1` +In this example, due to track work, the schedule has been minorly changed one day. There are new trip times, trip IDs, run event times, and a new service ID. + +If the roster files specify service IDs (which is recommended), then either `roster_dates.txt` or `employee_run_dates.txt` must be used to assign the roster position or employee to the new `(service_id, run_id)` pair, i.e. `(trackwork, 100)` instead of `(weekday, 100)`. + +However, in this example, the roster does not specify the service ID in `roster_positions.txt`. Therefore, the employee is assigned to run 100 on whichever service is active. Since both the regular and replacement runs have run ID `100`, on the day of the exception, the employee will be implicitly assigned to `(trackwork, 100)` without needing to specify that in the roster files. + +The spec describes this situation in [Service IDs in Rosters](/docs/spec/index.md#service-ids-in-rosters). + +**`calendar.txt`** + +``` +service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date +weekday,1,1,1,1,1,0,0,20250201,20240728 +``` + +**`calendar_dates.txt`** + +The trackwork is on Friday, February 2. + +``` +service_id,date,exception_type +weekday,20250207,2 +trackwork,20250207,1 +``` + +**`run_events.txt`** -## Define each roster position day-by-day +Because of the track work, travel is slower and the times are adjusted. -No weekly rosters / roster.txt. Just roster_dates.txt. +``` +service_id,run_id,event_sequence,event_type,trip_id,start_location,start_time,end_location,end_time +weekday,100,1,Operator,1001,suburb,08:00:00,downtown,09:00:00 +weekday,100,2,Operator,1002,downtown,17:00:00,suburb,18:00:00 +trackwork,100,1,Operator,2001,suburb,08:00:00,downtown,09:30:00 +trackwork,100,2,Operator,2002,downtown,16:30:00,suburb,18:00:00 +``` + +**`roster_positions.txt`** -## No rosters, just `employee_run_dates.txt` +Note that the service ID fields are left blank. -- Producer that doesn't use rosters, just uses `employee_dates` for everything. +``` +roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id +POSITION,20250201,20250228,,100,,100,,100,,100,,100,,,,, +``` + +**`employee_roster.txt`** + +``` +roster_position_id,start_date,end_date,employee_id +POSITION,20250201,20250228,EMPLOYEE +``` diff --git a/docs/spec/index.md b/docs/spec/index.md index cf8ace2..8903adc 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -180,7 +180,7 @@ Primary Key: `roster_position_id` Run IDs may be non-unique, they may be duplicated across services. E.g. there may be a "Run 100" on both Weekday and Weekend services. So a run as described in [`run_events.txt`](#run_eventstxt) is uniquely identified by a `(service_id, run_id)` pair. It is recommeded that producers include both a Service ID and Run ID when identifying a run in `roster.txt`, [`roster_dates.txt`](#roster_datestxt), and [`employee_run_dates.txt`](#employee_run_datestxt). -If a Run ID is included but a Service ID isn't, then the roster position or employee is assigned to work on whichever run in [`run_events.txt`](#run_eventstxt) has that Run ID and is on a service that is active that service day, according to GTFS [`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)/[`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt). (If this would be ambiguous because there are mutliple `(service_id, run_id)` pairs it could refer to, then the Service ID is required.) +If a Run ID is included but a Service ID isn't, then the roster position or employee is assigned to work on whichever run in [`run_events.txt`](#run_eventstxt) has that Run ID and is on a service that is active that service day, according to GTFS [`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)/[`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt). (If this would be ambiguous because there are mutliple runs with the same `run_id` active on the same day, and therefore multiple `(service_id, run_id)` pairs it could refer to, then the Service ID is required.) This is allowed as a shortcut for producers to reduce the level of duplication in the roster file if a roster position works runs with the same ID on different days. For example, if there's a minor schedule change one day due to track work, that day must be on a different Service ID to give new trip and run times. But if a roster position works "Run 100" on both the regular and modified service, and the roster file leaves the Service ID field blank, then the roster file doesn't need an exception for that day because it refers to "Run 100" on whichever service is happening. From d68b2f010b841cefef377f270ac5b859c8848a51 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Wed, 12 Mar 2025 17:57:09 -0400 Subject: [PATCH 14/19] add links --- docs/spec/.pages | 1 + docs/spec/examples/rostering.md | 89 ++++++++++++++++----------------- docs/spec/index.md | 4 +- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/spec/.pages b/docs/spec/.pages index ac55641..283e6c4 100644 --- a/docs/spec/.pages +++ b/docs/spec/.pages @@ -2,3 +2,4 @@ nav: - Reference: index.md - Revision History: revision-history.md - Examples: examples.md + - Roster Examples: examples/rostering.md diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index a3dad9d..9ebb7d2 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -2,16 +2,15 @@ A series of examples about how to use [roster.txt](/docs/spec.md#rostertxt), [roster_dates.txt](/docs/spec.md#roster_datestxt), [employee_roster.txt](/docs/spec.md#employee_rostertxt), and [employee_run_dates.txt](/docs/spec.md#employee_run_datestxt) to assign employees to roster positions and runs. -TODO check that all links work. TODO check column ordering ## Simplest example: employee_run_dates.txt only. -The simplest way to assign employees to runs is to use `employee_run_dates.txt`. This will require one row per employee per date. +The simplest way to assign employees to runs is to use [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). This will require one row per employee per date. In this example, `A` and `B` work Saturday Feb 1 and Wednesday Feb 5 through Friday Feb 7. `C` and `D` work Sunday Feb 2 through Tuesday Feb 4. -**`calendar.txt`** +**[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** ``` service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date @@ -21,7 +20,7 @@ weekday,1,1,1,1,1,0,0,20250201,20250207 February 1, 2025 was a Saturday. -**`run_events.txt`** +**[`run_events.txt`](/docs/spec.md#run_eventstxt)** For this example, the purpose of this file is just to show which runs exist. Real runs would have more interesting data. @@ -33,7 +32,7 @@ weekday,103,1,work,station,09:00:00,station,17:00:00 weekday,104,1,work,station,09:00:00,station,17:00:00 ``` -**`employee_run_dates.txt`** +**[`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt)** ``` date,service_id,run_id,employee_id @@ -57,11 +56,11 @@ date,service_id,run_id,employee_id This example represents the same schedule of runs as above, but groups multiple days of work into roster positions that employees are assigned to all at once. Roster positions `A` and `B` work Saturdays, Wednesdays, Thursdays, and Fridays. Positions `C` and `D` work Sundays, Mondays, and Tuesdays. -`calendar.txt` and `run_events.txt` are the same as the previous example. +[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt) and [`run_events.txt`](/docs/spec.md#run_eventstxt) are the same as the previous example. -This example does not have any exceptions to the regular schedule, so doesn't need `roster_dates.txt` or `employee_run_dates.txt`. +This example does not have any exceptions to the regular schedule, so doesn't need [`roster_dates.txt`](/docs/spec.md#roster_datestxt) or [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). -**`roster_positions.txt`** +**[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** ``` roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id @@ -71,7 +70,7 @@ sun_to_tue_1,20250201,20250207,weekday,103,weekday,103,,,,,,,,,weekend,101 sun_to_tue_2,20250201,20250207,weekday,104,weekday,104,,,,,,,,,weekend,102 ``` -**`employee_roster.txt`** +**[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** ``` roster_position_id,start_date,end_date,employee_id @@ -260,13 +259,13 @@ def trips_for_run(service_id, run_id, service_date): ## Holiday -In this example, the roster includes holidays, which are defined as exceptions in `roster_dates.txt`. +In this example, the roster includes holidays, which are defined as exceptions in [`roster_dates.txt`](/docs/spec.md#roster_datestxt). This agency has two workers, who work M-F. There's no service on weekends. On each holiday, the agency runs half the service, and each worker gets one holiday off. -The holidays are built into the roster positions, so there's no need for `employee_run_dates.txt`. +The holidays are built into the roster positions, so there's no need for [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). -**`calendar.txt`** +**[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** ``` service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date @@ -275,7 +274,7 @@ weekday,1,1,1,1,1,0,0,20240701,20240714 July 1, 2024 was a Monday. -**`calendar_dates.txt`** +**[`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt)** ``` service_id,date,exception_type @@ -287,7 +286,7 @@ holiday,20240711,1 Holidays are Tuesday, July 2, and Thursday, July 11. -**`run_events.txt`** +**[`run_events.txt`](/docs/spec.md#run_eventstxt)** For this example, the purpose of this file is just to show which runs exist. Real runs would have more interesting data. @@ -298,7 +297,7 @@ weekday,102,1,work,station,09:00:00,station,17:00:00 holiday,999,1,work,station,09:00:00,station,17:00:00 ``` -**`roster_positions.txt`** +**[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** ``` roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id @@ -306,7 +305,7 @@ POSITION-A,20240701,20240714,weekday,101,weekday,101,weekday,101,weekday,101,wee POSITION-B,20240701,20240714,weekday,102,weekday,102,weekday,102,weekday,102,weekday,102,,,, ``` -**`roster_dates.txt`** +**[`roster_dates.txt`](/docs/spec.md#roster_datestxt)** ``` roster_position_id,date,exception_type,service_id,run_id @@ -318,7 +317,7 @@ POSITION-B,20240711,2,, POSITION-B,20240711,1,holiday,999 ``` -**`employee_roster.txt`** +**[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** ``` roster_position_id,start_date,end_date,employee_id @@ -332,9 +331,9 @@ In this example, this agency runs service 4 days a week, with two employees who These vacations are built in to the roster. -The third employee has no regular work, so appears in `roster_dates.txt` but not `roster_positions.txt`. +The third employee has no regular work, so appears in [`roster_dates.txt`](/docs/spec.md#roster_datestxt) but not [`roster_positions.txt`](/docs/spec.md#roster_positionstxt). -**`calendar.txt`** +**[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** ``` service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date @@ -343,7 +342,7 @@ weekday,1,1,0,1,1,0,0,20240701,20240721 July 1, 2024 was a Monday. -**`run_events.txt`** +**[`run_events.txt`](/docs/spec.md#run_eventstxt)** In this simple example, there's only one employee working per day, and they only do one run_event per day. @@ -352,7 +351,7 @@ service_id,run_id,event_sequence,event_type,start_location,start_time,end_locati weekday,100,1,work,station,09:00:00,station,17:00:00 ``` -**`roster_positions.txt`** +**[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** One works Monday and Tuesady, the other works Thursday and Friday. @@ -362,7 +361,7 @@ POSITION-A,20240701,20240721,weekday,100,weekday,100,,,,,,,,,, POSITION-B,20240701,20240721,,,,,,,weekday,100,weekday,100,,,, ``` -**`roster_dates.txt`** +**[`roster_dates.txt`](/docs/spec.md#roster_datestxt)** When each roster position goes on vacation for two days, the third subsitute roster position fills in. @@ -378,7 +377,7 @@ POSITION-B,20240719,2,, POSITION-C,20240719,1,weekday,100 ``` -**`employee_roster.txt`** +**[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** ``` roster_position_id,start_date,end_date,employee_id @@ -387,18 +386,18 @@ POSITION-B,20240701,20240721,EMPLOYEE-B POSITION-C,20240701,20240721,EMPLOYEE-C ``` -The vacations are built into the roster positions, so the employees stay assigned to the roster position the whole time. There's no need for `employee_run_dates.txt`. +The vacations are built into the roster positions, so the employees stay assigned to the roster position the whole time. There's no need for [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). ## Vacation (not part of the roster position) -In this case, the same employees work on the same days as in the previous example. But this time the roster position continue their regular assignments on the vacation day, and the employees get their vacations by being unassigned from their roster position in `employee_run_dates.txt`. +In this case, the same employees work on the same days as in the previous example. But this time the roster position continue their regular assignments on the vacation day, and the employees get their vacations by being unassigned from their roster position in [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). -`calendar.txt` and `run_events.txt` are the same as above. +[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt) and [`run_events.txt`](/docs/spec.md#run_eventstxt) are the same as above. -**`roster_positions.txt`** +**[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** -`roster_positions.txt` is the same as in the previous example, because the regular week is the same. +[`roster_positions.txt`](/docs/spec.md#roster_positionstxt) is the same as in the previous example, because the regular week is the same. One works Monday and Tuesady, the other works Thursday and Friday. @@ -408,9 +407,9 @@ POSITION-A,20240701,20240721,weekday,100,weekday,100,,,,,,,,,, POSITION-B,20240701,20240721,,,,,,,weekday,100,weekday,100,,,, ``` -In this example, there is no `roster_dates.txt` file, because the roster positions continue reguarly. There is no 3rd roster position. +In this example, there is no [`roster_dates.txt`](/docs/spec.md#roster_datestxt) file, because the roster positions continue reguarly. There is no 3rd roster position. -**`employee_roster.txt`** +**[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** ``` roster_position_id,start_date,end_date,employee_id @@ -420,7 +419,7 @@ POSITION-B,20240701,20240721,EMPLOYEE-B `EMPLOYEE-C` has no roster position that they are regularly assigned to. -**`employee_run_dates.txt`** +**[`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt)** POSITION-A,20240708,2,, POSITION-C,20240708,1,weekday,100 @@ -444,18 +443,18 @@ date,exception_type,service_id,run_id,employee_id ## Multi-week roster positions -At some agencies (especially in Europe), roster positions repeat every two weeks. In TODS, this can be represented by having the first and second weeks be two different roster positions, and using `employee_roster.txt` to assign employees to each roster position each week. +At some agencies (especially in Europe), roster positions repeat every two weeks. In TODS, this can be represented by having the first and second weeks be two different roster positions, and using [`employee_roster.txt`](/docs/spec.md#employee_rostertxt) to assign employees to each roster position each week. In this example, there's one run per day on weekdays only. Position A works Mondays and Tuesdays. Position B works Thursdays and Fridays. They trade off on Wednesdays, so each position works an average of 2½ days per week. -**`calendar.txt`** +**[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** ``` service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,1,1,1,0,0,20240701,20240728 ``` -**`roster_positions.txt`** +**[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** ``` @@ -466,7 +465,7 @@ POSITION-B-WEEK-1,20240701,20240728,,,,,,,weekday,100,weekday,100,,,, POSITION-B-WEEK-2,20240701,20240728,,,,,weekday,100,weekday,100,weekday,100,,,, ``` -**`employee_roster.txt`** +**[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** ``` roster_position_id,start_date,end_date,employee_id @@ -486,14 +485,14 @@ At some agencies (especially in the UK), employees rotate among all roster posit In this example, there are 5 employees and 5 roster positions. Over a calendar of 3 weeks, each employee will work 3 of the 5 positions. -**`calendar.txt`** +**[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** ``` service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,1,1,1,0,0,20240701,20240721 ``` -**`roster_positions.txt`** +**[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** ``` @@ -505,7 +504,7 @@ POSITION-4,20240701,20240721,weekday,104,weekday,104,weekday,104,weekday,104,wee POSITION-5,20240701,20240721,weekday,105,weekday,105,weekday,105,weekday,105,weekday,105,,,, ``` -**`employee_roster.txt`** +**[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** ``` roster_position_id,start_date,end_date,employee_id @@ -530,20 +529,20 @@ POSITION-5,20240715,20240721,EMPLOYEE-C In this example, due to track work, the schedule has been minorly changed one day. There are new trip times, trip IDs, run event times, and a new service ID. -If the roster files specify service IDs (which is recommended), then either `roster_dates.txt` or `employee_run_dates.txt` must be used to assign the roster position or employee to the new `(service_id, run_id)` pair, i.e. `(trackwork, 100)` instead of `(weekday, 100)`. +If the roster files specify service IDs (which is recommended), then either [`roster_dates.txt`](/docs/spec.md#roster_datestxt) or [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt) must be used to assign the roster position or employee to the new `(service_id, run_id)` pair, i.e. `(trackwork, 100)` instead of `(weekday, 100)`. -However, in this example, the roster does not specify the service ID in `roster_positions.txt`. Therefore, the employee is assigned to run 100 on whichever service is active. Since both the regular and replacement runs have run ID `100`, on the day of the exception, the employee will be implicitly assigned to `(trackwork, 100)` without needing to specify that in the roster files. +However, in this example, the roster does not specify the service ID in [`roster_positions.txt`](/docs/spec.md#roster_positionstxt). Therefore, the employee is assigned to run 100 on whichever service is active. Since both the regular and replacement runs have run ID `100`, on the day of the exception, the employee will be implicitly assigned to `(trackwork, 100)` without needing to specify that in the roster files. The spec describes this situation in [Service IDs in Rosters](/docs/spec/index.md#service-ids-in-rosters). -**`calendar.txt`** +**[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** ``` service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,1,1,1,0,0,20250201,20240728 ``` -**`calendar_dates.txt`** +**[`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt)** The trackwork is on Friday, February 2. @@ -553,7 +552,7 @@ weekday,20250207,2 trackwork,20250207,1 ``` -**`run_events.txt`** +**[`run_events.txt`](/docs/spec.md#run_eventstxt)** Because of the track work, travel is slower and the times are adjusted. @@ -565,7 +564,7 @@ trackwork,100,1,Operator,2001,suburb,08:00:00,downtown,09:30:00 trackwork,100,2,Operator,2002,downtown,16:30:00,suburb,18:00:00 ``` -**`roster_positions.txt`** +**[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** Note that the service ID fields are left blank. @@ -574,7 +573,7 @@ roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_s POSITION,20250201,20250228,,100,,100,,100,,100,,100,,,,, ``` -**`employee_roster.txt`** +**[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** ``` roster_position_id,start_date,end_date,employee_id diff --git a/docs/spec/index.md b/docs/spec/index.md index 8903adc..6df717b 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -196,7 +196,7 @@ Dates may be added before the `start_date` or after the `end_date` defined in [` After evaluating [`roster.txt`](#rostertxt) and `roster_dates.txt`, each run can only be assigned to one roster position on each date. A roster position may be scheduled to do multiple runs on the same date. -This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#TODO). +This file may be used even when [`roster.txt`](#rostertxt) is not defined, in which case each roster position is made up of the dates added in this file. This may be useful for agencies whose rosters are very irregular. In this case, the `exception_type` column can be omitted because every row is adding a date, which is the default when the column is blank. Primary Key: `*` @@ -237,6 +237,6 @@ Primary Key: `*` | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | | `employee_id` | ID | Required | References an agency's external systems. Employee IDs are not used elsewhere in TODS. | -If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#TODO). +If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#simplest-example-employee_run_datestxt-only). Each run can only be assigned to one employee on each date. Employees may be scheduled to more than one run on the same date. From 841eaf871eed891ce0a89bd5fc2bf614e5b7bda4 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Wed, 12 Mar 2025 18:07:50 -0400 Subject: [PATCH 15/19] adjust column order. proofread --- docs/spec/examples/rostering.md | 64 ++++++++++++++------------------- docs/spec/index.md | 2 +- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index 9ebb7d2..2c0e315 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -2,8 +2,6 @@ A series of examples about how to use [roster.txt](/docs/spec.md#rostertxt), [roster_dates.txt](/docs/spec.md#roster_datestxt), [employee_roster.txt](/docs/spec.md#employee_rostertxt), and [employee_run_dates.txt](/docs/spec.md#employee_run_datestxt) to assign employees to roster positions and runs. -TODO check column ordering - ## Simplest example: employee_run_dates.txt only. The simplest way to assign employees to runs is to use [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). This will require one row per employee per date. @@ -35,26 +33,26 @@ weekday,104,1,work,station,09:00:00,station,17:00:00 **[`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt)** ``` -date,service_id,run_id,employee_id -20250201,weekend,101,A -20250201,weekend,102,B -20250202,weekend,101,C -20250202,weekend,102,D -20250203,weekday,103,C -20250203,weekday,104,D -20250204,weekday,103,C -20250204,weekday,104,D -20250205,weekday,103,A -20250205,weekday,104,B -20250206,weekday,103,A -20250206,weekday,104,B -20250207,weekday,103,A -20250207,weekday,104,B +date,employee_id,service_id,run_id +20250201,A,weekend,101 +20250201,B,weekend,102 +20250202,C,weekend,101 +20250202,D,weekend,102 +20250203,C,weekday,103 +20250203,D,weekday,104 +20250204,C,weekday,103 +20250204,D,weekday,104 +20250205,A,weekday,103 +20250205,B,weekday,104 +20250206,A,weekday,103 +20250206,B,weekday,104 +20250207,A,weekday,103 +20250207,B,weekday,104 ``` ## Example with Rosters -This example represents the same schedule of runs as above, but groups multiple days of work into roster positions that employees are assigned to all at once. Roster positions `A` and `B` work Saturdays, Wednesdays, Thursdays, and Fridays. Positions `C` and `D` work Sundays, Mondays, and Tuesdays. +This example represents the same schedule of runs as above, but it groups multiple days of work into roster positions that employees are assigned to all at once. Roster positions `A` and `B` work Saturdays, Wednesdays, Thursdays, and Fridays. Positions `C` and `D` work Sundays, Mondays, and Tuesdays. [`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt) and [`run_events.txt`](/docs/spec.md#run_eventstxt) are the same as the previous example. @@ -86,7 +84,7 @@ Here are example algorithms (written in pseudocode) for a couple typical lookups This algorithm will cover edge cases and work for both simple and complex cases. If you know you have simpler data, you may be able to use simpler rules. -### Given a date and trip ID, look up which employees are working on that trip +### Given a trip ID and date, look up which employees are working on that trip ``` # Returns a list of employee IDs @@ -162,7 +160,7 @@ def employees_on_run(service_id, run_id, roster_positions_on_run, service_date): return result ``` -### Given an date and employee ID, look up which trips they're working on that day +### Given an employee ID and date, look up which trips they're working on that day ``` # Returns a list of trip IDs @@ -421,24 +419,16 @@ POSITION-B,20240701,20240721,EMPLOYEE-B **[`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt)** -POSITION-A,20240708,2,, -POSITION-C,20240708,1,weekday,100 -POSITION-A,20240709,2,, -POSITION-C,20240709,1,weekday,100 -POSITION-B,20240718,2,, -POSITION-C,20240718,1,weekday,100 -POSITION-B,20240719,2,, -POSITION-C,20240719,1,weekday,100 ``` -date,exception_type,service_id,run_id,employee_id -20240708,2,,EMPLOYEE-A -20240708,1,weekday,100,EMPLOYEE-C -20240709,2,,EMPLOYEE-A -20240709,1,weekday,100,EMPLOYEE-C -20240718,2,,EMPLOYEE-B -20240718,1,weekday,100,EMPLOYEE-C -20240719,2,,EMPLOYEE-B -20240719,1,weekday,100,EMPLOYEE-C +date,employee_id,exception_type,service_id,run_id +20240708,EMPLOYEE-A,2, +20240708,EMPLOYEE-C,1,weekday,100 +20240709,EMPLOYEE-A,2, +20240709,EMPLOYEE-C,1,weekday,100 +20240718,EMPLOYEE-B,2, +20240718,EMPLOYEE-C,1,weekday,100 +20240719,EMPLOYEE-B,2, +20240719,EMPLOYEE-C,1,weekday,100 ``` ## Multi-week roster positions diff --git a/docs/spec/index.md b/docs/spec/index.md index 6df717b..1b7102a 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -232,10 +232,10 @@ Primary Key: `*` | **Field Name** | **Type** | **Required** | **Description** | | --- | --- | --- | --- | | `date` | Date | Required | | +| `employee_id` | ID | Required | References an agency's external systems. Employee IDs are not used elsewhere in TODS. | | `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | | `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | | `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | -| `employee_id` | ID | Required | References an agency's external systems. Employee IDs are not used elsewhere in TODS. | If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#simplest-example-employee_run_datestxt-only). From df8466f0c560b62101d7ac434fe45512d93b1781 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Wed, 12 Mar 2025 19:54:48 -0400 Subject: [PATCH 16/19] move employee_run_dates to top of roster. forbid run if exception_type=2 --- docs/spec/index.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index 1b7102a..17a11da 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -146,6 +146,24 @@ Because some events may overlap in time, it may not be possible to choose a sing - `start_time` may equal `end_time` for an event that's a single point in time (such as a report time) without any duration. - Recommended sort order: `service_id`, `run_id`, `event_sequence`. +### `employee_run_dates.txt` + +Describes which employees are scheduled to which runs on which dates. + +If [`employee_roster.txt`](#employee_rostertxt) is used, then describes exceptions to that schedule. + +If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#simplest-example-employee_run_datestxt-only). + +Primary Key: `*` + +| **Field Name** | **Type** | **Required** | **Description** | +| --- | --- | --- | --- | +| `date` | Date | Required | | +| `employee_id` | ID | Required | References an agency's external systems. Employee IDs are not used elsewhere in TODS. | +| `exception_type` | Enum | Optional | `1` or blank - The run is assigned to this employee on the specified date.
`2` - On this date, the employee will not work on runs assigned via the `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. The employee may still have runs assigned via other rows in this file. | +| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`.

If `exception_type` is `1` or blank, then `service_id` is optional and recommended. It's required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters).
If `exception_type` is `2`, then `service_id` is forbidden. | +| `run_id` | ID referencing `run_events.txt` | Conditionally Required | If `exception_type` is `1`, then `run_id` is required and is the run that's added to this employee's schedule.
If `exception_type` is `2`, then `run_id` is forbidden. | + ### `roster.txt` This file defines roster positions, groupings of work across multiple runs on multiple dates that an employee can be assigned to all at once. @@ -205,8 +223,8 @@ Primary Key: `*` | `roster_position_id` | ID referencing [`roster.txt`](#rostertxt) or ID | Required | If `exception_type` is `1`, then the ID does not have to appear in [`roster.txt`](#rostertxt). This file may define new roster positions. | | `date` | Date | Required | Date when exception occurs. | | `exception_type` | Enum | Optional | `1` (or blank) - The run is added to this roster for the specified date.
`2` - The roster will not work its regular run on this date. | -| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | -| `run_id` | ID referencing `run_events.txt` | Conditionally Required | The run that's either added or removed from this roster. Required if `exception_type` is `1`. Optional if `exception_type` is `2`. If `exception_type` is `2` and `run_id` is not blank, then it must match the Run ID that the roster was scheduled to do on this date according to [`roster.txt`](#rostertxt). | +| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`.

If `exception_type` is `1` or blank, then `service_id` is optional and recommended. It's required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters).
If `exception_type` is `2`, then `service_id` is forbidden. | +| `run_id` | ID referencing `run_events.txt` | Conditionally Required | If `exception_type` is `1`, then `run_id` is required and is the run that's added to this roster position.
If `exception_type` is `2`, then `run_id` is forbidden. | ### `employee_roster.txt` @@ -222,21 +240,3 @@ Primary Key: `(roster_position_id, start_date)` | `employee_id` | ID | Required | | Each roster position can only be assigned to one employee on each date. Employees may be scheduled to more than one roster position on the same date. - -### `employee_run_dates.txt` - -Describes which employees are scheduled to which runs on which dates. If [`employee_roster.txt`](#employee_rostertxt) is used, then describes exceptions to that schedule. - -Primary Key: `*` - -| **Field Name** | **Type** | **Required** | **Description** | -| --- | --- | --- | --- | -| `date` | Date | Required | | -| `employee_id` | ID | Required | References an agency's external systems. Employee IDs are not used elsewhere in TODS. | -| `exception_type` | Enum | Optional | `1` (or blank) - The run is assigned to this employee on the specified date.
`2` - The employee will not work this run on this date. | -| `service_id` | ID referencing `run_events.txt` | Conditionally Required | Part of the Run ID, which is refered to as `(service_id, run_id)`. Optional and recommended. Required in some cases to avoid ambiguity. See [Service IDs in Rosters](#service-ids-in-rosters). | -| `run_id` | ID referencing `run_events.txt` | Required | The run that's either added or removed from this employee's schedule. If `exception_type` is `2` and `run_id` is not blank, then it must match a Run ID that the employee was scheduled to do on this date according to `employee_roster.txt`, `roster.txt` and `roster_dates.txt`. | - -If a feed doesn't represent roster positions, it can still assign employees to runs by putting every run for every date in this file. In that case, the `exception_type` column can be omitted because every row would be adding a date, which is the default when the column is blank. [Example](./examples/rostering.md#simplest-example-employee_run_datestxt-only). - -Each run can only be assigned to one employee on each date. Employees may be scheduled to more than one run on the same date. From 6b533c2f7b8f037355c66fe100868539cf71268f Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Wed, 12 Mar 2025 20:06:38 -0400 Subject: [PATCH 17/19] lint --- docs/spec/examples/rostering.md | 69 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index 2c0e315..73285f9 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -2,7 +2,7 @@ A series of examples about how to use [roster.txt](/docs/spec.md#rostertxt), [roster_dates.txt](/docs/spec.md#roster_datestxt), [employee_roster.txt](/docs/spec.md#employee_rostertxt), and [employee_run_dates.txt](/docs/spec.md#employee_run_datestxt) to assign employees to roster positions and runs. -## Simplest example: employee_run_dates.txt only. +## Simplest example: employee_run_dates.txt only The simplest way to assign employees to runs is to use [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). This will require one row per employee per date. @@ -10,7 +10,7 @@ In this example, `A` and `B` work Saturday Feb 1 and Wednesday Feb 5 through Fri **[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** -``` +```csv service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekend,0,0,0,0,0,1,1,20250201,20250207 weekday,1,1,1,1,1,0,0,20250201,20250207 @@ -22,7 +22,7 @@ February 1, 2025 was a Saturday. For this example, the purpose of this file is just to show which runs exist. Real runs would have more interesting data. -``` +```csv service_id,run_id,event_sequence,event_type,start_location,start_time,end_location,end_time weekend,101,1,work,station,09:00:00,station,17:00:00 weekend,102,1,work,station,09:00:00,station,17:00:00 @@ -32,7 +32,7 @@ weekday,104,1,work,station,09:00:00,station,17:00:00 **[`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt)** -``` +```csv date,employee_id,service_id,run_id 20250201,A,weekend,101 20250201,B,weekend,102 @@ -60,7 +60,7 @@ This example does not have any exceptions to the regular schedule, so doesn't ne **[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** -``` +```csv roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id wed_to_sat_1,20250201,20250207,,,,,weekday,103,weekday,103,weekday,103,weekend,101,, wed_to_sat_2,20250201,20250207,,,,,weekday,104,weekday,104,weekday,104,weekend,102,, @@ -70,7 +70,7 @@ sun_to_tue_2,20250201,20250207,weekday,104,weekday,104,,,,,,,,,weekend,102 **[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** -``` +```csv roster_position_id,start_date,end_date,employee_id wed_to_sat_1,20250201,20250207,A wed_to_sat_2,20250201,20250207,B @@ -86,7 +86,7 @@ This algorithm will cover edge cases and work for both simple and complex cases. ### Given a trip ID and date, look up which employees are working on that trip -``` +```ruby # Returns a list of employee IDs def employees_on_trip(service_date, trip_id): # List of (service_id, run_id). There may be 0 (run_events.txt is incomplete), 1, or many (if multiple people work on that trip). @@ -162,7 +162,7 @@ def employees_on_run(service_id, run_id, roster_positions_on_run, service_date): ### Given an employee ID and date, look up which trips they're working on that day -``` +```ruby # Returns a list of trip IDs def trips_for_employee(employee_id, service_date): # List of roster postions that the employee is doing on this date @@ -265,7 +265,7 @@ The holidays are built into the roster positions, so there's no need for [`emplo **[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** -``` +```csv service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,1,1,1,0,0,20240701,20240714 ``` @@ -274,7 +274,7 @@ July 1, 2024 was a Monday. **[`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt)** -``` +```csv service_id,date,exception_type weekday,20240702,2 holiday,20240702,1 @@ -288,7 +288,7 @@ Holidays are Tuesday, July 2, and Thursday, July 11. For this example, the purpose of this file is just to show which runs exist. Real runs would have more interesting data. -``` +```csv service_id,run_id,event_sequence,event_type,start_location,start_time,end_location,end_time weekday,101,1,work,station,09:00:00,station,17:00:00 weekday,102,1,work,station,09:00:00,station,17:00:00 @@ -297,7 +297,7 @@ holiday,999,1,work,station,09:00:00,station,17:00:00 **[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** -``` +```csv roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id POSITION-A,20240701,20240714,weekday,101,weekday,101,weekday,101,weekday,101,weekday,101,,,, POSITION-B,20240701,20240714,weekday,102,weekday,102,weekday,102,weekday,102,weekday,102,,,, @@ -305,7 +305,7 @@ POSITION-B,20240701,20240714,weekday,102,weekday,102,weekday,102,weekday,102,wee **[`roster_dates.txt`](/docs/spec.md#roster_datestxt)** -``` +```csv roster_position_id,date,exception_type,service_id,run_id POSITION-A,20240702,2,, POSITION-B,20240702,2,, @@ -317,7 +317,7 @@ POSITION-B,20240711,1,holiday,999 **[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** -``` +```csv roster_position_id,start_date,end_date,employee_id POSITION-A,20240701,20240714,EMPLOYEE-A POSITION-B,20240701,20240714,EMPLOYEE-B @@ -333,7 +333,7 @@ The third employee has no regular work, so appears in [`roster_dates.txt`](/docs **[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** -``` +```csv service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,0,1,1,0,0,20240701,20240721 ``` @@ -344,7 +344,7 @@ July 1, 2024 was a Monday. In this simple example, there's only one employee working per day, and they only do one run_event per day. -``` +```csv service_id,run_id,event_sequence,event_type,start_location,start_time,end_location,end_time weekday,100,1,work,station,09:00:00,station,17:00:00 ``` @@ -353,7 +353,7 @@ weekday,100,1,work,station,09:00:00,station,17:00:00 One works Monday and Tuesady, the other works Thursday and Friday. -``` +```csv roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id POSITION-A,20240701,20240721,weekday,100,weekday,100,,,,,,,,,, POSITION-B,20240701,20240721,,,,,,,weekday,100,weekday,100,,,, @@ -363,7 +363,7 @@ POSITION-B,20240701,20240721,,,,,,,weekday,100,weekday,100,,,, When each roster position goes on vacation for two days, the third subsitute roster position fills in. -``` +```csv roster_position_id,date,exception_type,service_id,run_id POSITION-A,20240708,2,, POSITION-C,20240708,1,weekday,100 @@ -377,7 +377,7 @@ POSITION-C,20240719,1,weekday,100 **[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** -``` +```csv roster_position_id,start_date,end_date,employee_id POSITION-A,20240701,20240721,EMPLOYEE-A POSITION-B,20240701,20240721,EMPLOYEE-B @@ -386,7 +386,6 @@ POSITION-C,20240701,20240721,EMPLOYEE-C The vacations are built into the roster positions, so the employees stay assigned to the roster position the whole time. There's no need for [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). - ## Vacation (not part of the roster position) In this case, the same employees work on the same days as in the previous example. But this time the roster position continue their regular assignments on the vacation day, and the employees get their vacations by being unassigned from their roster position in [`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt). @@ -399,7 +398,7 @@ In this case, the same employees work on the same days as in the previous exampl One works Monday and Tuesady, the other works Thursday and Friday. -``` +```csv roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id POSITION-A,20240701,20240721,weekday,100,weekday,100,,,,,,,,,, POSITION-B,20240701,20240721,,,,,,,weekday,100,weekday,100,,,, @@ -409,7 +408,7 @@ In this example, there is no [`roster_dates.txt`](/docs/spec.md#roster_datestxt) **[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** -``` +```csv roster_position_id,start_date,end_date,employee_id POSITION-A,20240701,20240721,EMPLOYEE-A POSITION-B,20240701,20240721,EMPLOYEE-B @@ -419,7 +418,7 @@ POSITION-B,20240701,20240721,EMPLOYEE-B **[`employee_run_dates.txt`](/docs/spec.md#employee_run_datestxt)** -``` +```csv date,employee_id,exception_type,service_id,run_id 20240708,EMPLOYEE-A,2, 20240708,EMPLOYEE-C,1,weekday,100 @@ -439,15 +438,14 @@ In this example, there's one run per day on weekdays only. Position A works Mond **[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** -``` +```csv service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,1,1,1,0,0,20240701,20240728 ``` **[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** - -``` +```csv roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id POSITION-A-WEEK-1,20240701,20240728,weekday,100,weekday,100,weekday,100,,,,,,,, POSITION-A-WEEK-2,20240701,20240728,weekday,100,weekday,100,,,,,,,,,, @@ -457,7 +455,7 @@ POSITION-B-WEEK-2,20240701,20240728,,,,,weekday,100,weekday,100,weekday,100,,,, **[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** -``` +```csv roster_position_id,start_date,end_date,employee_id POSITION-A-WEEK1,20240701,20240707,EMPLOYEE-A POSITION-B-WEEK1,20240701,20240707,EMPLOYEE-B @@ -477,15 +475,14 @@ In this example, there are 5 employees and 5 roster positions. Over a calendar o **[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** -``` +```csv service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,1,1,1,0,0,20240701,20240721 ``` **[`roster_positions.txt`](/docs/spec.md#roster_positionstxt)** - -``` +```csv roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id POSITION-1,20240701,20240721,weekday,101,weekday,101,weekday,101,weekday,101,weekday,101,,,, POSITION-2,20240701,20240721,weekday,102,weekday,102,weekday,102,weekday,102,weekday,102,,,, @@ -496,7 +493,7 @@ POSITION-5,20240701,20240721,weekday,105,weekday,105,weekday,105,weekday,105,wee **[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** -``` +```csv roster_position_id,start_date,end_date,employee_id POSITION-1,20240701,20240707,EMPLOYEE-A POSITION-2,20240701,20240707,EMPLOYEE-B @@ -527,7 +524,7 @@ The spec describes this situation in [Service IDs in Rosters](/docs/spec/index.m **[`calendar.txt`](https://gtfs.org/documentation/schedule/reference/#calendartxt)** -``` +```csv service_id,monday,tuesday,wednesday,thursday,friday,saturday,start_date,end_date weekday,1,1,1,1,1,0,0,20250201,20240728 ``` @@ -536,7 +533,7 @@ weekday,1,1,1,1,1,0,0,20250201,20240728 The trackwork is on Friday, February 2. -``` +```csv service_id,date,exception_type weekday,20250207,2 trackwork,20250207,1 @@ -546,7 +543,7 @@ trackwork,20250207,1 Because of the track work, travel is slower and the times are adjusted. -``` +```csv service_id,run_id,event_sequence,event_type,trip_id,start_location,start_time,end_location,end_time weekday,100,1,Operator,1001,suburb,08:00:00,downtown,09:00:00 weekday,100,2,Operator,1002,downtown,17:00:00,suburb,18:00:00 @@ -558,14 +555,14 @@ trackwork,100,2,Operator,2002,downtown,16:30:00,suburb,18:00:00 Note that the service ID fields are left blank. -``` +```csv roster_position_id,start_date,end_date,monday_service_id,monday_run_id,tuesday_service_id,tuesday_run_id,wednesday_service_id,wednesday_run_id,thursday_service_id,thursday_run_id,friday_service_id,friday_run_id,saturday_service_id,saturday_run_id,sunday_service_id,sunday_run_id POSITION,20250201,20250228,,100,,100,,100,,100,,100,,,,, ``` **[`employee_roster.txt`](/docs/spec.md#employee_rostertxt)** -``` +```csv roster_position_id,start_date,end_date,employee_id POSITION,20250201,20250228,EMPLOYEE ``` From 6097800e4fe8ab896655e7073de027e4e0758e7c Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Wed, 12 Mar 2025 20:49:04 -0400 Subject: [PATCH 18/19] typo --- docs/spec/examples/rostering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/examples/rostering.md b/docs/spec/examples/rostering.md index 73285f9..a0a30b9 100644 --- a/docs/spec/examples/rostering.md +++ b/docs/spec/examples/rostering.md @@ -531,7 +531,7 @@ weekday,1,1,1,1,1,0,0,20250201,20240728 **[`calendar_dates.txt`](https://gtfs.org/documentation/schedule/reference/#calendar_datestxt)** -The trackwork is on Friday, February 2. +The trackwork is on Friday, February 7. ```csv service_id,date,exception_type From 80b71e312b9ffdbab1420bd42197cd1bb54187a8 Mon Sep 17 00:00:00 2001 From: Sky Rose Date: Wed, 12 Mar 2025 22:09:02 -0400 Subject: [PATCH 19/19] fix file list --- docs/spec/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/index.md b/docs/spec/index.md index 17a11da..a12bc94 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -23,10 +23,10 @@ All files are optional. | stop_times_supplement.txt | Supplement | Supplements and modifies GTFS [stop_times.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#stop_timestxt) with non-public times at which trips stop at locations, `stop_times` entries for non-public trips, and related information. | | routes_supplement.txt | Supplement | Supplements and modifies GTFS [routes.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#routestxt) with internal route identifiers and other non-public route identification. | | run_events.txt | TODS-Specific | Lists all trips and other scheduled activities to be performed by a member of personnel during a run. | +| employee_run_dates.txt | TODS-Specific | Assigns employees to runs. Or, if rosters are used, gives exceptions to roster assignments on specific dates. | | roster.txt | TODS-Specific | Lists the runs that a roster position is assigned to work on a typical week. | | roster_dates.txt | TODS-Specific | Lists the runs that a roster position is assigned to work on specific dates. | | employee_roster.txt | TODS-Specific | Lists which employee is assigned to which roster position. | -| employee_run_dates.txt | TODS-Specific | Exceptions to roster assignments. Assigns employees directly to runs on specific dates. | _The use of the Supplement standard to modify other GTFS files is not yet formally adopted into the specification and remains subject to change. Other files may be formally adopted in the future._