summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2015-11-06 14:41:58 -0500
committerluxagraf <sng@luxagraf.net>2015-11-06 14:41:58 -0500
commit78dc05f455f234da32c8c4d60b1b074755758d1c (patch)
treeb72a1cfb93d5b0315a5e16f1338f8a6793b21242
parent2311ea934932cf791a83f6c6264063a26468e98c (diff)
rewrote jrnl builder to be self contained in jrnl and based entirely off
url patterns/ views, refactored URLs/views to be class based and fixed a few django warning in URL files
-rw-r--r--app/books/urls.py2
-rw-r--r--app/builder/base.py118
-rw-r--r--app/jrnl/build.py48
-rw-r--r--app/jrnl/models.py2
-rw-r--r--app/jrnl/urls.py16
-rw-r--r--app/jrnl/views.py33
-rw-r--r--app/photos/urls.py4
-rw-r--r--design/templates/archives/writing_date.html2
-rw-r--r--design/templates/details/entry.amp175
9 files changed, 384 insertions, 16 deletions
diff --git a/app/books/urls.py b/app/books/urls.py
index 003d6d6..3efd730 100644
--- a/app/books/urls.py
+++ b/app/books/urls.py
@@ -5,5 +5,5 @@ urlpatterns = patterns('',
#url(r'(?P<slug>[-\w]+)/(?P<page>\d+)/$', 'blog.views.entry_list_by_area'),
url(r'(?P<page>\d+)/$', 'books.views.book_list'),
url(r'(?P<slug>[-\w]+)/$', 'books.views.book_detail'),
- url(r'^$', RedirectView.as_view(url="/books/1/")),
+ url(r'^$', RedirectView.as_view(url="/books/1/", permanent=False)),
)
diff --git a/app/builder/base.py b/app/builder/base.py
index 62cfad0..ded8bba 100644
--- a/app/builder/base.py
+++ b/app/builder/base.py
@@ -4,12 +4,128 @@ from decimal import Decimal
from django.test.client import Client
from django.template.loader import render_to_string
from django.template import Context
+from django.core.urlresolvers import reverse
from django.apps import apps
from django.conf import settings
+from jsmin import jsmin
+
+
+class _FileWriter(object):
+ """
+ Given a path and text object; write the page to disc
+ """
+ def __init__(self, path, text_object, ext='html', filename='index', base_path=settings.NFLATFILES_ROOT):
+ self.path = '%s%s' % (base_path, path)
+ if not os.path.isdir(self.path):
+ os.makedirs(self.path)
+ fpath = '%s%s.%s' % (self.path, filename, ext)
+ self.write(fpath, text_object)
+ if ext == 'js':
+ self.compress_js(filename, text_object)
+
+ def write(self, fpath, text_object):
+ f = open(fpath, 'wb')
+ f.write(text_object)
+ f.close()
+
+ def compress_js(self, filename, text_object):
+ path = '%s%s.min.js' % (self.path, filename)
+ compressed = jsmin(text_object.decode(encoding='UTF-8'))
+ self.write(path, compressed)
+
+
+class BuildNew():
+ def __init__(self, model, app):
+ self.model = apps.get_model(model, app)
+ self.get_model_queryset()
+ self.client = Client()
+
+ def build(self):
+ self.build_list_view()
+ self.build_detail_view()
+
+ def get_model_queryset(self):
+ return self.model.objects.filter(status__exact=1)
+
+ def write_file(self, path, text_object, ext='html', filename='index'):
+ self.writer = _FileWriter(path, text_object, ext=ext, filename=filename)
+
+ def get_pages(self, qs, paginate_by):
+ return int(ceil(Decimal(qs.count()) / Decimal(paginate_by)))
+
+ def build_list_view(self, base_path='', qs=None, paginate_by=10):
+ """
+ Archive Page builder that actually crawls the urls
+ because we need to be able to pass a request object to the template
+ """
+ if not qs:
+ qs = self.get_model_queryset()
+ pages = self.get_pages(qs, paginate_by)
+ for page in range(pages):
+ if int(pages) > 1:
+ path = '%s%s/' % (base_path, str(page + 1))
+ url = '%s%s/' % (base_path, str(page + 1))
+ else:
+ path = base_path
+ url = base_path
+ response = self.client.get(url, HTTP_HOST='127.0.0.1')
+ if page == 0:
+ self.write_file(base_path, response.content)
+ self.write_file(path, response.content)
+
+ def build_year_view(self, url, paginate_by=99999):
+ years = self.model.objects.dates('pub_date', 'year')
+ for year in years:
+ year = year.strftime('%Y')
+ qs = self.model.objects.filter(
+ status__exact=1,
+ pub_date__year=year
+ )
+ self.build_list_view(
+ base_path=reverse(url, kwargs={'year': year, }),
+ qs=qs,
+ paginate_by=paginate_by
+ )
+
+ def build_month_view(self, url, paginate_by=99999):
+ months = self.model.objects.dates('pub_date', 'month')
+ for m in months:
+ year = m.strftime('%Y')
+ month = m.strftime('%m')
+ qs = self.model.objects.filter(
+ status__exact=1,
+ pub_date__year=year,
+ pub_date__month=month
+ )
+ if qs.exists():
+ self.build_list_view(
+ base_path=reverse(url, kwargs={'year': year, 'month': month}),
+ qs=qs,
+ paginate_by=paginate_by
+ )
+
+ def build_detail_view(self):
+ '''
+ Grab all the blog posts, render them to a template
+ string and write that out to the filesystem
+ '''
+ for entry in self.get_model_queryset():
+ url = entry.get_absolute_url()
+ path, slug = os.path.split(entry.get_absolute_url())
+ path = '%s/' % path
+ # write html
+ response = self.client.get(url)
+ self.write_file(path, response.content, filename=slug)
+ # write txt
+ response = self.client.get('%s.txt' % url)
+ self.write_file(path, response.content, ext='txt', filename=slug)
+ # write AMP
+ response = self.client.get('%s.amp' % url)
+ self.write_file(path, response.content, ext='amp', filename=slug)
-#from pages.models import PageGenerator
class Build():
+
def write_file(self, path, text_object, ext='html', filename='index'):
"""
Given a path and object intended to be a webpage, write the page the
diff --git a/app/jrnl/build.py b/app/jrnl/build.py
new file mode 100644
index 0000000..504e288
--- /dev/null
+++ b/app/jrnl/build.py
@@ -0,0 +1,48 @@
+from django.core.urlresolvers import reverse
+from django.apps import apps
+from builder.base import BuildNew
+from itertools import chain
+
+
+class BuildJrnl(BuildNew):
+
+ def build(self):
+ self.build_list_view(
+ base_path=reverse("jrnl:live_redirect"),
+ paginate_by=24
+ )
+ self.build_year_view("jrnl:list_year")
+ self.build_month_view("jrnl:list_month")
+ self.build_detail_view()
+ self.build_location_view()
+
+ def build_location_view(self):
+ c = apps.get_model('locations', 'Country')
+ r = apps.get_model('locations', 'Region')
+ countries = c.objects.filter(visited=True)
+ regions = r.objects.all()
+ locations = list(chain(countries, regions))
+ for c in locations:
+ try:
+ qs = self.model.objects.filter(
+ status__exact=1,
+ location__state__country=c
+ )
+ except:
+ qs = self.model.objects.filter(
+ status__exact=1,
+ location__state__country__lux_region=c.id
+ )
+ pages = self.get_pages(qs, 24)
+ for page in range(pages):
+ base_path = reverse("jrnl:list_country", kwargs={'slug': c.slug, 'page': page + 1})
+ response = self.client.get(base_path, HTTP_HOST='127.0.0.1')
+ if page == 0:
+ self.write_file(base_path[:-2], response.content)
+ else:
+ self.write_file(base_path, response.content)
+
+
+def builder():
+ j = BuildJrnl("jrnl", "entry")
+ j.build()
diff --git a/app/jrnl/models.py b/app/jrnl/models.py
index fe7f274..2f84798 100644
--- a/app/jrnl/models.py
+++ b/app/jrnl/models.py
@@ -31,7 +31,7 @@ def image_url_replace(s):
def extract_images(s):
- soup = BeautifulSoup(s)
+ soup = BeautifulSoup(s, "lxml")
imgs = []
for img in soup.find_all('img'):
imgs.append(img['src'])
diff --git a/app/jrnl/urls.py b/app/jrnl/urls.py
index d8fa395..2b17033 100644
--- a/app/jrnl/urls.py
+++ b/app/jrnl/urls.py
@@ -5,12 +5,22 @@ from . import views
urlpatterns = [
url(
+ regex=r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+).txt$',
+ view=views.EntryDetailViewTXT.as_view(),
+ name="detail-txt"
+ ),
+ url(
+ regex=r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+).amp$',
+ view=views.EntryDetailViewAMP.as_view(),
+ name="detail-amp"
+ ),
+ url(
regex=r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+)$',
view=views.EntryDetailView.as_view(),
name="detail"
),
url(
- regex=r'^(?P<year>[0-9]{4})/(?P<month>[0-9]+)/$',
+ regex=r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$',
view=views.EntryMonthArchiveView.as_view(month_format='%m'),
name="list_month"
),
@@ -32,13 +42,13 @@ urlpatterns = [
# redirect /slug/ to /slug/1/ for live server
url(
regex=r'(?P<slug>[-\w]+)/$',
- view=RedirectView.as_view(url="/jrnl/%(slug)s/1/"),
+ view=RedirectView.as_view(url="/jrnl/%(slug)s/1/", permanent=False),
name="live_location_redirect"
),
# redirect / to /1/ for live server
url(
regex=r'',
- view=RedirectView.as_view(url="/jrnl/1/"),
+ view=RedirectView.as_view(url="/jrnl/1/", permanent=False),
name="live_redirect"
),
]
diff --git a/app/jrnl/views.py b/app/jrnl/views.py
index 7dd755b..634a375 100644
--- a/app/jrnl/views.py
+++ b/app/jrnl/views.py
@@ -6,7 +6,7 @@ from django.conf import settings
from .models import Entry, HomepageCurrator
-from locations.models import Country
+from locations.models import Country, Region
class EntryList(ListView):
@@ -38,15 +38,26 @@ class EntryCountryList(ListView):
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(EntryCountryList, self).get_context_data(**kwargs)
- context['region'] = Country.objects.get(slug__exact=self.kwargs['slug'])
+ try:
+ context['region'] = Country.objects.get(slug__exact=self.kwargs['slug'])
+ except:
+ context['region'] = Region.objects.get(slug__exact=self.kwargs['slug'])
return context
def get_queryset(self):
- country = Country.objects.get(slug__exact=self.kwargs['slug'])
- return Entry.objects.filter(
- status__exact=1,
- location__state__country=country
- ).order_by('-pub_date')
+ try:
+ region= Country.objects.get(slug__exact=self.kwargs['slug'])
+ qs = Entry.objects.filter(
+ status__exact=1,
+ location__state__country=region
+ ).order_by('-pub_date')
+ except:
+ region = Region.objects.get(slug__exact=self.kwargs['slug'])
+ qs = Entry.objects.filter(
+ status__exact=1,
+ location__state__country__lux_region=region.id
+ ).order_by('-pub_date')
+ return qs
class EntryYearArchiveView(YearArchiveView):
@@ -70,6 +81,14 @@ class EntryDetailView(DetailView):
slug_field = "slug"
+class EntryDetailViewTXT(EntryDetailView):
+ template_name = "details/entry.txt"
+
+
+class EntryDetailViewAMP(EntryDetailView):
+ template_name = "details/entry.amp"
+
+
class HomepageList(ListView):
"""
Return a main entry and list of Entries in reverse chronological order
diff --git a/app/photos/urls.py b/app/photos/urls.py
index 9ae065d..91aac41 100644
--- a/app/photos/urls.py
+++ b/app/photos/urls.py
@@ -6,6 +6,6 @@ urlpatterns = patterns('',
(r'data/(?P<slug>[-\w]+)/$', 'photos.views.photo_json'),
(r'galleries/(?P<slug>[-\w]+)/$', 'photos.views.gallery'),
(r'(?P<page>\d+)/$', 'photos.views.gallery_list'),
- (r'(?P<slug>[-\w]+)/$', RedirectView.as_view(url="/photos/%(slug)s/1/")),
- (r'', RedirectView.as_view(url="/photos/1/")),
+ (r'(?P<slug>[-\w]+)/$', RedirectView.as_view(url="/photos/%(slug)s/1/", permanent=False)),
+ (r'', RedirectView.as_view(url="/photos/1/", permanent=False)),
)
diff --git a/design/templates/archives/writing_date.html b/design/templates/archives/writing_date.html
index 7385e81..4091e74 100644
--- a/design/templates/archives/writing_date.html
+++ b/design/templates/archives/writing_date.html
@@ -13,7 +13,7 @@
</ul>
<main role="main" id="writing-archive" class="archive">
<h1>{% if not month %}{{year|date:"Y"}}, on luxagraf{%else%} Archive: {{month|date:"F Y"}}{% endif %}</h1>{% if not month %}
- <ul class="date-archive">{% regroup object_list by pub_date.month as entries_by_month %}{% for entries in entries_by_month %}
+ <ul class="date-archive">{% regroup object_list by pub_date.month as entries_by_month %}{% for entries in entries_by_month reversed %}
<li class="dater"><span>{{ entries.list.0.pub_date|date:"F Y" }}</span>
<ul>{% for post in entries.list %}
<li class="arc-item">
diff --git a/design/templates/details/entry.amp b/design/templates/details/entry.amp
new file mode 100644
index 0000000..6305d46
--- /dev/null
+++ b/design/templates/details/entry.amp
@@ -0,0 +1,175 @@
+{% load typogrify_tags %}
+<!doctype html>
+<html amp lang="en">
+<head>
+<meta charset="utf-8">
+<title>{%block pagetitle%}{{object.title}}{%endblock%}</title>
+<link rel="canonical" href="{{object.get_absolute_url}}">
+ <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
+ <meta name="twitter:card" content="summary_large_image"/>
+ <meta name="twitter:url" content="{{object.get_absolute_url}}">
+ <meta name="twitter:description" content="{{object.meta_description}}"/>
+ <meta name="twitter:title" content="{{object.title|safe}}"/>
+ <meta name="twitter:site" content="@luxagraf"/>
+ <meta name="twitter:domain" content="luxagraf"/>
+ <meta name="twitter:image:src" content="{{object.get_image_url}}"/>
+ <meta name="twitter:creator" content="@luxagraf"/>
+ <meta name="twitter:site:id" content="9469062">
+ <meta name="twitter:creator:id" content="9469062">
+ <meta name="twitter:description" content=""/>
+
+ <meta name="geo.placename" content="{% if object.country.name == "United States" %}{{object.location.name|smartypants|safe}}, {{object.state.name}}{%else%}{{object.location.name|smartypants|safe}}, {{object.country.name}}{%endif%}">
+ <meta name="geo.region" content="{{object.country.iso2}}{%if object.state.code != '' %}-{{object.state.code}}{%endif%}">
+ <meta property="og:type" content="article" />
+ <meta property="og:title" content="{{object.title|safe}}" />
+ <meta property="og:url" content="https://luxagraf.net{{object.get_absolute_url}}" />
+ <meta property="og:description" content="{{object.meta_description}}" />
+ <meta property="article:published_time" content="{{object.pub_date|date:'c'}}" />
+ <meta property="article:author" content="Luxagraf" />
+ <meta property="og:site_name" content="Luxagraf" />
+ <meta property="og:image" content="{{object.get_image_url}}" />{% for image in object.get_images %}
+ <meta property="og:image" content="{{image}}" />{% endfor %}
+ <meta property="og:locale" content="en_US" />
+
+
+<script type="application/ld+json">
+{
+ "@context": "http://schema.org",
+ "@type": "BlogPosting",
+ "headline": "{{object.title|safe}}",
+ "description": "{{object.meta_description}}",
+ "datePublished": "{{object.pub_date|date:"c"}}",
+ "author": {
+ "@type": "Person",
+ "name": "Scott Gilbertson"
+ },
+ "publisher": {
+ "@type": "Person",
+ "name": "Jeremy Keith",
+ "name": "Scott Gilbertson"
+ "logo": {
+ "@type": "ImageObject",
+ "url": "",
+ "width": 240,
+ "height": 53
+ }
+ }
+}
+</script>
+<style amp-custom>
+body {
+ font-size: 0.8125rem;
+ line-height: 1.73;
+ font-family: "Lucida Grande",Verdana,Helvetica,Arial,sans-serif;
+ background-color: #fff;
+ color: #333;
+ padding: 1em;
+}
+nav {
+ text-align: right;
+ max-width: 60em;
+ margin: 0 auto;
+}
+main {
+ max-width: 60em;
+ margin: 0 auto;
+}
+main footer {
+ text-align: right;
+}
+a {
+ text-decoration: underline;
+ color: #c63;
+}
+h1,h2,h3,h4,h5 {
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+ line-height: 1;
+ font-weight: bold;
+}
+h1 {
+ font-size: 1.5rem;
+ color: #666;
+}
+h2 {
+ font-size: 1.125rem;
+ color: #555;
+}
+h3 {
+ font-size: 1rem;
+ color: #444;
+}
+h4 {
+ font-size: 0.875rem;
+}
+h5 {
+ font-size: 0.75rem;
+}
+h1 a, h1 a *,
+h2 a, h2 a *,
+h3 a, h3 a *,
+h4 a, h4 a * {
+ text-decoration: none;
+ font-weight: bold
+}
+code {
+ font-size: 0.875rem;
+ font-family: "Courier New",monospace;
+}
+pre {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+blockquote {
+ font-family: Georgia,"Times New Roman",serif;
+ font-size: 0.875rem;
+}
+blockquote * {
+ font-style: italic;
+}
+blockquote * em {
+ font-weight: bold;
+}
+blockquote * strong {
+ font-style: normal;
+}
+hr {
+ border: none;
+ border-bottom: 0.0625rem dotted #ccc;
+}
+</style>
+<script async custom-element="amp-audio" src="https://cdn.ampproject.org/v0/amp-audio-0.1.js"></script>
+<script async custom-element="amp-iframe" src="https://cdn.ampproject.org/v0/amp-iframe-0.1.js"></script>
+<style>body {opacity: 0}</style><noscript><style>body {opacity: 1}</style></noscript>
+<script async src="https://cdn.ampproject.org/v0.js"></script>
+</head>
+<body>
+
+<nav>
+<a href="https://luxagraf.net/">
+<amp-img src="https://adactio.com/skins/default/images/logo.png" srcset="https://adactio.com/skins/default/images/logox2.png 2x" alt="adactio" width="240" height="53" layout="fixed"></amp-img>
+</a>
+</nav>
+
+<main class="h-entry">
+ <article class="h-entry hentry post--article{% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %} post--article--double{%endif%}{%endwith%}" itemscope itemType="http://schema.org/Article">
+ <header id="header" class="post--header {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %}post--header--double{%endif%}{%endwith%}">
+ <h1 class="p-name entry-title post--title" itemprop="headline">{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|smartypants|safe}}{%else%}{{object.title|smartypants|widont|safe}}{%endif%}</h1>
+ <time class="dt-published published dt-updated post--date" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, Y"}}</span></time>
+ <p class="p-author author hide" itemprop="author"><span class="byline-author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">Scott Gilbertson</span></span></p>
+ <aside class="p-location h-adr adr post--location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place">
+ {% if object.country.name == "United States" %}<span class="p-locality locality">{{object.location.name|smartypants|safe}}</span>, <a class="p-region region" href="/jrnl/united-states/" title="travel writing from the United States">{{object.state.name|safe}}</a>, <span class="p-country-name">U.S.</span>{%else%}<span class="p-region">{{object.location.name|smartypants|safe}}</span>, <a class="p-country-name country-name" href="/jrnl/{{object.country.slug}}/" title="travel writing from {{object.country.name}}">{{object.country.name|safe}}</a>{%endif%}
+ <span style="display: none;" itemprop="geo" itemscope itemtype="http://schema.org/GeoCoordinates">
+ <data itemprop="latitude" class="p-latitude" value="{{object.latitude}}">{{object.latitude}}</data>
+ <data itemprop="longitude" class="p-longitude" value="{{object.longitude}}">{{object.longitude}}</data>
+ </span>
+ {% with object.get_template_name_display as t %}{%if t == "single" or t == "single-dark" %} &ndash;&nbsp;<a href="" onclick="showMap({{object.latitude}}, {{object.longitude}}, { type:'point', lat:'{{object.latitude}}', lon:'{{object.longitude}}'}); return false;" title="see a map">Map</a>{%endif%}{%endwith%}
+ </aside>
+ </header>
+ <div id="article" class="e-content entry-content post--body post--body--{% with object.template_name as t %}{%if t == 0 or t == 2 %}single{%endif%}{%if t == 1 or t == 3 %}double{%endif%}{%endwith%}" itemprop="articleBody">
+ {{object.body_html|safe|smartypants|widont}}
+ </div>
+ </article>
+</main>
+
+</body>
+</html>