Skip to content
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

Import route_directions.txt, if present #73

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions multigtfs/migrations/0003_route_directions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# flake8: noqa
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion
import jsonfield.fields


class Migration(migrations.Migration):

dependencies = [
('multigtfs', '0002_add_on_delete'),
]

operations = [
migrations.CreateModel(
name='RouteDirection',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('direction', models.CharField(blank=True, choices=[('0', '0'), ('1', '1')], help_text='Direction of travel for direction description.', max_length=1)),
('direction_name', models.CharField(blank=True, help_text='Destination identification for passengers.', max_length=255)),
('extra_data', jsonfield.fields.JSONField(blank=True, default={}, null=True)),
('route', models.ForeignKey(blank=True, help_text='Route for which this direction description applies.', null=True, on_delete=django.db.models.deletion.CASCADE, to='multigtfs.Route')),
],
options={
'db_table': 'route_directions',
},
),
]
6 changes: 4 additions & 2 deletions multigtfs/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .feed_info import FeedInfo
from .frequency import Frequency
from .route import Route
from .route_direction import RouteDirection
from .service import Service
from .service_date import ServiceDate
from .shape import Shape, ShapePoint
Expand All @@ -33,5 +34,6 @@

# pyflakes be quiet
__models = (
Agency, Block, Fare, FareRule, Feed, FeedInfo, Frequency, Route, Service,
ServiceDate, Shape, ShapePoint, Stop, StopTime, Transfer, Trip, Zone)
Agency, Block, Fare, FareRule, Feed, FeedInfo, Frequency, Route,
RouteDirection, Service, ServiceDate, Shape, ShapePoint, Stop,
StopTime, Transfer, Trip, Zone)
4 changes: 3 additions & 1 deletion multigtfs/models/feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from .feed_info import FeedInfo
from .frequency import Frequency
from .route import Route
from .route_direction import RouteDirection
from .service import Service
from .service_date import ServiceDate
from .shape import ShapePoint, post_save_shapepoint
Expand Down Expand Up @@ -91,6 +92,7 @@ def import_gtfs(self, gtfs_obj):
gtfs_order = (
Agency, Stop, Route, Service, ServiceDate, ShapePoint, Trip,
StopTime, Frequency, Fare, FareRule, Transfer, FeedInfo,
RouteDirection,
)
post_save.disconnect(dispatch_uid='post_save_shapepoint')
post_save.disconnect(dispatch_uid='post_save_stop')
Expand Down Expand Up @@ -157,7 +159,7 @@ def export_gtfs(self, gtfs_file):

gtfs_order = (
Agency, Service, ServiceDate, Fare, FareRule, FeedInfo, Frequency,
Route, ShapePoint, StopTime, Stop, Transfer, Trip,
Route, RouteDirection, ShapePoint, StopTime, Stop, Transfer, Trip,
)

for klass in gtfs_order:
Expand Down
60 changes: 60 additions & 0 deletions multigtfs/models/route_direction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#
# Copyright 2012-2014 John Whitlock
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import unicode_literals

from django.utils.encoding import python_2_unicode_compatible
from jsonfield import JSONField

from multigtfs.models.base import models, Base


@python_2_unicode_compatible
class RouteDirection(Base):
"""Associate a name with a Route and Direction

Maps to non-standard route_directions.txt in GTFS feed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any online docs for the format of this file that we could link here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the best approach would be for me to create a section for it under Optional Files in docs/gtfs.rst? I could do that this afternoon and add to this PR.

The closest thing to documentation seems to be this discussion, which mainly discusses adding this info to trips.txt, but the final paragraph of the final post in that thread explains why a route_directions.txt would be a better approach and seems to be where it was first suggested. Guessing people ran off with it after that.

"""
route = models.ForeignKey(
'Route', null=True, blank=True,
help_text="Route for which this direction description applies.",
on_delete=models.CASCADE)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 adding the on_delete method

