diff --git a/examples/explore/exploreproj/settings.py b/examples/explore/exploreproj/settings.py index 5f30f6c..c23f048 100644 --- a/examples/explore/exploreproj/settings.py +++ b/examples/explore/exploreproj/settings.py @@ -21,7 +21,11 @@ ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv()) # Application definition +# NB new apps need to be placed first for their templates +# to override subsequent apps INSTALLED_APPS = [ + 'exploreapp', + 'multigtfs', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -29,8 +33,6 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.gis', - 'exploreapp', - 'multigtfs', ] LOCAL_INSTALLED_APPS = list(config('LOCAL_INSTALLED_APPS', diff --git a/examples/gtfsproj/.env.example b/examples/gtfsproj/.env.example new file mode 100644 index 0000000..fd2fc93 --- /dev/null +++ b/examples/gtfsproj/.env.example @@ -0,0 +1,7 @@ +SECRET_KEY=l$hc*-biy7*#7cic5q5r^mtf-2&l34pq4k7znn%)si+$h(i%e& + +DEBUG=True +ALLOWED_HOSTS=* + +DATABASE_URL=spatialite:///db.sqlite3 +#DATABASE_URL=postgis://USER:PASSWORD@HOST:PORT/DBNAME diff --git a/examples/gtfsproj/Dockerfile b/examples/gtfsproj/Dockerfile new file mode 100644 index 0000000..617281a --- /dev/null +++ b/examples/gtfsproj/Dockerfile @@ -0,0 +1,28 @@ +FROM python:3 + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + binutils \ + gdal-bin \ + libproj-dev \ + postgresql-client + +# Copy project code (current folder) +COPY . /code + +# Install project dependencies, please note uses +# pypi version of gtfsmulti not local version. +# NB: To use local you will need to copy multigtfs +# code to image and update requirements.txt +WORKDIR /code +RUN pip install -r requirements.txt + +# Create volume for feeds +VOLUME /gtfs/feeds + +# run_project script passes all arguments except +# bash to django manage.py script for project +# runs development server by default +RUN chmod +x /code/run_project.sh +ENTRYPOINT ["/code/run_project.sh"] +CMD ["./manage.py", "runserver", "0.0.0.0:8000"] diff --git a/examples/gtfsproj/README.md b/examples/gtfsproj/README.md new file mode 100644 index 0000000..d00a438 --- /dev/null +++ b/examples/gtfsproj/README.md @@ -0,0 +1,127 @@ +

Multi GTFS project

+ +This is an alternative example project. It is largely based on the +"explore" example project but has been recreated with slightly different +structure. + +The intention is that this example will ultimately provide a +slightly different experience. This will include more mapping (possibly +using leaflet), new template (e.g. bootstrap 4) and different navigation. + +

Run with Docker

+ +

Build and run

+ +Build docker image. This will use the settings in teh .env.example +file, which can be replaced by creating a ".env" file before building +the image (eg. cp env.example .env). + + # Get source code + git clone https://github.com/alaw005/django-multi-gtfs.git + cd django-multi-gtfs/examples/gtfsproj + + # Build image + sudo docker build -t multiproj . + +Run docker container as background (to run in forground change +"-d" to "-it". This will automatically run the development server +using the settings in the .env.example file. + + # Create container + sudo docker run -d -p 8000:8000 --name multiproj1 multiproj + +Now setup database and superuser, refer management commands below. +This is only required if database does not already exist or multgtfs +has been updated (in which case need to migrate only) + + # Setup database (if required) + sudo docker exec -it multiproj1 ./manage.py migrate + sudo docker exec -it multiproj1 ./manage.py createsuperuser + +Now you can access from browser using host IP address and port 8000. + +

Management commands

+ +You can run django management commands as follows: + + sudo docker exec -it multiproj1 ./manage.py help + +For example, to load a new feed you can do the following (assuming +container name is "multiproj1"). NB: The first command copies a +file from host system to the container. + + cat /host/feeds/feed.zip | sudo docker exec -i multiproj1 sh -c 'cat > /gtfs/feeds/feed.zip' + sudo docker exec -it multiproj1 ./manage.py importgtfs /gtfs/feeds/feed.zip + +To specify a feed name rather than the defaul use the following. + + sudo docker exec -it multiproj1 ./manage.py importgtfs --name "Feed name" /feeds/feed.zip + +

Creating postgres database (assumes server already running)

+ +At the command line (as long as postgres is installed): + + # Create database + createdb -h HOST -p 5432 -U USERNAME multigtfs + + # Install postgis extension + psql -h HOST -p 5432 -U USERNAME -d multigtfs -c "CREATE EXTENSION postgis;" + +Now initiatise the database using multigtfs: + + # Configure database + sudo docker exec -it multigtfs ./manage.py migrate + + # Create superuser + sudo docker exec -it multigtfs ./manage.py createsuperuser + +That's it. + +

Run without Docker

+ +The following applies to Ubunutu + + sudo apt-get install -y git python-pip python-virtualenv spatialite-bin gdal-bin + + cd ~/ + git clone https://github.com/alaw005/django-multi-gtfs.git + + # Navigate to project folder + cd django-multi-gtfs/examples/multiproj + + # Create and activate new virtual environment (myenv). + # Use "deactivate" to deactivate virtualenv. + virtualenv --always-copy myenv + source myenv/bin/activate + + pip install -r requirements.txt + + # Create multigtfs tables in database + ./manage.py migrate + + # Create db.sqlite3, superuser. Enter "yes" to create superuser and + # then enter username and password when prompted (you can leave email address blank) + ./manage.py createsuperuser + + # Run + ./manage.py runserver 0.0.0.0:8000 + +Now you can access from browser using host IP address and port 8000. + +

Environment variables

