Skip to content

Commit

Permalink
Merge branch 'redesign'
Browse files Browse the repository at this point in the history
  • Loading branch information
flibbertigibbet committed Feb 13, 2017
2 parents e147202 + a786c64 commit 9638f2a
Show file tree
Hide file tree
Showing 204 changed files with 10,474 additions and 5,485 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Development Installation
3. Copy `deployment/ansible/group_vars/development_template` to `deployment/ansible/group_vars/development` and edit variables
4. Run `vagrant up`
5. See the app at http://localhost:8024! See OpenTripPlanner at http://localhost:9090.
6. Running `npm run gulp-watch` from `/opt/app/src` will automatically collect static files together when changes are detected for Django template consumption.
6. Running `npm run gulp-watch` from `/opt/app/src` will automatically collect static files together when changes are detected for Django template consumption. Alternatively, `npm run gulp-development` can be run manually whenever changes are made to the static files.

Building AMIs
------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ postgres_host: "192.168.8.25"
django_workers: 5
production: False

app_sass_version: "3.4.9"
app_sass_version: "3.4.22"

app_log: "/var/log/cac-tripplanner-app.log"
app_cron_event_feed_log: "/var/log/event-feed.log"
Expand Down
4 changes: 2 additions & 2 deletions deployment/cac-stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ def main():
default=None,
help='One of "dev", "staging", "prod"')
cac_stacks.add_argument('--stack-color', type=str, required=True,
choices=['green', 'blue'],
choices=['green', 'blue', 'orange'],
default=None,
help='One of "green", "blue"')
help='One of "green", "blue", "orange"')
cac_stacks.set_defaults(func=launch_stacks)

# AMI Management
Expand Down
2 changes: 1 addition & 1 deletion deployment/cloudformation/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def set_up_stack(self):

self.param_color = self.add_parameter(Parameter(
'StackColor', Type='String',
Description='Stack color', AllowedValues=['Blue', 'Green']),
Description='Stack color', AllowedValues=['Blue', 'Green', 'Orange']),
source='StackColor')

self.param_stacktype = self.add_parameter(Parameter(
Expand Down
6 changes: 4 additions & 2 deletions deployment/cloudformation/stacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

stack_colors = {
'green': 'Green',
'blue': 'Blue'
'blue': 'Blue',
'orange': 'Orange'
}


Expand All @@ -39,5 +40,6 @@ def build_stacks(options, stack_type, stack_color):
if not stack_color:
d.go()
else:
OtpServerStack(globalconfig=g, VPC=v, DataPlane=d).go()
if options['StackColor'] != 'Orange':
OtpServerStack(globalconfig=g, VPC=v, DataPlane=d).go()
WebServerStack(globalconfig=g, VPC=v, DataPlane=d).go()
10 changes: 9 additions & 1 deletion python/cac_tripplanner/cac_tripplanner/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,13 @@
["Table", "HorizontalRule"],
["SpecialChar"],
["Source"]
]
],
'extraPlugins': ','.join(
['autolink',
'autoembed',
'embed',
'embedsemantic',
'autogrow']),
}
}

Expand All @@ -230,6 +236,8 @@

# OTP CONFIGURATION
OTP_URL = secrets['otp_url']
ROUTING_URL = OTP_URL.format(router='default') + 'plan'
ISOCHRONE_URL = OTP_URL.format(router='default') + 'isochrone'

# Settings for S3 storage
# No need to specify AWS access and secret keys -- they are pulled from
Expand Down
43 changes: 24 additions & 19 deletions python/cac_tripplanner/cac_tripplanner/urls.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
from django.conf.urls import include, url
from django.views.generic import RedirectView
from django.contrib.gis import admin

from django.contrib.staticfiles import views as staticviews

from cms import views as cms_views
from destinations.views import (FindReachableDestinations, SearchDestinations, FeedEvents,
map as map_view, directions as directions_view)
from destinations import views as dest_views

import settings

