-
Notifications
You must be signed in to change notification settings - Fork 8
Issue 40 validate example data #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
a24f119
74b02d9
9db30a9
7fdddfe
dba9e26
eaa009a
af15c9a
c0acbd1
bcc5a0f
3532b15
53a05b9
3aadfea
9661da0
5332619
e05cd3c
9d50d5d
06a8294
102e1c3
d7a9756
446ec34
5088e6c
ec3c7e8
1b94140
9d8f05f
88e19b0
cb6dd18
5bc9310
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| name: Validate Example TIDES Data | ||
|
|
||
| on: | ||
| push: | ||
| paths: | ||
| - 'data/*/data/*' | ||
| - 'spec/*' | ||
| pull_request: | ||
| paths: | ||
| - 'data/*/data/*' | ||
| - 'spec/*' | ||
| workflow_dispatch: | ||
| create: | ||
|
|
||
| jobs: | ||
| validate: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v2 | ||
| - name: Validate data | ||
| uses: frictionlessdata/repository@v2 | ||
|
botanize marked this conversation as resolved.
|
||
| with: | ||
| packages: "data/*/data/datapackage.json" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| # Contributors to the TIDES Suite | ||
| # Contributors to the TIDES Suite |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Data Directory Organization | ||
|
e-lo marked this conversation as resolved.
Outdated
|
||
|
|
||
| Each TIDES Data Package example should follow the following directory structure, consistent with the structure of the [Frictionless Data Package specification](https://specs.frictionlessdata.io/data-package/), including: | ||
|
|
||
| ``` | ||
| unique-example-name | ||
| \data # data to be validated against the TIDES specification | ||
|
e-lo marked this conversation as resolved.
Outdated
|
||
| \data\datapackages.json # data package metadata per https://specs.frictionlessdata.io/data-package/ | ||
|
e-lo marked this conversation as resolved.
Outdated
|
||
| \raw # data which the agency uses to create TIDES data | ||
| \scripts # scripts used to transform raw --> TIDES | ||
| ``` | ||
|
|
||
| ## Data validation | ||
|
|
||
| Data with a valid `datapackage.json` can be easily validated using the [frictionless framework](https://framework.frictionlessdata.io/), which can be installed and invoke as follows: | ||
|
|
||
| ```bash | ||
| pip install frictionless | ||
| frictionless validate path/to/your/datapackage.json | ||
| ``` | ||
|
|
||
| ### Continuous Data Validation | ||
|
|
||
| Example data in the `\data` subdirectories is validated upon a push action to the main repository according to the `TIDES` schema posted to the `main` branch. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # Example TIDES Data Package | ||
|
|
||
| Template directory for example scaffolding and helper scripts. | ||
|
|
||
| ## Scripts for Generating Data | ||
|
|
||
| `scripts\create_example.py` has some template code which can help with the following | ||
|
|
||
| - `write_schema_examples()`: will generate blank csvs according to the TIDES schema | ||
|
e-lo marked this conversation as resolved.
|
||
| - `write_datapackage()` will generate a datapackage.json based on the TIDES schemas and a set of defaults specified in th script. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'd prefer a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the functions serve much purpose other than for developers to be able to auto-generate the example
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But what value is a CSV template? It's just a header row, which is trivial if you're formatting data from another source anyway. Like I guess you could tell python/R to append to the empty csv instead of writing a header, but that saves essentially no effort or may even create a tiny amount of work (e.g., |
||
|
|
||
| To run both (note this replaces the existing files in the directory) | ||
|
|
||
| ```bash | ||
| python data/example/scripts/create_example.py | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| { | ||
| "name": "Example TIDES Data Package", | ||
|
e-lo marked this conversation as resolved.
Outdated
|
||
| "title": "example", | ||
| "profile": "tabular-data-package", | ||
| "licenses": [ | ||
| { | ||
| "name": "Apache-2.0" | ||
| } | ||
| ], | ||
|
botanize marked this conversation as resolved.
Outdated
|
||
| "contributors": [ | ||
| { | ||
| "title": "My Name", | ||
| "email": "me@myself.com" | ||
|
e-lo marked this conversation as resolved.
Outdated
|
||
| } | ||
| ], | ||
| "maintainers": [ | ||
| { | ||
| "title": "Another Name", | ||
| "email": "another@myself.com" | ||
| } | ||
| ], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple of custom fields might be nice, e.g., "_CAD": "TransitMaster",
"_NTDID": 50027,
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ NTDID Wondering if we should add the software to the individual resources as Thoughts?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per https://specs.frictionlessdata.io/patterns/#private-properties, this shouldn't be a private property b/c it is user specified metadata.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now, added:
|
||
| "resources": [ | ||
| { | ||
| "name": "devices", | ||
| "path": "devices.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/devices.schema.json" | ||
| }, | ||
| { | ||
| "name": "vehicle_locations", | ||
| "path": "vehicle_locations.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/vehicle_locations.schema.json" | ||
| }, | ||
| { | ||
| "name": "fare_transactions", | ||
| "path": "fare_transactions.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/fare_transactions.schema.json" | ||
| }, | ||
| { | ||
| "name": "train_cars", | ||
| "path": "train_cars.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/train_cars.schema.json" | ||
| }, | ||
| { | ||
| "name": "operators", | ||
| "path": "operators.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/operators.schema.json" | ||
| }, | ||
| { | ||
| "name": "stop_visits", | ||
| "path": "stop_visits.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/stop_visits.schema.json" | ||
| }, | ||
| { | ||
| "name": "vehicle_train_cars", | ||
| "path": "vehicle_train_cars.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/vehicle_train_cars.schema.json" | ||
| }, | ||
| { | ||
| "name": "vehicles", | ||
| "path": "vehicles.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/vehicles.schema.json" | ||
| }, | ||
| { | ||
| "name": "trips_performed", | ||
| "path": "trips_performed.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/trips_performed.schema.json" | ||
| }, | ||
| { | ||
| "name": "station_activities", | ||
| "path": "station_activities.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/station_activities.schema.json" | ||
| }, | ||
| { | ||
| "name": "passenger_events", | ||
| "path": "passenger_events.csv", | ||
| "schema": "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/passenger_events.schema.json" | ||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| device_id,stop_id,vehicle_id,train_car_id,device_type,device_vendor,device_model,location |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| transaction_id,date,timestamp,amount,currency_type,fare_action,trip_id_performed,stop_sequence,vehicle_id,device_id,fare_id,stop_id,group_size,media_type,rider_category,fare_product,fare_period,fare_capped,fare_media_id,fare_media_id_purchased,balance |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| operator_id |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| passenger_event_id,date,timestamp,trip_id_performed,stop_sequence,event_type,vehicle_id,device_id,train_car_id,stop_id |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| date,stop_id,time_period_start,time_period_end,time_period_category,total_entries,total_exits,number_of_transactions,transaction_revenue_cash,transaction_revenue_smartcard,transaction_revenue_magcard,transaction_revenue_bankcard,transaction_revenue_nfc,transaction_revenue_optical,transaction_revenue_operator,transaction_revenue_other,transaction_count_cash,transaction_count_smartcard,transaction_count_magcard,transaction_count_bankcard,transaction_count_nfc,transaction_count_optical,transaction_count_operator,transaction_count_other,bike_entries,bike_exits,ramp_entries,ramp_exits |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| date,trip_id_performed,stop_sequence,vehicle_id,dwell,stop_id,checkpoint,schedule_arrival_time,schedule_departure_time,actual_arrival_time,actual_departure_time,distance,boarding_1,alighting_1,boarding_2,alighting_2,load,door_open,door_close,door_status,ramp_deployed_time,ramp_failure,kneel_deployed_time,lift_deployed_time,bike_rack_deployed,bike_load,revenue,number_of_transactions,transaction_revenue_cash,transaction_revenue_smartcard,transaction_revenue_magcard,transaction_revenue_bankcard,transaction_revenue_nfc,transaction_revenue_optical,transaction_revenue_operator,transaction_revenue_other,transaction_count_cash,transaction_count_smartcard,transaction_count_magcard,transaction_count_bankcard,transaction_count_nfc,transaction_count_optical,transaction_count_operator,transaction_count_other,schedule_relationship |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| train_car_id,model_name,facility_name,capacity_seated,wheelchair_capacity,bike_capacity,bike_rack,capacity_standing,train_car_type |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| date,trip_id_performed,vehicle_id,trip_id_scheduled,route_id,route_type,shape_id,direction_id,operator_id,block_id,trip_start_stop_id,trip_end_stop_id,schedule_trip_start,schedule_trip_end,actual_trip_start,actual_trip_end,in_service,schedule_relationship |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| location_ping_id,date,timestamp,trip_id_performed,stop_sequence,vehicle_id,device_id,stop_id,current_status,latitude,longitude,gps_quality,heading,speed,odometer,schedule_deviation,headway_deviation,in_service,schedule_relationship |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| vehicle_id,train_car_id,order,operator_id |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| vehicle_id,vehicle_start,vehicle_end,model_name,facility_name,capacity_seated,wheelchair_capacity,capacity_bike,bike_rack,capacity_standing |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| import glob | ||
| import json | ||
| import os | ||
| import pathlib | ||
|
|
||
|
|
||
| EXAMPLE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
| BASE_REPO_DIR = os.path.dirname(os.path.dirname(EXAMPLE_DIR)) | ||
| TIDES_SPEC = os.path.join(BASE_REPO_DIR, "spec") | ||
| SCHEMAS = glob.glob(os.path.join(TIDES_SPEC, "**/*.schema.json"), recursive=True) | ||
| SCHEMAS_LOC = "https://raw.githubusercontent.com/TIDES-transit/TIDES/main/spec/" | ||
|
|
||
| # DATAPACKAGE.JSON INFORMATION | ||
| # Per https://specs.frictionlessdata.io/data-package/ | ||
| TITLE = "example" | ||
| NAME = "Example TIDES Data Package" | ||
| PROFILE = "tabular-data-package" | ||
| LICENSES = [{"name": "Apache-2.0"}] | ||
| SOURCES = [{"title": "Generated from /scripts/create_example.py"}] | ||
| CONTRIBUTORS = [{"title": "My Name", "email": "me@myself.com"}] | ||
| MAINTAINERS = [{"title": "Another Name", "email": "another@myself.com"}] | ||
| DATAPACKAGE_TEMPLATE = { | ||
| "name": NAME, | ||
| "title": TITLE, | ||
| "profile": PROFILE, | ||
| "licenses": LICENSES, | ||
| "contributors": CONTRIBUTORS, | ||
| "maintainers": MAINTAINERS, | ||
| "resources": [], | ||
| } | ||
|
|
||
|
|
||
| def write_schema_examples( | ||
| out_dir: str, | ||
| schemas: list = SCHEMAS, | ||
| ) -> None: | ||
| """Write blank csvs to out_dir with headings for for each schema in list. | ||
|
|
||
| Args: | ||
| out_dir (str): Where blank csvs are written. | ||
| schemas (list, optional): List of schemas to generate blank csvs for. Defaults to SCHEMAS. | ||
| """ | ||
| for s in schemas: | ||
| write_csv_for_schema(s, out_dir) | ||
|
|
||
|
|
||
| def write_datapackage( | ||
| out_dir: str, | ||
| schemas: list = SCHEMAS, | ||
| template: dict = DATAPACKAGE_TEMPLATE, | ||
| ) -> None: | ||
| """Write a datapackage.json file in Frictionless data-package format based on list of schemas. | ||
|
|
||
| Args: | ||
| out_dir (str): directory where datapackage.json is written. | ||
| schemas (list, optional): List of schemas to add to resources list . Defaults to SCHEMAS. | ||
| """ | ||
| datapackage = template | ||
| datapackage["resources"] = [schema_to_resources(s) for s in schemas] | ||
|
|
||
| out_filename = os.path.join(out_dir, "datapackage.json") | ||
| with open(out_filename, "w") as outfile: | ||
| outfile.write(json.dumps(datapackage, indent=4)) | ||
| print(f"Wrote {out_filename}") | ||
| json.dumps | ||
|
|
||
|
|
||
| def schema_to_resources(schema_filename: str) -> dict: | ||
| """Transform a schema filename into a frictionless resource for listing in datapackage.json | ||
|
|
||
| Args: | ||
| schema_filename (str): Schema file in frictionless format. | ||
|
|
||
| Returns: | ||
| dict: object consistent with frictionless data resource specification | ||
| """ | ||
| schema_filename = pathlib.Path(schema_filename) | ||
| name = schema_filename.stem.split(".")[0] | ||
| path = name + ".csv" | ||
| schema_loc = SCHEMAS_LOC + name + ".schema.json" | ||
|
|
||
| return {"name": name, "path": path, "schema": schema_loc} | ||
|
|
||
|
|
||
| def write_csv_for_schema( | ||
| schema_filename: str, | ||
| out_dir: str, | ||
| ) -> None: | ||
| """Creates blank csvs which comply with a schema. | ||
|
|
||
| Args: | ||
| schema_filename (str): Filename with the Frictionless data schema | ||
| out_dir (str): Where the csv will be written | ||
| """ | ||
| schema = read_schema(schema_filename) | ||
| fields = [s["name"] for s in schema["fields"]] | ||
| schema_name = pathlib.Path(schema_filename).stem.split(".")[0] | ||
| out_filename = os.path.join(out_dir, schema_name + ".csv") | ||
| with open(out_filename, "w") as outfile: | ||
| outfile.write(",".join(fields)) | ||
| print(f"Wrote {out_filename}") | ||
|
e-lo marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| def read_schema(schema_file: str) -> dict: | ||
| """ | ||
| Reads in schema from schema json file and returns as dictionary. | ||
|
|
||
| Args: | ||
| schema_file: File location of the schema json file. | ||
| Returns: The schema as a dictionary | ||
| """ | ||
| with open(schema_file, encoding="utf-8") as f: | ||
| schema = json.load(f) | ||
| return schema | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| write_schema_examples(out_dir=os.path.join(EXAMPLE_DIR, "data")) | ||
| write_datapackage(out_dir=os.path.join(EXAMPLE_DIR, "data")) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| frictionless | ||
| frictionless[pandas] | ||
|
e-lo marked this conversation as resolved.
Outdated
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Development | ||
|
|
||
| {{ include_file('CONTRIBUTING.md') }} | ||
|
|
||
| {{ include_file('CODE_OF_CONDUCT.md') }} | ||
|
|
||
| {{ include_file('contributors.md') }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Example Data | ||
|
|
||
| Example data can be found in the `/data` directory, with one directory for each example. | ||
|
|
||
| {{ include_file('data/README.md')}} | ||
|
|
||
| ## Data List | ||
|
|
||
| {{ list_examples('data') }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {{ include_file('README.md') }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,4 +8,4 @@ mkdocs-material | |
| mkdocs-mermaid2-plugin | ||
| mkdocs-redirects | ||
| pandas | ||
| tabulate | ||
| tabulate | ||
Uh oh!
There was an error while loading. Please reload this page.