+ +You can either set environment variables in the .env file as per above or directly +at command line when you create the container. For example, to use existing postgis database: + + sudo docker run -d -p 8000:8000 --name multiproj1 -e DATABASE_URL=postgis://USER:PASSWORD@HOST:PORT/DBNAME multiproj + +The environment variables you can change are identified in the following file: + + django-multi-gtfs/examples/gtfsproj/gtfsproj/settings.py + +They all use the config() function e.g. SECRET_KEY is set in the following example, +indicated by the "config('SECRET_KEY'" section. + + SECRET_KEY = config('SECRET_KEY', default='+$8pgzf)luxklr(rhzg4!$6+b^2hbw*-frvh_2-7an9-_==n_u', cast=str) + + diff --git a/examples/gtfsproj/gtfsapp/__init__.py b/examples/gtfsproj/gtfsapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/gtfsproj/gtfsapp/admin.py b/examples/gtfsproj/gtfsapp/admin.py new file mode 100644 index 0000000..13be29d --- /dev/null +++ b/examples/gtfsproj/gtfsapp/admin.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.contrib import admin + +# Register your models here. diff --git a/examples/gtfsproj/gtfsapp/apps.py b/examples/gtfsproj/gtfsapp/apps.py new file mode 100644 index 0000000..47dddba --- /dev/null +++ b/examples/gtfsproj/gtfsapp/apps.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class GtfsappConfig(AppConfig): + name = 'gtfsapp' diff --git a/examples/gtfsproj/gtfsapp/migrations/__init__.py b/examples/gtfsproj/gtfsapp/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/gtfsproj/gtfsapp/models.py b/examples/gtfsproj/gtfsapp/models.py new file mode 100644 index 0000000..1dfab76 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/models.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models + +# Create your models here. diff --git a/examples/gtfsproj/gtfsapp/static/css/explore.css b/examples/gtfsproj/gtfsapp/static/css/explore.css new file mode 100644 index 0000000..4151d28 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/static/css/explore.css @@ -0,0 +1,8 @@ +body { + padding-top: 20px; +} + +.body-top { + padding: 40px 15px; + text-align: center; +} diff --git a/examples/gtfsproj/gtfsapp/static/js/explore.js b/examples/gtfsproj/gtfsapp/static/js/explore.js new file mode 100644 index 0000000..1e6ae2c --- /dev/null +++ b/examples/gtfsproj/gtfsapp/static/js/explore.js @@ -0,0 +1,44 @@ +explore = { + mapPoint: function(map_id, x, y, zoom) { + var map = new OpenLayers.Map(map_id); + var osm = new OpenLayers.Layer.OSM("OpenStreetMap Map"); + var fromProjection = new OpenLayers.Projection("EPSG:4326"); + var toProjection = new OpenLayers.Projection("EPSG:900913"); + var position = new OpenLayers.LonLat(x, y).transform(fromProjection, toProjection); + map.addLayer(osm); + + var markers = new OpenLayers.Layer.Markers( "Markers" ); + map.addLayer(markers); + markers.addMarker(new OpenLayers.Marker(position)); + + map.setCenter(position, zoom); + }, + mapLine: function(map_id, line_wkt) { + var map = new OpenLayers.Map(map_id); + var osm = new OpenLayers.Layer.OSM("OpenStreetMap Map"); + var fromProjection = new OpenLayers.Projection("EPSG:4326"); + var toProjection = new OpenLayers.Projection("EPSG:900913"); + map.addLayer(osm); + + var style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']); + style.strokeColor = "red"; + style.strokeColor = "red"; + style.fillColor = "red"; + style.pointRadius = 10; + style.strokeWidth = 3; + style.rotation = 45; + style.strokeLinecap = "butt"; + var vectorLayer = new OpenLayers.Layer.Vector("Line", {'style': style}); + var wkt = new OpenLayers.Format.WKT({ + 'internalProjection': toProjection, + 'externalProjection': fromProjection, + 'style': style }); + var lineVector = wkt.read(line_wkt); + var bounds = lineVector.geometry.getBounds(); + + map.addLayer(vectorLayer); + vectorLayer.addFeatures([lineVector]); + + map.zoomToExtent(bounds); + } +} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/agency_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/agency_detail.html new file mode 100644 index 0000000..4b3e8d7 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/agency_detail.html @@ -0,0 +1,21 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Agency {{object}}{% endblock %} +{% block page_title %}Agency {{object}}{% endblock %} +{% block page_top_content_elem %} +An Agency represents a transit agency. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
agency_id
{{object.agency_id|default:'Not set'}}
+
name
{{object.name}}
+
url
{% if object.url %}{{object.url}}{% else %}Not set{% endif %}
+
timezone
{{object.timezone}}
+
lang
{{object.lang|default:'Not set'}}
+
phone
{{object.phone|default:'Not set'}}
+
fare_url
{% if object.fare_url %}{{object.fare_url}}{% else %}Not set{% endif %}
+
+{% include "multigtfs/extra_data.html" %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/agency_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/agency_list.html new file mode 100644 index 0000000..2f9b4ec --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/agency_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Agencies{% endblock %} +{% block page_title %}Agencies{% endblock %} +{% block page_top_content_elem %} +An Agency represents a transit agency. +{% endblock %} +{% block page_middle_content %} +{% for agency in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No agencies yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/base.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/base.html new file mode 100644 index 0000000..775cacf --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/base.html @@ -0,0 +1,53 @@ +{% load staticfiles %} + + + + + + + {% block head_title %}Multi GTFS - django-multi-gtfs{% endblock %} + + + + + {% block head_extra %}{% endblock %} + + + +
+
+

{% block page_title %}TODO: set page_title{% endblock %}

+ {% block page_top_content_elem %} +

+ {% block page_top_content %}TODO: set page_top_content{% endblock %} +

+ {% endblock %} +

+
{% block page_middle_content %}{% endblock %}
+
+ + + {% block body_script %}{% endblock %} + + diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/block_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/block_detail.html new file mode 100644 index 0000000..77c2cd6 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/block_detail.html @@ -0,0 +1,19 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Block {{object}}{% endblock %} +{% block page_title %}Block {{object}}{% endblock %} +{% block page_top_content_elem %} +A Block represents a fare zone for a trip. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
block_id
{{object.block_id}}
+
+{% include "multigtfs/extra_data.html" %} +

Related Objects

+ +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/block_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/block_list.html new file mode 100644 index 0000000..10bccee --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/block_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Blocks{% endblock %} +{% block page_title %}Blocks{% endblock %} +{% block page_top_content_elem %} +A Block represents a fare zone for a trip. +{% endblock %} +{% block page_middle_content %} +{% for blocks in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No blockss yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/extra_data.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/extra_data.html new file mode 100644 index 0000000..d04d11e --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/extra_data.html @@ -0,0 +1,8 @@ +{% if object.extra_data %} +

Extra Attributes

+
+{% for key, val in object.extra_data.iteritems %} +
{{ key }}
{{ val }}
+{% endfor %} +
+{% endif %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/fare_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/fare_detail.html new file mode 100644 index 0000000..5b21737 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/fare_detail.html @@ -0,0 +1,24 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Fare {{object}}{% endblock %} +{% block page_title %}Fare {{object}}{% endblock %} +{% block page_top_content_elem %} +A Fare describes how passengers pay for service. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
fare_id
{{object.fare_id}}
+
price
{{object.price}}
+
currency_type
{{object.currency_type}}
+
payment_method
{{object.get_payment_method_display|default:'Not set'}} +
transfers
{{object.get_transfers_display|default:'Not set'}} +
transfer_duration
{{object.transfer_duration|default:'Not set'}}
+
+{% include "multigtfs/extra_data.html" %} +

Related Objects

+ +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/fare_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/fare_list.html new file mode 100644 index 0000000..0aa4340 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/fare_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}Explore - - django-multi-gtfs - Fares{% endblock %} +{% block page_title %}Fares{% endblock %} +{% block page_top_content_elem %} +A Fare describes how passengers pay for service. +{% endblock %} +{% block page_middle_content %} +{% for fare in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No fares yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/farerule_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/farerule_detail.html new file mode 100644 index 0000000..573bec2 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/farerule_detail.html @@ -0,0 +1,17 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - FareRule {{object}}{% endblock %} +{% block page_title %}FareRule {{object}}{% endblock %} +{% block page_top_content_elem %} +A FareRule is the intersection between a Route and a Fare +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
route
Route {{object.route}}
+
fare
Fare {{object.fare}}
+
origin
{% if object.origin_id %}Zone {{object.origin}}{% else %}Not set{% endif %}
+
destination
{% if object.destination_id %}Zone {{object.destination}}{% else %}Not set{% endif %}
+
contains
{% if object.contains_id %}Zone {{object.contains}}{% else %}Not set{% endif %}
+
+{% include "multigtfs/extra_data.html" %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/farerule_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/farerule_list.html new file mode 100644 index 0000000..d514a6b --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/farerule_list.html @@ -0,0 +1,20 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - FareRules{% endblock %} +{% block page_title %}FareRules{% endblock %} +{% block page_top_content_elem %} +A FareRule is the intersection between a Fare and a Route. +{% endblock %} +{% block page_middle_content %} +{% if route %} +

FareRules for Route {{route}}

+{% elif fare %} +

FareRules for Fare {{fare}}

+{% endif %} +{% for farerule in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No farerules yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/feed_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/feed_detail.html new file mode 100644 index 0000000..3e4e6c7 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/feed_detail.html @@ -0,0 +1,25 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Feed {{object}}{% endblock %} +{% block page_title %}Feed {{object}}{% endblock %} +{% block page_top_content_elem %} +A Feed represents a single GTFS feed. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
id
{{object.id}}
+
name
{{object.name}}
+
created
{{object.created}}
+
+

Related Objects

+ +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/feed_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/feed_list.html new file mode 100644 index 0000000..382f8dc --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/feed_list.html @@ -0,0 +1,20 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Feeds{% endblock %} +{% block nav_feed_active_class %} class="active"{% endblock %} +{% block page_title %}Feeds{% endblock %} +{% block page_top_content_elem %} +A Feed represents a single GTFS feed. +{% endblock %} +{% block page_middle_content %} +{% for feed in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No feeds yet.

+{% endfor %} +
+

To add a feed, use:

+
./manage.py importgtfs --name "Feed Name" /path/to/feed.zip
+{% endblock %} + diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/feedinfo_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/feedinfo_detail.html new file mode 100644 index 0000000..baca415 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/feedinfo_detail.html @@ -0,0 +1,20 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - FeedInfo {{object}}{% endblock %} +{% block page_title %}FeedInfo {{object}}{% endblock %} +{% block page_top_content_elem %} +FeedInfo contains information about the feed. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
publisher_name
{{object.publisher_name}}
+
publisher_url
{% if object.publisher_url %}{{object.publisher_url}}{% else %}Not set{% endif %}
+
lang
{{object.lang|default:'Not set'}}
+
start_date
{{object.start_date|default:'Not set'}}
+
end_date
{{object.end_date|default:'Not set'}}
+
version
{{object.version|default:'Not set'}}
+
+{% include "multigtfs/extra_data.html" %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/feedinfo_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/feedinfo_list.html new file mode 100644 index 0000000..0074da5 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/feedinfo_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - FeedInfos{% endblock %} +{% block page_title %}FeedInfos{% endblock %} +{% block page_top_content_elem %} +FeedInfo contains information about the feed. +{% endblock %} +{% block page_middle_content %} +{% for feedinfo in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No feedinfos yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/frequency_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/frequency_detail.html new file mode 100644 index 0000000..e0e2d3b --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/frequency_detail.html @@ -0,0 +1,18 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Frequency {{object}}{% endblock %} +{% block page_title %}Frequency {{object}}{% endblock %} +{% block page_top_content_elem %} +A Frequency represents a periodic trip without a set schedule. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
trip
Trip {{object.trip}}
+
id
{{object.id}}
+
start_time
{{object.start_time}}
+
end_time
{{object.end_time}}
+
headway_secs
{{object.headway_secs}}
+
exact_times
{{object.get_exact_times_display|default:'Not set'}} +
+{% include "multigtfs/extra_data.html" %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/frequency_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/frequency_list.html new file mode 100644 index 0000000..5dbf630 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/frequency_list.html @@ -0,0 +1,19 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Frequencies{% endblock %} +{% block page_title %}Frequencies{% endblock %} +{% block page_top_content_elem %} +A Frequency represents a periodic trip without a set schedule. +route runs. +{% endblock %} +{% block page_middle_content %} +{% if trip %} +

Frequencies for Trip {{trip}}

+{% endif %} +{% for frequency in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No frequencies yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/route_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/route_detail.html new file mode 100644 index 0000000..83fd1c1 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/route_detail.html @@ -0,0 +1,57 @@ +{% extends "multigtfs/base.html" %} +{% load staticfiles %} +{% block head_title %}{{ block.super }} - Route {{object}}{% endblock %} + +{% block head_extra %} +{% if object.geometry %} + + +{% endif %} +{% endblock %} + +{% block page_title %}Route {{object}}{% endblock %} + +{% block page_top_content_elem %} +A Route is a named route in the schedule +{% endblock %} + +{% block page_middle_content %} +{% if object.geometry %} +
+{% else %} +

Map

+

+ This route does not have a populated geometry. A route must have at least + one related Trip, and update_geometry() must be run on the Route. +

+{% endif %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
route_id
{{object.route_id}}
+
agency
{% if object.agency_id %}Agency {{object.agency}}{% else %}Not set{% endif %}
+
short_name
{{object.short_name}}
+
long_name
{{object.long_name}}
+
desc
{{object.desc|default:'Not set'}}
+
rtype
{{object.get_rtype_display|default:'Not set'}} +
url
{% if object.url %}{{object.url}}{% else %}Not set{% endif %}
+
color
{{object.color|default:'Not set'}}
+
text_color
{{object.text_color|default:'Not set'}}
+ {% if object.color and object.text_color %}
(Sample Coloring)
{{object.short_name}} {{object.long_name}}
{% endif %} +
+{% include "multigtfs/extra_data.html" %} +

Related Objects

+ +{% endblock %} + +{% block body_script %} +{% if object.geometry %} + +{% endif %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/route_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/route_list.html new file mode 100644 index 0000000..cd0c706 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/route_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Routes{% endblock %} +{% block page_title %}Routes{% endblock %} +{% block page_top_content_elem %} +A Route is named route in the schedule +{% endblock %} +{% block page_middle_content %} +{% for route in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No routes yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/service_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/service_detail.html new file mode 100644 index 0000000..cb6a056 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/service_detail.html @@ -0,0 +1,29 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Service {{object}}{% endblock %} +{% block page_title %}Service {{object}}{% endblock %} +{% block page_top_content_elem %} +A Service is one or more days with the same schedule. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
service_id
{{object.service_id|default:'Not set'}}
+
monday
{{object.monday}}
+
tuesday
{{object.tuesday}}
+
wednesday
{{object.wednesday}}
+
thursday
{{object.thursday}}
+
friday
{{object.friday}}
+
saturday
{{object.saturday}}
+
sunday
{{object.sunday}}
+
start_date
{{object.start_date}}
+
end_date
{{object.end_date}}
+
+{% include "multigtfs/extra_data.html" %} +

Related Objects

+ +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/service_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/service_list.html new file mode 100644 index 0000000..1b67b4d --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/service_list.html @@ -0,0 +1,16 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Services{% endblock %} +{% block page_title %}Services{% endblock %} +{% block page_top_content_elem %} +A Service is one or more days of the week that a scheduled +route runs. +{% endblock %} +{% block page_middle_content %} +{% for service in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No services yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/servicedate_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/servicedate_detail.html new file mode 100644 index 0000000..293e454 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/servicedate_detail.html @@ -0,0 +1,16 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Trip {{object}}{% endblock %} +{% block page_title %}Trip {{object}}{% endblock %} +{% block page_top_content_elem %} +A ServiceDate is an exception to a Service's routine schedule +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
service
Service {{object.service}}
+
id
{{object.id}}
+
date
{{object.date}}
+
exception_type
{{object.get_exception_type_display|default:'Not set'}}
+
+{% include "multigtfs/extra_data.html" %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/servicedate_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/servicedate_list.html new file mode 100644 index 0000000..c877ab9 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/servicedate_list.html @@ -0,0 +1,16 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - ServiceDates{% endblock %} +{% block page_title %}ServiceDates{% endblock %} +{% block page_top_content_elem %} +A ServiceDate is an exception to a Service's routine schedule +{% endblock %} +{% block page_middle_content %} +

ServiceDates for Service {{service}}

+{% for servicedate in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No servicedates yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/shape_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/shape_detail.html new file mode 100644 index 0000000..9562163 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/shape_detail.html @@ -0,0 +1,48 @@ +{% extends "multigtfs/base.html" %} +{% load staticfiles %} +{% block head_title %}{{ block.super }} - Shape {{object}}{% endblock %} + +{% block head_extra %} +{% if object.geometry %} + + +{% endif %} +{% endblock %} + +{% block page_title %}Shape {{object}}{% endblock %} + +{% block page_top_content_elem %} +A Shape is the path a vehicle travels, on the road or on a track. +{% endblock %} + +{% block page_middle_content %} +{% if object.geometry %} +
+{% else %} +

Map

+

+ This shape does not have a populated geometry. A shape must have at + least 2 ShapePoints, and update_geometry() must be run on the shape. +

+{% endif %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
shape_id
{{object.shape_id|default:'Not set'}}
+
+{% include "multigtfs/extra_data.html" %} +

Related Objects

+ +{% endblock %} + +{% block body_script %} +{% if object.geometry %} + +{% endif %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/shape_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/shape_list.html new file mode 100644 index 0000000..2b5f38e --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/shape_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Shapes{% endblock %} +{% block page_title %}Shapes{% endblock %} +{% block page_top_content_elem %} +A Shape is the path a vehicle travels, on the road or on a track. +{% endblock %} +{% block page_middle_content %} +{% for shape in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No shapes yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/shapepoint_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/shapepoint_detail.html new file mode 100644 index 0000000..9aad541 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/shapepoint_detail.html @@ -0,0 +1,28 @@ +{% extends "multigtfs/base.html" %} +{% load staticfiles %} +{% block head_title %}{{ block.super }} - ShapePoint {{object}}{% endblock %} +{% block head_extra %} + + +{% endblock %} +{% block page_title %}ShapePoint {{object}}{% endblock %} +{% block page_top_content_elem %} +A ShapePoint is a point of a shape +{% endblock %} +{% block page_middle_content %} +
+

Attributes

+
+
shape
Shape {{object.shape}}
+
id
{{object.id}}
+
point
{{object.point}}
+
sequence
{{object.sequence}}
+
traveled
{{object.traveled}}
+
+{% include "multigtfs/extra_data.html" %} +{% endblock %} +{% block body_script %} + +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/shapepoint_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/shapepoint_list.html new file mode 100644 index 0000000..abcada8 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/shapepoint_list.html @@ -0,0 +1,18 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - ShapePoints{% endblock %} +{% block page_title %}ShapePoints{% endblock %} +{% block page_top_content_elem %} +A ShapePoint is a point of a shape +{% endblock %} +{% block page_middle_content %} +{% if shape %} +

ShapePoints for Shape {{shape}}

+{% endif %} +{% for shapepoint in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No shapepoints yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/stop_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/stop_detail.html new file mode 100644 index 0000000..2203778 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/stop_detail.html @@ -0,0 +1,40 @@ +{% extends "multigtfs/base.html" %} +{% load staticfiles %} +{% block head_title %}{{ block.super }} - Stop {{object}}{% endblock %} +{% block head_extra %} + + +{% endblock %} +{% block page_title %}Stop {{object}}{% endblock %} +{% block page_top_content_elem %} +A Stop is a stop or station in the schedule. +{% endblock %} +{% block page_middle_content %} +
+

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
stop_id
{{object.stop_id|default:'Not set'}}
+
code
{{object.code|default:'Not set'}}
+
name
{{object.name}}
+
desc
{{object.desc|default:'Not set'}}
+
point
{{object.point}}
+
zone
{% if object.zone_id %}Zone {{object.zone}}{% else %}No Zone{% endif %}
+
url
{% if object.url %}{{object.url}}{% else %}Not set{% endif %}
+
location_type
{{object.get_location_type_display|default:'Not set'}}
+
parent_station
{% if object.parent_station_id %}Parent Station {{object.name}}{% else %}No Parent Station{% endif %}
+
timezone
{{object.timezone|default:'Not set'}}
+
wheelchair_boarding
{{object.get_wheelchair_boarding_type_display|default:'Not set'}}
+
+{% include "multigtfs/extra_data.html" %} +

Related Objects

+ +{% endblock %} +{% block body_script %} + +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/stop_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/stop_list.html new file mode 100644 index 0000000..148b753 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/stop_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Stops{% endblock %} +{% block page_title %}Stops{% endblock %} +{% block page_top_content_elem %} +A Stop is a stop or station in the schedule. +{% endblock %} +{% block page_middle_content %} +{% for stop in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No stops yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/stoptime_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/stoptime_detail.html new file mode 100644 index 0000000..8aaf4ab --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/stoptime_detail.html @@ -0,0 +1,22 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - StopTime {{object}}{% endblock %} +{% block page_title %}StopTime {{object}}{% endblock %} +{% block page_top_content_elem %} +A StopTime is the intersection between a Trip and a Stop. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
trip
Trip {{object.trip}}
+
stop
Stop {{object.stop}}
+
id
{{object.id}}
+
arrival_time
{{object.arrival_time|default:'Not set'}}
+
departure_time
{{object.departure_time|default:'Not set'}}
+
stop_sequence
{{object.stop_sequence}}
+
stop_headsign
{{object.stop_headsign|default:'Not set'}}
+
pickup_type
{{object.get_pickup_type_display|default:'Not set'}} +
dropoff_type
{{object.get_dropoff_type_display|default:'Not set'}} +
shape_dist_traveled
{{object.shape_dist_traveled|default:'Not set'}}
+
+{% include "multigtfs/extra_data.html" %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/stoptime_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/stoptime_list.html new file mode 100644 index 0000000..fb873c9 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/stoptime_list.html @@ -0,0 +1,20 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - StopTimes{% endblock %} +{% block page_title %}StopTimes{% endblock %} +{% block page_top_content_elem %} +A StopTime is the intersection between a Trip and a Stop. +{% endblock %} +{% block page_middle_content %} +{% if stop %} +

StopTimes for Stop {{stop}}

+{% elif trip %} +

StopTimes for Trip {{trip}}

+{% endif %} +{% for stoptime in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No stoptimes yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/trip_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/trip_detail.html new file mode 100644 index 0000000..39895d3 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/trip_detail.html @@ -0,0 +1,57 @@ +{% extends "multigtfs/base.html" %} +{% load staticfiles %} +{% block head_title %}{{ block.super }} - Trip {{object}}{% endblock %} + +{% block head_extra %} +{% if object.geometry %} + + +{% endif %} +{% endblock %} + +{% block page_title %}Trip {{object}}{% endblock %} + +{% block page_top_content_elem %} +A Trip is a single run of a Route +{% endblock %} + +{% block page_middle_content %} +{% if object.geometry %} +
+{% else %} +

Map

+

+ This trip does not have a populated geometry. A trip must have a + related Shape or at least 2 StopTrips, and update_geometry() must + be run on the Trip. +

+{% endif %} +

Attributes

+
+
route
Route {{object.route}}
+
service
Service {{object.service}}
+
id
{{object.id}}
+
trip_id
{{object.trip_id|default:'Not set'}}
+
headsign
{{object.headsign|default:'Not set'}}
+
short_name
{{object.short_name|default:'Not set'}}
+
direction
{{object.get_direction_display|default:'Not set'}}
+
block
{% if object.block_id %}Block {{object.block}}{% else %}No Block{% endif %}
+
shape
{% if object.shape_id %}Shape {{object.shape}}{% else %}No Shape{% endif %}
+
wheelchair_accessible
{{object.get_wheelchair_accessible_display|default:'Not set'}}
+
bikes_allowed
{{object.get_bikes_allowed_display|default:'Not set'}}
+
+{% include "multigtfs/extra_data.html" %} +

Related Objects

+ +{% endblock %} + +{% block body_script %} +{% if object.geometry %} + +{% endif %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/trip_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/trip_list.html new file mode 100644 index 0000000..6221e90 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/trip_list.html @@ -0,0 +1,24 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Trips{% endblock %} +{% block page_title %}Trips{% endblock %} +{% block page_top_content_elem %} +A Trip is a single run of a Route +{% endblock %} +{% block page_middle_content %} +{% if route %} +

Trips for Route {{route}}

+{% elif service %} +

Trips for Service {{service}}

+{% elif shape %} +

Trips for Shape {{shape}}

+{% elif the_block %} +

Trips for Block {{the_block}}

+{% endif %} +{% for trip in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No trips yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/zone_detail.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/zone_detail.html new file mode 100644 index 0000000..d22b0dc --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/zone_detail.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Zone {{object}}{% endblock %} +{% block page_title %}Zone {{object}}{% endblock %} +{% block page_top_content_elem %} +A Zone is an area with the same fare rules. +{% endblock %} +{% block page_middle_content %} +

Attributes

+
+
feed
Feed {{object.feed}}
+
id
{{object.id}}
+
zone_id
{{object.zone_id}}
+
+{% include "multigtfs/extra_data.html" %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/templates/multigtfs/zone_list.html b/examples/gtfsproj/gtfsapp/templates/multigtfs/zone_list.html new file mode 100644 index 0000000..b1e7b35 --- /dev/null +++ b/examples/gtfsproj/gtfsapp/templates/multigtfs/zone_list.html @@ -0,0 +1,15 @@ +{% extends "multigtfs/base.html" %} +{% block head_title %}{{ block.super }} - Zones{% endblock %} +{% block page_title %}Zones{% endblock %} +{% block page_top_content_elem %} +A Zone is an area with the same fare rules. +{% endblock %} +{% block page_middle_content %} +{% for zone in object_list %} + {% if forloop.first %}{% endif %} +{% empty %} +

No zones yet.

+{% endfor %} +{% endblock %} diff --git a/examples/gtfsproj/gtfsapp/tests.py b/examples/gtfsproj/gtfsapp/tests.py new file mode 100644 index 0000000..5982e6b --- /dev/null +++ b/examples/gtfsproj/gtfsapp/tests.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.test import TestCase + +# Create your tests here. diff --git a/examples/gtfsproj/gtfsapp/urls.py b/examples/gtfsproj/gtfsapp/urls.py new file mode 100644 index 0000000..4760a3d --- /dev/null +++ b/examples/gtfsproj/gtfsapp/urls.py @@ -0,0 +1,62 @@ +from django.conf.urls import url, include + +from gtfsapp.views import ( + FeedList, FeedInfoList, FeedAgencyList, FeedRouteList, + FeedStopList, FeedServiceList, FeedShapeList, FeedFareList, + FeedZoneList, + + FeedDetail, FeedInfoDetail, AgencyDetail, + RouteDetail, TripDetail, StopDetail, StopTimeDetail, + ServiceDetail, ShapeDetail, ShapePointDetail, FareDetail, + ZoneDetail, ServiceDateDetail, + + StopTimeByStopList, FareRuleByRouteList, TripByRouteList, + + FrequencyByTripList, StopTimeByTripList, + + TripByShapeList, ShapePointByShapeList, + + ServiceDateByServiceList, TripByServiceList, +) + +urlpatterns = [ + url(r'^$', FeedList.as_view(), name='feed_list'), + url(r'feed/(?P\d+)/feedinfo/$', FeedInfoList.as_view(), name='feedinfo_list'), + url(r'feed/(?P\d+)/agency/$', FeedAgencyList.as_view(), name='agency_list'), + url(r'feed/(?P\d+)/route/$', FeedRouteList.as_view(), name='route_list'), + url(r'feed/(?P\d+)/stop/$', FeedStopList.as_view(), name='stop_list'), + url(r'feed/(?P\d+)/service/$', FeedServiceList.as_view(), name='service_list'), + url(r'feed/(?P\d+)/shape/$', FeedShapeList.as_view(), name='shape_list'), + url(r'feed/(?P\d+)/fare/$', FeedFareList.as_view(), name='fare_list'), + url(r'feed/(?P\d+)/zone/$', FeedZoneList.as_view(), name='zone_list'), + + url(r'^feed/(?P\d+)/$', FeedDetail.as_view(), name='feed_detail'), + url(r'feed/(?P\d+)/feedinfo/(?P\d+)/$', FeedInfoDetail.as_view(), name='feedinfo_detail'), + url(r'feed/(?P\d+)/agency/(?P\d+)/$', AgencyDetail.as_view(), name='agency_detail'), + url(r'feed/(?P\d+)/route/(?P\d+)/$', RouteDetail.as_view(), name='route_detail'), + url(r'feed/(?P\d+)/trip/(?P\d+)/$', TripDetail.as_view(), name='trip_detail'), + url(r'feed/(?P\d+)/stop/(?P\d+)/$', StopDetail.as_view(), name='stop_detail'), + url(r'feed/(?P\d+)/stoptime/(?P\d+)/$', StopTimeDetail.as_view(), name='stoptime_detail'), + url(r'feed/(?P\d+)/service/(?P\d+)/$', ServiceDetail.as_view(), name='service_detail'), + url(r'feed/(?P\d+)/shape/(?P\d+)/$', ShapeDetail.as_view(), name='shape_detail'), + url(r'feed/(?P\d+)/shapepoint/(?P\d+)$', ShapePointDetail.as_view(), name='shapepoint_detail'), + url(r'feed/(?P\d+)/servicedate/(?P\d+)/$', ServiceDateDetail.as_view(), name='servicedate_detail'), + + url(r'feed/(?P\d+)/fare/(?P\d+)/$', FareDetail.as_view(), name='fare_detail'), + url(r'feed/(?P\d+)/zone/(?P\d+)/$', ZoneDetail.as_view(), name='zone_detail'), + + url(r'feed/(?P\d+)/route/(?P\d+)/farerule/$', FareRuleByRouteList.as_view(), name='farerule_by_route_list'), + url(r'feed/(?P\d+)/route/(?P\d+)/trip/$', TripByRouteList.as_view(), name='trip_by_route_list'), + + url(r'feed/(?P\d+)/trip/(?P\d+)/frequency/$', FrequencyByTripList.as_view(), name='frequency_by_trip_list'), + url(r'feed/(?P\d+)/trip/(?P\d+)/stoptime/$', StopTimeByTripList.as_view(), name='stoptime_by_trip_list'), + + url(r'feed/(?P\d+)/stop/(?P\d+)/stoptime/$', StopTimeByStopList.as_view(), name='stoptime_by_stop_list'), + + + url(r'feed/(?P\d+)/shape/(?P\d+)/shapepoint/$', ShapePointByShapeList.as_view(), name='shapepoint_by_shape_list'), + url(r'feed/(?P\d+)/shape/(?P\d+)/trip/$', TripByShapeList.as_view(), name='trip_by_shape_list'), + + url(r'feed/(?P\d+)/service/(?P\d+)/servicedate/$', ServiceDateByServiceList.as_view(), name='servicedate_by_service_list'), + url(r'feed/(?P\d+)/service/(?P\d+)/trip/$', TripByServiceList.as_view(), name='trip_by_service_list'), ] + diff --git a/examples/gtfsproj/gtfsapp/views.py b/examples/gtfsproj/gtfsapp/views.py new file mode 100644 index 0000000..ee7337d --- /dev/null +++ b/examples/gtfsproj/gtfsapp/views.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.shortcuts import render +from django.views.generic import ListView, DetailView + +from multigtfs.models import ( + Feed, FeedInfo, Agency, Route, Trip, Service, Stop, + Shape, ShapePoint, Fare, Zone, StopTime, FareRule, Frequency, + ServiceDate +) + +# List feed +class FeedList(ListView): + queryset = Feed.objects.order_by('name', '-created') + +# Filter lists by feed +class ByFeedListView(ListView): + by_col = 'feed_id' + by_kwarg = 'feed_id' + by_class = Feed + by_classname = 'feed' + + def get_context_data(self, **kwargs): + context = super(ByFeedListView, self).get_context_data( + **kwargs) + context[self.by_classname] = self.by_class.objects.get( + id=self.kwargs[self.by_kwarg]) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + q_filter = {self.by_col: self.kwargs[self.by_kwarg]} + qset = super(ByFeedListView, self).get_queryset(**kwargs) + return qset.filter(**q_filter) + + +# List +class FeedInfoList(ByFeedListView): + model = FeedInfo + +class FeedAgencyList(ByFeedListView): + model = Agency + +class FeedStopList(ByFeedListView): + model = Stop + +class FeedRouteList(ByFeedListView): + model = Route + +class FeedShapeList(ByFeedListView): + model = Shape + +class FeedServiceList(ByFeedListView): + model = Service + +class FeedZoneList(ByFeedListView): + model = Zone + +class FeedFareList(ByFeedListView): + model = Fare + + +class StopTimeByStopList(ListView): + model = StopTime + + def get_context_data(self, **kwargs): + context = super(StopTimeByStopList, self).get_context_data( + **kwargs) + context['stop'] = Stop.objects.get(id=self.kwargs['stop_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return StopTime.objects.filter(stop_id=self.kwargs['stop_id']) + + +# Detail +class FeedDetail(DetailView): + model = Feed + +class FeedInfoDetail(DetailView): + model = FeedInfo + +class AgencyDetail(DetailView): + model = Agency + +class StopDetail(DetailView): + model = Stop + +class StopTimeDetail(DetailView): + model = StopTime + +class RouteDetail(DetailView): + model = Route + +class TripDetail(DetailView): + model = Trip + +class ShapeDetail(DetailView): + model = Shape + +class ShapePointDetail(DetailView): + model = ShapePoint + +class ServiceDetail(DetailView): + model = Service + +class ZoneDetail(DetailView): + model = Zone + +class FareDetail(DetailView): + model = Fare + +class ServiceDateDetail(DetailView): + model = ServiceDate + + + +class TripByRouteList(ListView): + model = Trip + + def get_context_data(self, **kwargs): + context = super(TripByRouteList, self).get_context_data(**kwargs) + context['route'] = Route.objects.get(id=self.kwargs['route_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return Trip.objects.filter(route_id=self.kwargs['route_id']) + + +class FareRuleByRouteList(ListView): + model = FareRule + + def get_context_data(self, **kwargs): + context = super(FareRuleByRouteList, self).get_context_data( + **kwargs) + context['route'] = Route.objects.get(id=self.kwargs['route_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return FareRule.objects.filter(route_id=self.kwargs['route_id']) + + +class StopTimeByTripList(ListView): + model = StopTime + + def get_context_data(self, **kwargs): + context = super(StopTimeByTripList, self).get_context_data( + **kwargs) + context['trip'] = Trip.objects.get(id=self.kwargs['trip_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return StopTime.objects.filter(trip=self.kwargs['trip_id']) + + +class FrequencyByTripList(ListView): + model = Frequency + + def get_context_data(self, **kwargs): + context = super(FrequencyByTripList, self).get_context_data( + **kwargs) + context['trip'] = Trip.objects.get(id=self.kwargs['trip_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return Frequency.objects.filter(trip=self.kwargs['trip_id']) + + + +class TripByShapeList(ListView): + model = ShapePoint + + def get_context_data(self, **kwargs): + context = super(TripByShapeList, self).get_context_data(**kwargs) + context['shape'] = Shape.objects.get(id=self.kwargs['shape_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return Trip.objects.filter(shape=self.kwargs['shape_id']) + +class ShapePointByShapeList(ListView): + model = ShapePoint + + def get_context_data(self, **kwargs): + context = super(ShapePointByShapeList, self).get_context_data( + **kwargs) + context['shape'] = Shape.objects.get(id=self.kwargs['shape_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return ShapePoint.objects.filter(shape=self.kwargs['shape_id']) + +class ServiceDateByServiceList(ListView): + model = ServiceDate + + def get_context_data(self, **kwargs): + context = super(ServiceDateByServiceList, self).get_context_data( + **kwargs) + context['service'] = Service.objects.get(id=self.kwargs['service_id']) + return context + + def get_queryset(self, **kwargs): + return ServiceDate.objects.filter(service=self.kwargs['service_id']) + +class TripByServiceList(ListView): + model = Trip + + def get_context_data(self, **kwargs): + context = super(TripByServiceList, self).get_context_data(**kwargs) + context['service'] = Service.objects.get(id=self.kwargs['service_id']) + context['feed_id'] = self.kwargs['feed_id'] + return context + + def get_queryset(self, **kwargs): + return Trip.objects.filter(service=self.kwargs['service_id']) diff --git a/examples/gtfsproj/gtfsproj/__init__.py b/examples/gtfsproj/gtfsproj/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/gtfsproj/gtfsproj/settings.py b/examples/gtfsproj/gtfsproj/settings.py new file mode 100644 index 0000000..e486728 --- /dev/null +++ b/examples/gtfsproj/gtfsproj/settings.py @@ -0,0 +1,126 @@ +""" +Django settings for gtfsproj project. + +Generated by 'django-admin startproject' using Django 1.11.5. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os +import dj_database_url +from decouple import config, Csv + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = config('SECRET_KEY', default='+$8pgzf)luxklr(rhzg4!$6+b^2hbw*-frvh_2-7an9-_==n_u', cast=str) + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = config('DEBUG', default=False, cast=bool) +ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv()) + + +# Application definition +# NB new apps need to be placed first for their templates +# to override subsequent apps +INSTALLED_APPS = [ + 'gtfsapp', + 'multigtfs', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.gis', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'gtfsproj.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'gtfsproj.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': config('DATABASE_URL', + default='spatialite:///%s' % os.path.join(BASE_DIR, 'db.sqlite3'), + cast=dj_database_url.parse) +} + + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = config('LANGUAGE_CODE', default='en-us') + +TIME_ZONE = config('TIME_ZONE', default='UTC') + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.11/howto/static-files/ + +STATIC_URL = config('STATIC_URL', default='/static/') +STATIC_ROOT = config('STATIC_ROOT', default='/var/www/multigtfs/static') diff --git a/examples/gtfsproj/gtfsproj/urls.py b/examples/gtfsproj/gtfsproj/urls.py new file mode 100644 index 0000000..e057e9e --- /dev/null +++ b/examples/gtfsproj/gtfsproj/urls.py @@ -0,0 +1,22 @@ +"""gtfsproj URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import include, url +from django.contrib import admin + +urlpatterns = [ + url(r'^admin/', admin.site.urls), + url(r'', include('gtfsapp.urls')), +] diff --git a/examples/gtfsproj/gtfsproj/wsgi.py b/examples/gtfsproj/gtfsproj/wsgi.py new file mode 100644 index 0000000..278abd7 --- /dev/null +++ b/examples/gtfsproj/gtfsproj/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for gtfsproj project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gtfsproj.settings") + +application = get_wsgi_application() diff --git a/examples/gtfsproj/manage.py b/examples/gtfsproj/manage.py new file mode 100755 index 0000000..9d97264 --- /dev/null +++ b/examples/gtfsproj/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gtfsproj.settings") + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) + raise + execute_from_command_line(sys.argv) diff --git a/examples/gtfsproj/requirements.txt b/examples/gtfsproj/requirements.txt new file mode 100644 index 0000000..0f82aa8 --- /dev/null +++ b/examples/gtfsproj/requirements.txt @@ -0,0 +1,16 @@ +# Install multigtfs from PyPi +multigtfs +# Or from local source project +#-e ../.. + +# Configure the database from the environment +dj-database-url + +# Configure other settings from the environment +python-decouple + +# Connect to postgresql database +psycopg2 + +# Install gunicorn server +gunicorn diff --git a/examples/gtfsproj/run_project.sh b/examples/gtfsproj/run_project.sh new file mode 100644 index 0000000..652be41 --- /dev/null +++ b/examples/gtfsproj/run_project.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +# Create environment variables if not exist +ENVIRONMENT_FILE=".env" +if [ ! -f $ENVIRONMENT_FILE ]; then + echo "Create default parameters using $ENVIRONMENT_FILE file" + cp .env.example $ENVIRONMENT_FILE +fi + +# If no arguments provided then run server on port 8000 +if [ $# -eq 0 ]; then + + # If no arguments provided then run server on port 8000 + echo "Run development server on port 8000" + exec ./manage.py runserver 0.0.0.0:8000 + +elif [ "$1" == "gunicorn" ]; then + + echo "Run gunicorn server on port 8000" + exec gunicorn --bind 0.0.0.0:8000 gtfsproj.wsgi + +else + + exec "$@" + +fi diff --git a/multigtfs/templates/admin/delete_confirmation.html b/multigtfs/templates/admin/delete_confirmation.html new file mode 100644 index 0000000..403f560 --- /dev/null +++ b/multigtfs/templates/admin/delete_confirmation.html @@ -0,0 +1,50 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls static %} + +{% block extrahead %} + {{ block.super }} + {{ media }} + +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +{% if perms_lacking %} +

{% blocktrans with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}

+
    + {% for obj in perms_lacking %} +
  • {{ obj }}
  • + {% endfor %} +
+{% elif protected %} +

{% blocktrans with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would require deleting the following protected related objects:{% endblocktrans %}

+
    + {% for obj in protected %} +
  • {{ obj }}
  • + {% endfor %} +
+{% else %} +

{% blocktrans with escaped_object=object %}Are you sure you want to delete the {{ object_name }} "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktrans %}

+ {% include "admin/includes/object_delete_summary.html" %} +
{% csrf_token %} +
+ + {% if is_popup %}{% endif %} + {% if to_field %}{% endif %} + + {% trans "No, take me back" %} +
+
+{% endif %} +{% endblock %} diff --git a/multigtfs/templates/admin/delete_selected_confirmation.html b/multigtfs/templates/admin/delete_selected_confirmation.html new file mode 100644 index 0000000..3af3112 --- /dev/null +++ b/multigtfs/templates/admin/delete_selected_confirmation.html @@ -0,0 +1,51 @@ +{% extends "admin/base_site.html" %} +{% load i18n l10n admin_urls static %} + +{% block extrahead %} + {{ block.super }} + {{ media }} + +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +{% if perms_lacking %} +

{% blocktrans %}Deleting the selected {{ objects_name }} would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}

+
    + {% for obj in perms_lacking %} +
  • {{ obj }}
  • + {% endfor %} +
+{% elif protected %} +

{% blocktrans %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktrans %}

+
    + {% for obj in protected %} +
  • {{ obj }}
  • + {% endfor %} +
+{% else %} +

{% blocktrans %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktrans %}

+ {% include "admin/includes/object_delete_summary.html" %} +
{% csrf_token %} +
+ {% for obj in queryset %} + + {% endfor %} + + + + {% trans "No, take me back" %} +
+
+{% endif %} +{% endblock %}