urlpatterns = [
# Home
url(r'^$', cms_views.home, name='home'),
# Home view, which is also the directions and explore views
url(r'^$', dest_views.home, name='home'),
url(r'^explore$', dest_views.explore, name='explore'),

# Map
url(r'^api/destinations/search$', SearchDestinations.as_view(), name='api_destinations_search'),
url(r'^api/feedevents$', FeedEvents.as_view(), name='api_feedevents'),
url(r'^map/reachable$', FindReachableDestinations.as_view(), name='reachable'),
url(r'^map/', map_view, name='map'),
url(r'^directions/', directions_view, name='directions'),
url(r'^api/destinations/search$', dest_views.SearchDestinations.as_view(),
name='api_destinations_search'),
url(r'^api/feedevents$', dest_views.FeedEvents.as_view(), name='api_feedevents'),
url(r'^map/reachable$', dest_views.FindReachableDestinations.as_view(), name='reachable'),

# print directions view. TODO: update or delete
# url(r'^directions/', dest_views.directions, name='directions'),

# Handle pre-redesign URLs by redirecting
url(r'^(?:map/)?directions/', RedirectView.as_view(pattern_name='home', query_string=True,
permanent=True)),

# Places
url(r'^place/(?P<pk>[\d-]+)/$', dest_views.place_detail, name='place-detail'),

# About and FAQ
url(r'^info/(?P<slug>[\w-]+)/$', cms_views.about_faq, name='about-faq'),
# About (no more FAQ)
url(r'^(?P<slug>about)/$', cms_views.about_faq, name='about'),

# All Published Articles
url(r'^api/articles$', cms_views.AllArticles.as_view(), name='api_articles'),

# Community Profiles
url(r'^community-profile/(?P<slug>[\w-]+)/$',
cms_views.community_profile_detail,
name='community-profile-detail'),

# Tips and Tricks
url(r'^tips-and-tricks/(?P<slug>[\w-]+)/$',
cms_views.tips_and_tricks_detail,
name='tips-and-tricks-detail'),
url(r'^learn/$', cms_views.learn_list, name='learn-list'),
url(r'^learn/(?P<slug>[\w-]+)/$', cms_views.learn_detail, name='learn-detail'),

# Link Shortening
url(r'^link/', include('shortlinks.urls')),
Expand Down
4 changes: 2 additions & 2 deletions python/cac_tripplanner/cms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class Meta:

def clean_wide_image(self):
"""Custom validator for wide_image field"""
return validate_image(self.cleaned_data.get('wide_image', False), 1440, 400)
return validate_image(self.cleaned_data.get('wide_image', False), 680, 200)

def clean_narrow_image(self):
"""Custom validator for narrow image field"""
return validate_image(self.cleaned_data.get('narrow_image', False), 400, 600)
return validate_image(self.cleaned_data.get('narrow_image', False), 310, 218)
25 changes: 25 additions & 0 deletions python/cac_tripplanner/cms/migrations/0010_auto_20170201_1527.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models
import cms.models


class Migration(migrations.Migration):

dependencies = [
('CMS', '0009_auto_20150515_1248'),
]

operations = [
migrations.AlterField(
model_name='article',
name='narrow_image',
field=models.ImageField(help_text=b'The small image. Will be displayed at 310x218.', null=True, upload_to=cms.models.generate_filename),
),
migrations.AlterField(
model_name='article',
name='wide_image',
field=models.ImageField(help_text=b'The large image. Will be displayed at 680x200.', null=True, upload_to=cms.models.generate_filename),
),
]
11 changes: 3 additions & 8 deletions python/cac_tripplanner/cms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,7 @@ def random(self):
"""Returns a randomized article"""
# Need to use the full object, because there is a magic transformation of the URL
# at some point which is needed for assembling the s3 url.
randomized = self.published().order_by('?')[:1]

if randomized:
return randomized[0]
else:
None
return self.published().order_by('?').first()


class CommunityProfileManager(ArticleManager):
Expand Down Expand Up @@ -92,9 +87,9 @@ class ArticleTypes(object):
modified = models.DateTimeField(auto_now=True)
content_type = models.CharField(max_length=4, choices=ArticleTypes.CHOICES)
wide_image = models.ImageField(upload_to=generate_filename, null=True,
help_text='The wide image. Will be displayed at 1440x400.')
help_text='The large image. Will be displayed at 680x200.')
narrow_image = models.ImageField(upload_to=generate_filename, null=True,
help_text='The narrow image. Will be displayed at 400x600.')
help_text='The small image. Will be displayed at 310x218.')

@property
def published(self):
Expand Down
38 changes: 19 additions & 19 deletions python/cac_tripplanner/cms/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,18 @@ def setUp(self):
**common_args)

def test_home_view(self):
"""Verify that home view includes both tips and a community profile"""
"""Verify that home view includes one article"""

# Delete second published article so random always returns the same item
self.published_tips.delete()

url = reverse('home')
response = self.client.get(url)
self.assertContains(response, self.published_comm.slug, count=2, status_code=200)
self.assertContains(response, self.published_tips.slug, count=2, status_code=200)
self.assertContains(response, 'Places we love', status_code=200)
self.assertContains(response, self.published_comm.title, status_code=200)

def test_community_profile_manager(self):
"""Test that community profile manager works"""
"""Test that article manager properly filters community articles"""
community_profiles_count = Article.profiles.count()
self.assertEqual(community_profiles_count, 2)

Expand All @@ -69,7 +73,7 @@ def test_community_profile_manager(self):
self.assertEqual(random, published)

def test_tips_and_tricks_manager(self):
"""Test that community profile manager works"""
"""Test that article manager properly filters tip articles"""

tips_count = Article.tips.count()
self.assertEqual(tips_count, 2)
Expand All @@ -82,31 +86,27 @@ def test_tips_and_tricks_manager(self):
self.assertEqual(random, published)

def test_article_manager(self):
"""Test that community profile manager works"""
"""Test that article manager filters unpublished articles"""

published_articles_count = Article.objects.published().count()
self.assertEqual(published_articles_count, 2)

