From 62f57ad6b4cb7c9ae4de3bba3a42dd20819cd879 Mon Sep 17 00:00:00 2001 From: Morgan Harvey Date: Mon, 31 Jul 2017 13:43:23 -0700 Subject: [PATCH 1/3] Begin addition of route_direction Begin addition of non-standard route_direction.txt --- multigtfs/models/route_direction.py | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 multigtfs/models/route_direction.py diff --git a/multigtfs/models/route_direction.py b/multigtfs/models/route_direction.py new file mode 100644 index 0000000..bf055f5 --- /dev/null +++ b/multigtfs/models/route_direction.py @@ -0,0 +1,58 @@ +# +# 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. + """ + route = models.ForeignKey( + 'Route', null=True, blank=True, + help_text="Route for which this direction description applies.") + 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.fare.feed.id, self.fare.fare_id, self.route.route_id) + return u + + class Meta: + db_table = 'route_directions' + app_label = 'multigtfs' + + # For Base import/export + _column_map = ( + ('route_id', 'route__route_id'), + ('direction', 'direction'), + ('direction_name', 'direction_name'), + ) + _filename = 'route_directions.txt' + _rel_to_feed = 'route__feed' + _sort_order = ('route__route_id', 'direction') + _unique_fields = () From 18390976190dbc8407f6ee7f4e95f7d537faf429 Mon Sep 17 00:00:00 2001 From: Morgan Harvey Date: Mon, 28 Aug 2017 20:09:46 -0700 Subject: [PATCH 2/3] Tests and completion of model for route_directions.txt Finished specification of model for RouteDirection. Added basic tests and configurations. --- multigtfs/migrations/0003_route_directions.py | 30 ++++++++ multigtfs/models/__init__.py | 6 +- multigtfs/models/route_direction.py | 10 +-- multigtfs/tests/test_route_direction.py | 69 +++++++++++++++++++ 4 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 multigtfs/migrations/0003_route_directions.py create mode 100644 multigtfs/tests/test_route_direction.py diff --git a/multigtfs/migrations/0003_route_directions.py b/multigtfs/migrations/0003_route_directions.py new file mode 100644 index 0000000..1f21065 --- /dev/null +++ b/multigtfs/migrations/0003_route_directions.py @@ -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', + }, + ), + ] diff --git a/multigtfs/models/__init__.py b/multigtfs/models/__init__.py index 55b2dd2..8d7ac68 100644 --- a/multigtfs/models/__init__.py +++ b/multigtfs/models/__init__.py @@ -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 @@ -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) diff --git a/multigtfs/models/route_direction.py b/multigtfs/models/route_direction.py index bf055f5..480bba4 100644 --- a/multigtfs/models/route_direction.py +++ b/multigtfs/models/route_direction.py @@ -28,7 +28,8 @@ class RouteDirection(Base): """ route = models.ForeignKey( 'Route', null=True, blank=True, - help_text="Route for which this direction description applies.") + help_text="Route for which this direction description applies.", + on_delete=models.CASCADE) direction = models.CharField( max_length=1, blank=True, choices=(('0', '0'), ('1', '1')), @@ -39,7 +40,8 @@ class RouteDirection(Base): extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): - u = "%d-%s-%s" % (self.fare.feed.id, self.fare.fare_id, self.route.route_id) + u = "%d-%s-%s" % ( + self.route.feed.id, self.route.route_id, self.direction) return u class Meta: @@ -49,10 +51,10 @@ class Meta: # For Base import/export _column_map = ( ('route_id', 'route__route_id'), - ('direction', 'direction'), + ('direction_id', 'direction'), ('direction_name', 'direction_name'), ) _filename = 'route_directions.txt' _rel_to_feed = 'route__feed' _sort_order = ('route__route_id', 'direction') - _unique_fields = () + _unique_fields = ('route_id', 'direction_id',) diff --git a/multigtfs/tests/test_route_direction.py b/multigtfs/tests/test_route_direction.py new file mode 100644 index 0000000..71d4479 --- /dev/null +++ b/multigtfs/tests/test_route_direction.py @@ -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 +""") From a196e3a1a4d17b1dcb9a1d50efa31ff67c2b0be5 Mon Sep 17 00:00:00 2001 From: Morgan Harvey Date: Tue, 29 Aug 2017 10:52:03 -0700 Subject: [PATCH 3/3] Add RouteDirection to feed.py to allow import Add RouteDirection to feed.py to get route_directions.txt to import. Modified associated tests to account for new optional file. --- multigtfs/models/feed.py | 4 +++- multigtfs/tests/test_feed.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/multigtfs/models/feed.py b/multigtfs/models/feed.py index 0aa7d95..6e2a604 100644 --- a/multigtfs/models/feed.py +++ b/multigtfs/models/feed.py @@ -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 @@ -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') @@ -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: diff --git a/multigtfs/tests/test_feed.py b/multigtfs/tests/test_feed.py index 3472ea1..fabbfeb 100644 --- a/multigtfs/tests/test_feed.py +++ b/multigtfs/tests/test_feed.py @@ -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') @@ -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) @@ -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',