Skip to content

Commit 04c14ca

Browse files
authored
Rename project, update documentation for open source, bump version (#45)
* Update docuementation for open source, bump version * Rename GTFS-flex-to-GOFS-lite to GTFS-flex-to-GOFS * Revert "Rename GTFS-flex-to-GOFS-lite to GTFS-flex-to-GOFS" This reverts commit bd5a92d. * Rename project to GTFS-flex-to-GOFS
1 parent 49d4081 commit 04c14ca

30 files changed

+155
-64
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
.vscode/
88
tests/.work/
99
build/
10-
gtfs_flex_to_gofs_lite/output
10+
gtfs_flex_to_gofs/output
1111
dist/
1212
.claude/

CLAUDE.md

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,51 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
This is a Python tool that converts GTFS-Flex (General Transit Feed Specification - Flexible services) data to the GOFS-lite (General On-demand Feed Specification - lite) format. It processes on-demand/flexible transit services data and outputs standardized GOFS files.
7+
This is a Python tool that converts GTFS-Flex (General Transit Feed Specification - Flexible services) data to the GOFS (General On-demand Feed Specification) format. It processes on-demand/flexible transit services data and outputs standardized GOFS files.
88

99
## Core Architecture
1010

1111
The conversion process follows this flow:
12-
1. Load GTFS data using `gtfs_loader`
12+
1. Load GTFS data using `gtfs_loader`
1313
2. Extract relevant data through `operation_rules.py` which analyzes trip types and creates `GofsData`
14-
3. Generate individual GOFS files via modules in `gtfs_flex_to_gofs_lite/files/`
15-
4. Output structured GOFS-lite format files
14+
3. Generate individual GOFS files via modules in `gtfs_flex_to_gofs/files/`
15+
4. Output structured GOFS format files
1616

1717
### Key Components
1818

19-
- **`gofs_lite_converter.py`**: Main conversion orchestrator that coordinates all file generation
19+
- **`gofs_converter.py`**: Main conversion orchestrator that coordinates all file generation
2020
- **`gofs_data.py`**: Contains `GofsData` class that tracks extracted IDs and transfers from GTFS-Flex
2121
- **`files/operation_rules.py`**: Core logic for classifying trip types (regular_service, deviated_service, pure_microtransit, other) and extracting zone-to-zone transfers
2222
- **`files/` directory**: Individual modules for generating each GOFS file type (zones, calendars, fares, etc.)
2323
- **`gofs_file.py`**: Base class for GOFS file objects with save functionality
2424

2525
### Trip Classification Logic
2626

27-
The system classifies GTFS trips into four types:
28-
- `PURE_MICROTRANSIT`: Flexible zone-to-zone services
29-
- `DEVIATED_SERVICE`: Routes with some flexible elements
30-
- `REGULAR_SERVICE`: Fixed route services
31-
- `OTHER`: Unclassified trips
27+
The system classifies GTFS trips into four types based on stop time patterns in `operation_rules.py:get_type_of_trip()`:
28+
- `PURE_MICROTRANSIT`: All stop times are flexible zones (have pickup/dropoff windows)
29+
- `DEVIATED_SERVICE`: Mix of regular stops and flexible zones (3+ stop times required)
30+
- `REGULAR_SERVICE`: All stop times are regular fixed stops
31+
- `OTHER`: Invalid or unclassifiable trips (e.g., <2 stops)
3232

3333
Only `PURE_MICROTRANSIT` trips are converted to GOFS format.
3434

35+
### Conversion Workflow
36+
37+
1. **Trip Analysis** (`operation_rules.py:create()`):
38+
- Iterates through all GTFS trips and their stop times
39+
- Classifies each trip using `get_type_of_trip()`
40+
- For `PURE_MICROTRANSIT` trips, extracts zone-to-zone transfers
41+
- Populates `GofsData` with extracted IDs (routes, calendars, zones, booking rules)
42+
43+
2. **File Generation** (`gofs_converter.py:convert_to_gofs()`):
44+
- Creates individual GOFS files using `GofsData` to filter relevant GTFS entities
45+
- Files generated in order: operation_rules, zones, system_information, service_brands, vehicle_types, calendars, fares, wait_times, wait_time, booking_rules, gofs_versions, gofs
46+
- Each file module has a `create()` function that returns a `GofsFile` object
47+
48+
3. **Output** (`gofs_converter.py:save_files()`):
49+
- Single folder mode: All GOFS files in one directory
50+
- Split-by-route mode: Creates one folder per route_id, with filtered operation_rules per folder
51+
3552
## Development Commands
3653

3754
### Installation
@@ -41,12 +58,20 @@ uv sync --extra dev
4158

4259
### Running Tests
4360
```bash
44-
python -m pytest .
61+
uv run python -m pytest . # All tests
62+
uv run python -m pytest tests/ -v # Verbose output
4563
```
4664

4765
### Running the Tool
4866
```bash
49-
python -m gtfs_flex_to_gofs_lite --gtfs-dir <input_dir> --gofs-lite-dir <output_dir> --url <base_url>
67+
python -m gtfs_flex_to_gofs --gtfs-dir <input_dir> --gofs-dir <output_dir> --url <base_url>
68+
69+
# With optional parameters:
70+
--ttl <seconds> # Time-to-live for GOFS files (default: 86400)
71+
--timestamp <unix_time> # Fixed timestamp for deterministic output
72+
--split-by-route # Create separate GOFS folders per route
73+
--out-gtfs-dir <dir> # Output directory for patched GTFS
74+
--no-warning # Silence warnings
5075
```
5176

5277
### Test Regeneration
@@ -63,15 +88,33 @@ python -m pytest tests/test_runner.py::test_default[test_simple_conversion]
6388

6489
Replace `test_simple_conversion` with any test case name from the `tests/` directory.
6590

91+
### Linting
92+
```bash
93+
uv run flake8
94+
```
95+
96+
### Deployment
97+
98+
Deployment to PyPI is automated via GitHub Actions when the version in `pyproject.toml` is updated on the `main` branch. See `.github/workflows/release.yml` for details.
99+
66100
## Dependencies
67101

68102
- `py-gtfs-loader`: Custom GTFS loading library from TransitApp
69103
- `shapely>=2.0.4`: Geometric operations
70104
- `pytest`: Testing framework
71105

72-
## File Structure Notes
106+
## Testing Structure
107+
108+
Test cases use a snapshot-based approach:
109+
- Each test case lives in `tests/test_*/` directory
110+
- `input/`: Contains GTFS files (including required `locations.geojson`)
111+
- `expected_default/`: Contains expected GOFS output files
112+
- `test_runner.py` uses pytest parametrization to automatically discover all test cases
113+
- Tests compare generated output against expected files using `gtfs_loader.test_support`
114+
- `createTests.sh` regenerates all expected output files (run after intentional changes)
115+
116+
### Important Testing Notes
73117

74-
- Test cases are in `tests/test_*/` with `input/` and `expected_default/` subdirectories
118+
- Tests use `timestamp=0` for deterministic output
75119
- The `locations.geojson` file in GTFS input is required for conversion - without it, conversion is skipped
76-
- GOFS files are generated with specific headers including TTL, version, and timestamps
77-
- Split-by-route functionality creates separate GOFS folders per route when enabled
120+
- Test isolation: each test gets its own work directory created by `test_support.create_test_data()`

README.md

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,79 @@
1-
Tool to convert GTFS-Flex data to the GOFS-lite format
1+
# GTFS-flex-to-GOFS
22

3-
To install:
4-
* `uv sync --extra dev`
3+
[![Build Status](https://github.com/TransitApp/GTFS-flex-to-GOFS/actions/workflows/pull-request.yml/badge.svg)](https://github.com/TransitApp/GTFS-flex-to-GOFS/actions)
4+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5+
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
56

6-
To run test:
7-
* `uv run python -m pytest .`
7+
A Python tool to convert [GTFS-Flex](https://github.com/MobilityData/gtfs-flex) (General Transit Feed Specification - Flexible services) data to the [GOFS](https://gofs.org) (General On-demand Feed Specification) format.
88

9-
You can use `createTests.sh` to regenerate the test.
9+
This tool processes on-demand and flexible transit services data from GTFS-Flex feeds and outputs standardized GOFS files for consumption by trip planning applications and mobility platforms.
1010

11-
To deploy a new version, run:
11+
## Installation
12+
13+
Install from PyPI:
14+
```bash
15+
pip install GTFS-flex-to-GOFS
1216
```
13-
rm -r dist/
14-
uv build
15-
TWINE_USERNAME=transit TWINE_REPOSITORY_URL=https://pypi.transitapp.com:443 TWINE_PASSWORD=[PASSWORD] twine upload dist/*
17+
18+
Or install from source:
19+
```bash
20+
git clone https://github.com/TransitApp/GTFS-flex-to-GOFS.git
21+
cd GTFS-flex-to-GOFS
22+
uv sync --extra dev
1623
```
1724

18-
The twine password can be found in 1Password under the same `PyPI password`.
25+
## Usage
1926

20-
### man
27+
```bash
28+
gtfs-flex-to-gofs --gtfs-dir <input_dir> --gofs-dir <output_dir> --url <base_url>
2129
```
22-
Convert GTFS-flex on-demand format to GOFS-lite
2330

31+
### Command Line Options
32+
33+
```
2434
optional arguments:
2535
-h, --help show this help message and exit
2636
--gtfs-dir Dir input gtfs directory
27-
--gofs-lite-dir Dir output gofs directory
37+
--gofs-dir Dir output gofs directory
2838
--url URL auto-discovery url. Base URL indicate for where each files will be uploaded (and downloadable)
2939
--ttl TTL time to live of the generated gofs files in seconds (default: 86400)
3040
--no-warning Silence warnings
3141
```
42+
43+
## Development
44+
45+
### Running Tests
46+
47+
```bash
48+
uv run python -m pytest .
49+
# or for verbose output:
50+
uv run python -m pytest tests/ -v
51+
```
52+
53+
### Regenerating Test Fixtures
54+
55+
```bash
56+
./createTests.sh
57+
```
58+
59+
## Features
60+
61+
- Converts GTFS-Flex pure microtransit services to GOFS format
62+
- Supports zone-based on-demand transit operations
63+
- Generates all required GOFS files (zones, calendars, fares, booking rules, etc.)
64+
- Optional split-by-route output for multi-service feeds
65+
- Automated CI/CD with GitHub Actions
66+
67+
## Contributing
68+
69+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
70+
71+
## License
72+
73+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
74+
75+
## Related Projects
76+
77+
- [GTFS-Flex Specification](https://github.com/MobilityData/gtfs-flex)
78+
- [GOFS Specification](https://gofs.org)
79+
- [py-gtfs-loader](https://github.com/TransitApp/py-gtfs-loader)

createTests.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
python3 -m gtfs_flex_to_gofs_lite --gtfs-dir ./tests/test_simple_conversion/input --gofs-lite-dir ./tests/test_simple_conversion/expected_default --url "" --timestamp 0
2-
python3 -m gtfs_flex_to_gofs_lite --gtfs-dir ./tests/test_non_pure_microtransit_route/input --gofs-lite-dir ./tests/test_non_pure_microtransit_route/expected_default --url "" --timestamp 0
3-
python3 -m gtfs_flex_to_gofs_lite --gtfs-dir ./tests/test_ondemand_stops/input --gofs-lite-dir ./tests/test_ondemand_stops/expected_default --url "" --timestamp 0
4-
python3 -m gtfs_flex_to_gofs_lite --gtfs-dir ./tests/test_added_calendar_dates/input --gofs-lite-dir ./tests/test_added_calendar_dates/expected_default --url "" --timestamp 0
5-
python3 -m gtfs_flex_to_gofs_lite --gtfs-dir ./tests/test_removed_calendar_dates/input --gofs-lite-dir ./tests/test_removed_calendar_dates/expected_default --url "" --timestamp 0
1+
uv run python -m gtfs_flex_to_gofs --gtfs-dir ./tests/test_simple_conversion/input --gofs-dir ./tests/test_simple_conversion/expected_default --url "" --timestamp 0
2+
uv run python -m gtfs_flex_to_gofs --gtfs-dir ./tests/test_non_pure_microtransit_route/input --gofs-dir ./tests/test_non_pure_microtransit_route/expected_default --url "" --timestamp 0
3+
uv run python -m gtfs_flex_to_gofs --gtfs-dir ./tests/test_ondemand_stops/input --gofs-dir ./tests/test_ondemand_stops/expected_default --url "" --timestamp 0
4+
uv run python -m gtfs_flex_to_gofs --gtfs-dir ./tests/test_added_calendar_dates/input --gofs-dir ./tests/test_added_calendar_dates/expected_default --url "" --timestamp 0
5+
uv run python -m gtfs_flex_to_gofs --gtfs-dir ./tests/test_removed_calendar_dates/input --gofs-dir ./tests/test_removed_calendar_dates/expected_default --url "" --timestamp 0
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import gtfs_loader
33
from pathlib import Path
44

5-
from .gofs_lite_converter import convert_to_gofs_lite
5+
from .gofs_converter import convert_to_gofs
66
from .patch_gtfs import patch_gtfs
77
from .utils import yellow_text
88

@@ -11,11 +11,11 @@
1111

1212
def main(args):
1313
gtfs = gtfs_loader.load(args.gtfs_dir, itineraries=args.itineraries)
14-
gofs_lite_dir = Path(args.gofs_lite_dir)
14+
gofs_dir = Path(args.gofs_dir)
1515

16-
gofs_lite_dir.mkdir(parents=True, exist_ok=True)
16+
gofs_dir.mkdir(parents=True, exist_ok=True)
1717

18-
convert_to_gofs_lite(gtfs, gofs_lite_dir, args.ttl, args.url, args.split_by_route, args.timestamp, itineraries=args.itineraries)
18+
convert_to_gofs(gtfs, gofs_dir, args.ttl, args.url, args.split_by_route, args.timestamp, itineraries=args.itineraries)
1919

2020
if args.out_gtfs_dir:
2121
patch_gtfs(args, gtfs, itineraries=args.itineraries)
@@ -34,11 +34,11 @@ def print_args_warnings(args):
3434

3535
if __name__ == '__main__':
3636
parser = argparse.ArgumentParser(
37-
description='Convert GTFS-flex on-demand format to GOFS-lite')
37+
description='Convert GTFS-flex on-demand format to GOFS')
3838
parser.add_argument(
3939
'--gtfs-dir', help='input gtfs directory', metavar='Dir', required=True)
4040
parser.add_argument(
41-
'--gofs-lite-dir', help='output gofs directory', metavar='Dir', required=True)
41+
'--gofs-dir', help='output gofs directory', metavar='Dir', required=True)
4242
parser.add_argument(
4343
'--out-gtfs-dir', help='output directory of patched gtfs', metavar='Dir', required=False)
4444
parser.add_argument(
File renamed without changes.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def extract_calendar_dates_only_calendar(gtfs, used_calendar_ids):
120120

121121
def create_calendar_with_list_dates(calendar_id, active_dates_calendar):
122122
# take a list of dates, and create a calendar with all the dates with a list of exception for all the missing dates
123-
# workaround for the lack of support for a list of supported dates in GOFS-lite
123+
# workaround for the lack of support for a list of supported dates in GOFS
124124
active_dates_calendar.sort()
125125
start_date = active_dates_calendar[0]
126126
end_date = active_dates_calendar[-1]

0 commit comments

Comments
 (0)