def test_community_profile_detail_view(self):
"""Test that community profile detail view works"""
url = reverse('community-profile-detail',
def test_learn_detail_view(self):
"""Test that learn detail view works"""
url = reverse('learn-detail',
kwargs={'slug': self.published_comm.slug})
response = self.client.get(url)
self.assertContains(response, 'published-comm', status_code=200)

url = reverse('community-profile-detail',
url = reverse('learn-detail',
kwargs={'slug': self.unpublished_comm.slug})
response_404 = self.client.get(url)
self.assertEqual(response_404.status_code, 404)

def test_tips_and_tricks_detail_view(self):
"""Test that tips and tricks detail view works"""
url = reverse('tips-and-tricks-detail',
kwargs={'slug': self.published_tips.slug})
def test_learn_list_view(self):
"""Test that learn list view works"""
url = reverse('learn-list')
response = self.client.get(url)
self.assertContains(response, 'published-tips', status_code=200)

url = reverse('tips-and-tricks-detail',
kwargs={'slug': self.unpublished_tips.slug})
response_404 = self.client.get(url)
self.assertEqual(response_404.status_code, 404)
self.assertContains(response, self.published_comm.title, status_code=200)
self.assertContains(response, self.published_tips.title, status_code=200)
92 changes: 32 additions & 60 deletions python/cac_tripplanner/cms/views.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,55 @@
import json
from random import shuffle

from django.conf import settings
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.views.generic import View

from .models import AboutFaq, Article
from destinations.models import Destination
from cac_tripplanner.settings import FB_APP_ID, HOMEPAGE_RESULTS_LIMIT, DEBUG


def home(request):

# get randomized community profile
community_profile = Article.profiles.random()

# get randomized tips and tricks
tips_and_tricks = Article.tips.random()

# get a few randomized destinations
destination_ids = list(Destination.objects.published().values_list('id', flat=True))
shuffle(destination_ids)
destinations = Destination.objects.filter(id__in=destination_ids[:4])

context = dict(community_profile=community_profile,
tips_and_tricks=tips_and_tricks,
destinations=destinations,
fb_app_id=FB_APP_ID,
debug=DEBUG)
return render(request, 'home.html', context=context)

DEFAULT_CONTEXT = {
'debug': settings.DEBUG,
'fb_app_id': settings.FB_APP_ID,
'routing_url': settings.ROUTING_URL
}

def about_faq(request, slug):
page = get_object_or_404(AboutFaq.objects.all(), slug=slug)
context = {'page': page, 'debug': DEBUG}
context = dict(tab='about', page=page, **DEFAULT_CONTEXT)
return render(request, 'about-faq.html', context=context)

def learn_list(request):
articles = Article.objects.published().order_by('-publish_date')
context = dict(tab='info', articles=articles, **DEFAULT_CONTEXT)
return render(request, 'learn-list.html', context=context)

def community_profile_detail(request, slug):
"""Profile/Article view
:param slug: article slug to lookup profile
"""
community_profile = get_object_or_404(Article.profiles.published(),
slug=slug)
context = {'article': community_profile, 'debug': DEBUG}
return render(request, 'community-profile-detail.html', context=context)
def learn_detail(request, slug):
article = get_object_or_404(Article.objects.published(), slug=slug)
more_articles = Article.objects.published().order_by('-publish_date').exclude(pk=article.pk)[:3]
context = dict(tab='info', article=article, more_articles=more_articles, **DEFAULT_CONTEXT)
return render(request, 'learn-detail.html', context=context)


def tips_and_tricks_detail(request, slug):
"""Tips and tricks detail view
:param slug: article slug to lookup tips and tricks
"""
tips_and_tricks = get_object_or_404(Article.tips.published(),
slug=slug)
context = {'article': tips_and_tricks, 'debug': DEBUG}
return render(request, 'tips-and-tricks-detail.html', context=context)

class AllArticles(View):
""" API endpoint for the Articles model """

def get(self, request, *args, **kwargs):
""" GET title, URL, and images for the 20 most recent articles that are published"""
results = Article.objects.published().order_by('-publish_date')[:HOMEPAGE_RESULTS_LIMIT]

# resolve full URLs to articles and their images
response = []
for obj in results:
article = {}
article['wide_image'] = obj.wide_image.url
article['narrow_image'] = obj.narrow_image.url
article['title'] = obj.title
if obj.content_type == 'prof':
relative_url = reverse(community_profile_detail, args=[obj.slug])
else:
relative_url = reverse(tips_and_tricks_detail, args=[obj.slug])
article['url'] = request.build_absolute_uri(relative_url)
response.append(article)
def serialize_article(self, request, article):
return {
'wide_image': article.wide_image.url,
'narrow_image': article.narrow_image.url,
'title': article.title,
'url': request.build_absolute_uri(reverse(learn_detail, args=[article.slug]))
}

def get(self, request, *args, **kwargs):
""" GET title, URL, and images for published articles """
try:
limit = int(request.GET.get('limit'))
except (ValueError, TypeError):
limit = settings.HOMEPAGE_RESULTS_LIMIT

results = Article.objects.published().order_by('-publish_date')[:limit]
response = [self.serialize_article(request, article) for article in results]
return HttpResponse(json.dumps(response), 'application/json')
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/cac_tripplanner/default_media/square/BartramsGarden.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 9638f2a

Please sign in to comment.