direction = models.CharField(
max_length=1, blank=True,
choices=(('0', '0'), ('1', '1')),
help_text="Direction of travel for direction description.")
direction_name = models.CharField(
max_length=255, blank=True,
help_text="Destination identification for passengers.")
extra_data = JSONField(default={}, blank=True, null=True)

def __str__(self):
u = "%d-%s-%s" % (
self.route.feed.id, self.route.route_id, self.direction)
return u

class Meta:
db_table = 'route_directions'
app_label = 'multigtfs'

# For Base import/export
_column_map = (
('route_id', 'route__route_id'),
('direction_id', 'direction'),
('direction_name', 'direction_name'),
)
_filename = 'route_directions.txt'
_rel_to_feed = 'route__feed'
_sort_order = ('route__route_id', 'direction')
_unique_fields = ('route_id', 'direction_id',)
6 changes: 4 additions & 2 deletions multigtfs/tests/test_feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

from multigtfs.models import (
Agency, Block, Fare, FareRule, Feed, FeedInfo, Frequency,
Route, Service, ServiceDate, Shape, ShapePoint, Stop, StopTime, Transfer,
Trip, Zone)
Route, RouteDirection, Service, ServiceDate, Shape, ShapePoint,
Stop, StopTime, Transfer, Trip, Zone)

my_dir = os.path.dirname(__file__)
fixtures_dir = os.path.join(my_dir, 'fixtures')
Expand Down Expand Up @@ -189,6 +189,7 @@ def test_import_gtfs_test4(self):
self.assertEqual(FeedInfo.objects.count(), 1)
self.assertEqual(Frequency.objects.count(), 0)
self.assertEqual(Route.objects.count(), 1)
self.assertEqual(RouteDirection.objects.count(), 2)
self.assertEqual(Service.objects.count(), 1)
self.assertEqual(ServiceDate.objects.count(), 15)
self.assertEqual(Shape.objects.count(), 1)
Expand Down Expand Up @@ -811,6 +812,7 @@ def test_export_gtfs_test4(self):
'fare_rules.txt',
'feed_info.txt',
'routes.txt',
'route_directions.txt',
'shapes.txt',
'stop_times.txt',
'stops.txt',
Expand Down
69 changes: 69 additions & 0 deletions multigtfs/tests/test_route_direction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#
# Copyright 2012-2014 John Whitlock
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import unicode_literals

from django.test import TestCase
from django.utils.six import StringIO

from multigtfs.models import Feed, Route, RouteDirection


class RouteDirectionTest(TestCase):
def setUp(self):
self.feed = Feed.objects.create()
self.route = Route.objects.create(
feed=self.feed, route_id='R1', rtype=3)

def test_string(self):
direction = RouteDirection.objects.create(
route=self.route, direction='0',
direction_name='Direction')
self.assertEqual(str(direction), '%d-R1-0' % self.feed.id)

def test_import_route_directions_txt(self):
route_directions_txt = StringIO("""\
route_id,direction_id,direction_name
R1,0,Direction
""")
RouteDirection.import_txt(route_directions_txt, self.feed)
direction = RouteDirection.objects.get()
self.assertEqual(direction.route, self.route)
self.assertEqual(direction.direction, '0')
self.assertEqual(direction.direction_name, 'Direction')

def test_import_route_directions_txt_duplicate(self):
route_directions_txt = StringIO("""\
route_id,direction_id,direction_name
R1,0,Direction
R1,0,Direction
""")
RouteDirection.import_txt(route_directions_txt, self.feed)
direction = RouteDirection.objects.get()
self.assertEqual(direction.direction, '0')

def test_export_route_directions_empty(self):
directions_txt = RouteDirection.export_txt(self.feed)
self.assertFalse(directions_txt)

def test_export_route_directions_txt(self):
RouteDirection.objects.create(
route=self.route, direction='0',
direction_name='Direction')
directions_txt = RouteDirection.export_txt(self.feed)
self.assertEqual(directions_txt, """\
route_id,direction_id,direction_name
R1,0,Direction
""")