Skip to content

Commit

Permalink
Merge pull request #18 from raceconditionrunning/build-db
Browse files Browse the repository at this point in the history
build routes.yml from route-db.csv
  • Loading branch information
ztatlock committed Apr 6, 2024
2 parents 73e50c9 + 1a0cbe4 commit 48dffc5
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 138 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/github-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,23 @@ jobs:
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.x'
cache: pip

- name: Install calendar dependencies
run: pip install -r requirements.txt

- name: Build routes
run: make _data/routes.yml

- name: Build calendar
run: make rcc.ics

- name: Set up Pages
id: pages
uses: actions/configure-pages@v3
uses: actions/configure-pages@v4

- name: Set up Ruby
uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
Expand All @@ -62,7 +65,7 @@ jobs:
- name: Upload artifact
# Automatically uploads an artifact from the './_site' directory by default
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3

# Deployment job
deploy:
Expand All @@ -74,4 +77,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ vendor/

# auto-built iCal file
rcc.ics
rcc_weekends.ics

# misc files
.DS_Store

# python virtual environment
.venv

# Bundler
.bundle/
vendor/
Expand Down
47 changes: 26 additions & 21 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
addressable (2.8.5)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
afm (0.2.2)
async (2.6.4)
async (2.10.1)
console (~> 1.10)
fiber-annotation
io-event (~> 1.1)
io-event (~> 1.5, >= 1.5.1)
timers (~> 4.1)
bigdecimal (3.1.7)
colorator (1.1.0)
concurrent-ruby (1.2.2)
console (1.23.2)
concurrent-ruby (1.2.3)
console (1.23.6)
fiber-annotation
fiber-local
json
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
Expand All @@ -25,7 +27,8 @@ GEM
fiber-annotation (0.2.0)
fiber-local (1.0.0)
forwardable-extended (2.6.0)
google-protobuf (3.24.4)
google-protobuf (4.26.1)
rake (>= 13)
hashery (2.1.2)
html-proofer (5.0.8)
addressable (~> 2.3)
Expand All @@ -36,13 +39,13 @@ GEM
typhoeus (~> 1.3)
yell (~> 2.0)
zeitwerk (~> 2.5)
htmlbeautifier (1.4.2)
htmlbeautifier (1.4.3)
htmlcompressor (0.4.0)
http_parser.rb (0.8.0)
i18n (1.14.1)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
io-event (1.3.3)
jekyll (4.3.2)
io-event (1.5.1)
jekyll (4.3.3)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
Expand Down Expand Up @@ -70,51 +73,53 @@ GEM
jekyll
jekyll-watch (2.2.1)
listen (~> 3.0)
json (2.7.2)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.8.0)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
mini_portile2 (2.8.5)
nokogiri (1.16.2)
nokogiri (1.16.3)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
pdf-reader (2.11.0)
pdf-reader (2.12.0)
Ascii85 (~> 1.0)
afm (~> 0.2.1)
hashery (~> 2.0)
ruby-rc4
ttfunk
public_suffix (5.0.3)
public_suffix (5.0.5)
racc (1.7.3)
rainbow (3.1.1)
rake (13.0.6)
rake (13.2.0)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.6)
rouge (4.1.3)
rouge (4.2.1)
ruby-rc4 (0.1.5)
safe_yaml (1.0.5)
sass-embedded (1.69.4)
google-protobuf (~> 3.23)
sass-embedded (1.74.1)
google-protobuf (>= 3.25, < 5.0)
rake (>= 13.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
timers (4.3.5)
ttfunk (1.7.0)
typhoeus (1.4.0)
ttfunk (1.8.0)
bigdecimal (~> 3.1)
typhoeus (1.4.1)
ethon (>= 0.9.0)
unicode-display_width (2.5.0)
webrick (1.8.1)
yell (2.2.2)
zeitwerk (2.6.12)
zeitwerk (2.6.13)

PLATFORMS
ruby
Expand Down
20 changes: 19 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
DATA = _data
ROUTES = $(DATA)/routes.yml
ROUTE_DB_CSV = $(DATA)/route-db.csv
ROUTE_DB_YML = $(DATA)/route-db.yml
LEGACY_ROUTES = $(DATA)/legacy-routes.yml
SCHEDULE = $(DATA)/schedule.yml

.PHONY: all build serve publish clean

all: build

rcc.ics: _bin/mkical.py _data/schedule.yml _data/routes.yml
$(ROUTE_DB_YML): _bin/route-db.py $(ROUTE_DB_CSV)
python3 $< $(DATA)

$(ROUTES): $(ROUTE_DB_YML) $(LEGACY_ROUTES)
cat $(ROUTE_DB_YML) > $(ROUTES)
echo >> $(ROUTES)
echo "# LEGACY ROUTES" >> $(ROUTES)
echo >> $(ROUTES)
cat $(LEGACY_ROUTES) >> $(ROUTES)

rcc.ics: _bin/mkical.py $(SCHEDULE) $(ROUTES)
python3 $<

build: rcc.ics
Expand All @@ -12,4 +29,5 @@ serve: rcc.ics
watchy -w _config.yml -- bundle exec jekyll serve --watch --drafts --host=0.0.0.0

clean:
rm -f $(ROUTES) $(ROUTE_DB_YML)
rm -rf _site/ .jekyll-cache/ rcc.ics rcc_weekends.ics
131 changes: 131 additions & 0 deletions _bin/route-db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import csv
import os
import sys

FIELDS = ['id', 'name', 'start', 'dist', 'elev', 'end', 'type', 'map']
TYPES = ['Loop', 'P2P', 'OB']
LOCS = [
'Beacon',
'CapHill',
'ColCity',
'CSE',
'GasWorks',
'JaysCafe',
'Locks',
'MtBaker',
'Northgate',
'Roosevelt',
'SODO',
'Westlake'
]

warnings = False
def warn(msg):
global warnings
warnings = True
print(f"WARNING! {msg}")

def warn_rc(route, msg):
warn(f"route {route['id']}: {msg}")

def check_route(route, gpx_dir):
# all fields set
for field in FIELDS:
if field not in route or route[field].strip() == '':
warn_rc(route, f"missing '{field}' field")

# valid type
if route['type'] not in TYPES:
warn_rc(route, f"invalid type '{route['type']}'")

# valid start and end locations
if route['start'] not in LOCS:
warn_rc(route, f"invalid start '{route['start']}'")
if route['end'] not in LOCS:
warn_rc(route, f"invalid end '{route['end']}'")

# valid dist and elev
try:
assert 0 < float(route['dist'])
except:
warn_rc(route, f"invalid dist '{route['dist']}'")
try:
assert 0 < float(route['elev'])
except:
warn_rc(route, f"invalid elev '{route['elev']}'")

# every route has a gpx
gpx_path = os.path.join(gpx_dir, route['id']) + '.gpx'
if not os.path.isfile(gpx_path):
warn_rc(route, f"no GPX file at '{gpx_path}'")

def main():
# get args
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <data_dir>")
exit(1)

data_dir = sys.argv[1]
if not os.path.isdir(data_dir):
print(f"Error: no such directory '{data_dir}'")
exit(1)

csv_path = os.path.join(data_dir, 'route-db.csv')
if not os.path.isfile(csv_path):
print(f"Error: no route-db.csv at '{csv_path}'")
exit(1)

gpx_dir = os.path.join(data_dir, 'gpx')
if not os.path.isdir(gpx_dir):
print(f"Error: no gpx directory at '{gpx_dir}'")
exit(1)

yml_path = os.path.join(data_dir, 'route-db.yml')

# read routes
with open(csv_path, 'r') as f:
reader = csv.DictReader(f, dialect='unix')
routes = list(reader)

# ensure all route ids unique
ids = set()
for route in routes:
if route['id'] in ids:
warn(f"route {route['id']} is not unique")
ids.add(route['id'])

# check every route
for route in routes:
check_route(route, gpx_dir)

# bail if any warnings
if warnings:
print("Exiting due to warnings - please fix and re-run.")
exit(1)

# sort routes by start and increasing distance
routes.sort(key=lambda x: (x['start'], float(x['dist']), x['end'], x['type'], x['id']))

# write sorted routes back
with open(csv_path, 'w') as f:
writer = csv.DictWriter(f, fieldnames=reader.fieldnames, dialect='unix')
writer.writeheader()
for route in routes:
writer.writerow(route)

# output yaml version as well
with open(yml_path, 'w') as f:
f.write('# AUTOGENERATED - DO NOT EDIT\n\n')
for route in routes:
f.write(f"- id: {route['id']}\n")
f.write(f" name: \"{route['name']}\"\n")
f.write(f" start: \"{route['start']}\"\n")
f.write(f" dist: {route['dist']}\n")
f.write(f" elev: {route['elev']}\n")
f.write(f" end: \"{route['end']}\"\n")
f.write(f" type: \"{route['type']}\"\n")
f.write(f" map: \"{route['map']}\"\n")
f.write('\n')

if __name__ == '__main__':
main()
3 changes: 2 additions & 1 deletion _data/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
route-db.yaml
route-db.yml
routes.yml
File renamed without changes.
Loading

0 comments on commit 48dffc5

Please sign in to comment.