commit
128f67f0e1
19 changed files with 227 additions and 40 deletions
|
@ -1,4 +1,6 @@
|
|||
FROM python:3.6
|
||||
FROM python:3.6.3
|
||||
|
||||
RUN apt-get update && apt-get upgrade -y
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
@ -10,4 +12,3 @@ ENV PYTHONUNBUFFERED 1
|
|||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV PYTHONIOENCODING utf_8
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
release_dir: /opt/publichealth
|
||||
archive_dir: /opt/www-old
|
||||
django_log_dir: /var/log/publichealth
|
||||
redis_data_dir: /opt/redis/data
|
||||
ipv4_addresses: "{{ ansible_all_ipv4_addresses }}"
|
||||
environment:
|
||||
COMPOSE_FILE: "{{ release_dir }}/docker-compose.yml"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"dependencies": {
|
||||
"bootstrap-sass": "bootstrap-sass-official#^3.3.7",
|
||||
"slick-carousel": "^1.4.1",
|
||||
"slick-lightbox": "^0.2.10"
|
||||
"slick-lightbox": "^0.2.10",
|
||||
"cookieconsent": "cookieconsent2#^3.0.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
"bower": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "^1.0.1",
|
||||
"grunt": "^1.0.2",
|
||||
"grunt-bg-shell": "^2.3.3",
|
||||
"grunt-bowercopy": "^1.2.4",
|
||||
"grunt-browser-sync": "^2.2.0",
|
||||
"grunt-contrib-imagemin": "^1.0.1",
|
||||
"grunt-contrib-imagemin": "^2.0.1",
|
||||
"grunt-contrib-sass": "^1.0.0",
|
||||
"grunt-contrib-watch": "^1.0.0"
|
||||
"grunt-contrib-watch": "^1.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
|
|
63
publichealth/home/migrations/0022_auto_20180525_1520.py
Normal file
63
publichealth/home/migrations/0022_auto_20180525_1520.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.13 on 2018-05-25 13:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import wagtail.wagtailcore.blocks
|
||||
import wagtail.wagtailcore.fields
|
||||
import wagtail.wagtailimages.blocks
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0021_auto_20171013_2321'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='articleindexpage',
|
||||
name='intro_en',
|
||||
field=wagtail.wagtailcore.fields.RichTextField(blank=True, default=''),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='articleindexpage',
|
||||
name='title_en',
|
||||
field=models.CharField(blank=True, default='', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='articlepage',
|
||||
name='body_en',
|
||||
field=wagtail.wagtailcore.fields.StreamField((('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('section', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('info', wagtail.wagtailcore.blocks.StructBlock((('title', wagtail.wagtailcore.blocks.CharBlock(required=True)), ('photo', wagtail.wagtailimages.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.wagtailcore.blocks.RichTextBlock(required=True)), ('action', wagtail.wagtailcore.blocks.CharBlock(required=False)), ('url', wagtail.wagtailcore.blocks.URLBlock(required=False))), icon='help')), ('media', wagtail.wagtailcore.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))), blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='articlepage',
|
||||
name='intro_en',
|
||||
field=wagtail.wagtailcore.fields.RichTextField(blank=True, default=''),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='articlepage',
|
||||
name='title_en',
|
||||
field=models.CharField(blank=True, default='', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contact',
|
||||
name='title_en',
|
||||
field=models.CharField(default='', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='body_en',
|
||||
field=wagtail.wagtailcore.fields.RichTextField(blank=True, default=''),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='infos_en',
|
||||
field=wagtail.wagtailcore.fields.StreamField((('info', wagtail.wagtailcore.blocks.StructBlock((('title', wagtail.wagtailcore.blocks.CharBlock(required=True)), ('photo', wagtail.wagtailimages.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.wagtailcore.blocks.RichTextBlock(required=True)), ('action', wagtail.wagtailcore.blocks.CharBlock(required=False)), ('url', wagtail.wagtailcore.blocks.URLBlock(required=False))))),), blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='intro_en',
|
||||
field=wagtail.wagtailcore.fields.RichTextField(blank=True, default=''),
|
||||
),
|
||||
]
|
|
@ -31,16 +31,20 @@ class InfoBlock(StructBlock):
|
|||
|
||||
class ArticleIndexPage(Page):
|
||||
title_fr = models.CharField(max_length=255, default="")
|
||||
title_en = models.CharField(max_length=255, default="", blank=True)
|
||||
trans_title = TranslatedField(
|
||||
'title',
|
||||
'title_fr',
|
||||
'title_en',
|
||||
)
|
||||
|
||||
intro_de = RichTextField(default='', blank=True)
|
||||
intro_fr = RichTextField(default='', blank=True)
|
||||
intro_en = RichTextField(default='', blank=True)
|
||||
trans_intro = TranslatedField(
|
||||
'intro_de',
|
||||
'intro_fr',
|
||||
'intro_en',
|
||||
)
|
||||
|
||||
feed_image = models.ForeignKey(
|
||||
|
@ -54,6 +58,8 @@ class ArticleIndexPage(Page):
|
|||
FieldPanel('intro_de'),
|
||||
FieldPanel('title_fr'),
|
||||
FieldPanel('intro_fr'),
|
||||
FieldPanel('title_en'),
|
||||
FieldPanel('intro_en'),
|
||||
ImageChooserPanel('feed_image'),
|
||||
]
|
||||
|
||||
|
@ -87,16 +93,20 @@ class ImageCarouselBlock(StructBlock):
|
|||
|
||||
class ArticlePage(Page):
|
||||
title_fr = models.CharField(max_length=255, default="")
|
||||
title_en = models.CharField(max_length=255, default="", blank=True)
|
||||
trans_title = TranslatedField(
|
||||
'title',
|
||||
'title_fr',
|
||||
'title_en',
|
||||
)
|
||||
|
||||
intro_de = RichTextField(default='', blank=True)
|
||||
intro_fr = RichTextField(default='', blank=True)
|
||||
intro_en = RichTextField(default='', blank=True)
|
||||
trans_intro = TranslatedField(
|
||||
'intro_de',
|
||||
'intro_fr',
|
||||
'intro_en',
|
||||
)
|
||||
|
||||
gallery = StreamField([
|
||||
|
@ -122,9 +132,18 @@ class ArticlePage(Page):
|
|||
('gallery', 'Image gallery'),
|
||||
], icon='media'))
|
||||
], null=True, blank=True)
|
||||
body_en = StreamField([
|
||||
('paragraph', RichTextBlock()),
|
||||
('section', CharBlock(classname="full title")),
|
||||
('info', InfoBlock(icon='help')),
|
||||
('media', ChoiceBlock(choices=[
|
||||
('gallery', 'Image gallery'),
|
||||
], icon='media'))
|
||||
], null=True, blank=True)
|
||||
trans_body = TranslatedField(
|
||||
'body_de',
|
||||
'body_fr',
|
||||
'body_en',
|
||||
)
|
||||
|
||||
date = models.DateField("Date", null=True, blank=True)
|
||||
|
@ -142,10 +161,13 @@ class ArticlePage(Page):
|
|||
search_fields = Page.search_fields + [
|
||||
index.SearchField('title', partial_match=True, boost=10),
|
||||
index.SearchField('title_fr', partial_match=True, boost=10),
|
||||
index.SearchField('title_en', partial_match=True, boost=10),
|
||||
index.SearchField('body_de', partial_match=True),
|
||||
index.SearchField('body_fr', partial_match=True),
|
||||
index.SearchField('body_en', partial_match=True),
|
||||
index.SearchField('intro_de', partial_match=True),
|
||||
index.SearchField('intro_fr', partial_match=True),
|
||||
index.SearchField('intro_en', partial_match=True),
|
||||
]
|
||||
content_panels = [
|
||||
MultiFieldPanel([
|
||||
|
@ -158,6 +180,11 @@ class ArticlePage(Page):
|
|||
FieldPanel('intro_fr'),
|
||||
], heading="Français"),
|
||||
StreamFieldPanel('body_fr'),
|
||||
MultiFieldPanel([
|
||||
FieldPanel('title_en'),
|
||||
FieldPanel('intro_en'),
|
||||
], heading="English"),
|
||||
StreamFieldPanel('body_en'),
|
||||
MultiFieldPanel([
|
||||
ImageChooserPanel('feed_image'),
|
||||
], heading="Images"),
|
||||
|
@ -192,16 +219,20 @@ class ArticleRelatedLink(Orderable):
|
|||
class HomePage(Page):
|
||||
intro_de = RichTextField(default='')
|
||||
intro_fr = RichTextField(default='')
|
||||
intro_en = RichTextField(default='', blank=True)
|
||||
trans_intro = TranslatedField(
|
||||
'intro_de',
|
||||
'intro_fr',
|
||||
'intro_en',
|
||||
)
|
||||
|
||||
body_de = RichTextField(default='', blank=True)
|
||||
body_fr = RichTextField(default='', blank=True)
|
||||
body_en = RichTextField(default='', blank=True)
|
||||
trans_body = TranslatedField(
|
||||
'body_de',
|
||||
'body_fr',
|
||||
'body_en',
|
||||
)
|
||||
|
||||
infos_de = StreamField([
|
||||
|
@ -210,9 +241,13 @@ class HomePage(Page):
|
|||
infos_fr = StreamField([
|
||||
('info', InfoBlock())
|
||||
], null=True, blank=True)
|
||||
infos_en = StreamField([
|
||||
('info', InfoBlock())
|
||||
], null=True, blank=True)
|
||||
trans_infos = TranslatedField(
|
||||
'infos_de',
|
||||
'infos_fr',
|
||||
'infos_en',
|
||||
)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
|
@ -226,13 +261,19 @@ class HomePage(Page):
|
|||
FieldPanel('body_fr', classname="full"),
|
||||
StreamFieldPanel('infos_fr'),
|
||||
], heading="Français"),
|
||||
MultiFieldPanel([
|
||||
FieldPanel('intro_en', classname="full"),
|
||||
FieldPanel('body_en', classname="full"),
|
||||
StreamFieldPanel('infos_en'),
|
||||
], heading="English"),
|
||||
]
|
||||
|
||||
@property
|
||||
def featured(self):
|
||||
# Get list of live pages that are descendants of this page
|
||||
articles = ArticlePage.objects.live() #.descendant_of(self)
|
||||
articles = ArticlePage.objects.live().descendant_of(self)
|
||||
articles = articles.filter(on_homepage=True)
|
||||
articles = articles.filter(feed_image__isnull=False)
|
||||
# Order by most recent date first
|
||||
#articles = articles.order_by('-date')
|
||||
return articles[:4]
|
||||
|
@ -241,7 +282,7 @@ class HomePage(Page):
|
|||
def blogentries(self):
|
||||
# Get list of latest news
|
||||
curlang = translation.get_language()
|
||||
if not curlang in ['de', 'fr']: curlang = 'de' # Default language
|
||||
if not curlang in ['de', 'fr', 'en', 'it']: curlang = 'de' # Default language
|
||||
parent = BlogPage.objects.filter(slug='news-%s' % curlang)
|
||||
if not parent: return []
|
||||
posts = EntryPage.objects.live().descendant_of(parent[0])
|
||||
|
@ -257,8 +298,9 @@ class HomePage(Page):
|
|||
curlang = translation.get_language()
|
||||
if curlang in ['de']:
|
||||
entries = entries.exclude(lang='fr')
|
||||
elif curlang in ['fr']:
|
||||
else:
|
||||
entries = entries.exclude(lang='de')
|
||||
# TODO: English news?
|
||||
news = events = jobs = []
|
||||
Stream1 = Stream.objects.filter(title='News')
|
||||
if Stream1: news = entries.filter(stream=Stream1)
|
||||
|
|
|
@ -52,9 +52,11 @@ class Contact(models.Model):
|
|||
"""
|
||||
title = models.CharField(max_length=255, default="")
|
||||
title_fr = models.CharField(max_length=255, default="")
|
||||
title_en = models.CharField(max_length=255, default="")
|
||||
trans_title = TranslatedField(
|
||||
'title',
|
||||
'title_fr',
|
||||
'title_en',
|
||||
)
|
||||
address = models.TextField(default="", blank=True)
|
||||
phone = models.CharField(max_length=40, default="")
|
||||
|
@ -75,6 +77,7 @@ class Contact(models.Model):
|
|||
|
||||
panels = Page.content_panels + [
|
||||
FieldPanel('title_fr'),
|
||||
FieldPanel('title_en'),
|
||||
FieldPanel('address'),
|
||||
FieldPanel('phone'),
|
||||
FieldPanel('email'),
|
||||
|
|
|
@ -9,15 +9,23 @@ register = template.Library()
|
|||
def language_switcher(context):
|
||||
url = '/$lang$'
|
||||
if 'page' in context:
|
||||
url = context['page'].url.split('/')
|
||||
if len(url) > 2 and len(url[1]) >= 2:
|
||||
url[1] = '$lang$'
|
||||
url = '/'.join(url)
|
||||
urlparts = context['page'].get_url_parts()
|
||||
if urlparts is not None:
|
||||
s, r, page_url_relative_to_site_root = urlparts
|
||||
url = page_url_relative_to_site_root.split('/')
|
||||
if len(url) > 0 and len(url[1]) == 2:
|
||||
url[1] = '$lang$'
|
||||
url = '/'.join(url)
|
||||
else:
|
||||
url = '/$lang$'
|
||||
language_array = [
|
||||
{ 'code': 'de', 'title': 'De', 'url': url.replace('$lang$','de') },
|
||||
{ 'code': 'fr', 'title': 'Fr', 'url': url.replace('$lang$','fr') }
|
||||
]
|
||||
if context['page'].get_site().root_page.slug == "sphc":
|
||||
language_array.append({ 'code': 'en', 'title': 'En', 'url': url.replace('$lang$','en') })
|
||||
return {
|
||||
'languages': [
|
||||
{ 'code': 'de', 'title': 'De', 'url': url.replace('$lang$','de') },
|
||||
{ 'code': 'fr', 'title': 'Fr', 'url': url.replace('$lang$','fr') }
|
||||
],
|
||||
'languages': language_array,
|
||||
'currentlangcode': translation.get_language(),
|
||||
'request': context['request'],
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
from django.utils import translation
|
||||
|
||||
class TranslatedField(object):
|
||||
def __init__(self, de_field, fr_field):
|
||||
def __init__(self, de_field, fr_field, en_field):
|
||||
self.de_field = de_field
|
||||
self.fr_field = fr_field
|
||||
self.en_field = en_field
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if translation.get_language() == 'fr':
|
||||
return getattr(instance, self.fr_field)
|
||||
elif translation.get_language() == 'en':
|
||||
return getattr(instance, self.en_field)
|
||||
else:
|
||||
return getattr(instance, self.de_field)
|
||||
|
|
|
@ -146,6 +146,7 @@ PASSWORD_REQUIRED_TEMPLATE = 'password.html'
|
|||
LANGUAGES = (
|
||||
('de', u'Deutsch'),
|
||||
('fr', u'Français'),
|
||||
('en', u'English'),
|
||||
)
|
||||
LANGUAGE_CODE = 'de' # default language
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ TEMPLATES[0]['OPTIONS']['debug'] = True
|
|||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'CHANGEME!!!'
|
||||
|
||||
# SECURITY WARNING: CAREFUL! your dev site is open to the world
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
INTERNAL_IPS = ('127.0.0.1', '10.0.2.2')
|
||||
|
||||
BASE_URL = 'http://localhost:8000'
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
@import 'paper/variables';
|
||||
@import '../libs/bootstrap-sass/assets/stylesheets/bootstrap';
|
||||
@import '../libs/cookieconsent/build/cookieconsent.min.css';
|
||||
@import 'paper/bootswatch';
|
||||
|
||||
// Modules
|
||||
|
|
|
@ -18,6 +18,11 @@ footer#footer {
|
|||
color: white;
|
||||
text-align: center;
|
||||
font-size: 70%;
|
||||
|
||||
a {
|
||||
color: lighten($brand-secondary, 25%);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
article footer .btn {
|
||||
|
|
31
publichealth/static/css/sphc.css
Normal file
31
publichealth/static/css/sphc.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
#news, #footer, .contact-nav .link { display: none; }
|
||||
|
||||
.navbar-brand span, .navbar-brand img { display:none; }
|
||||
a.navbar-brand {
|
||||
height: 60px;
|
||||
padding-right: 62px;
|
||||
background: url('/static/images/ssph-logo.jpg') center right no-repeat;
|
||||
background-size: auto 75%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.navbar-brand:after {
|
||||
content: 'Swiss Public Health Conference';
|
||||
}
|
||||
|
||||
#carousel-banner .carousel-caption > * { display: none; }
|
||||
#carousel-banner .carousel-caption {
|
||||
width: 13em;
|
||||
background: rgba(0,0,50,0.4);
|
||||
border: 6px solid white;
|
||||
font-size: 155%;
|
||||
padding: 1em 1em;
|
||||
left: 50%;
|
||||
margin-left: -6.5em;
|
||||
bottom: 2em;
|
||||
}
|
||||
#carousel-banner .carousel-caption:before {
|
||||
content: 'SPHC 2018'; display: block;
|
||||
}
|
||||
#carousel-banner .carousel-caption:after {
|
||||
content: 'Better Health Faster';
|
||||
}
|
BIN
publichealth/static/images/ssph-logo.jpg
Normal file
BIN
publichealth/static/images/ssph-logo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 586 KiB |
|
@ -68,6 +68,25 @@ $(document).ready(function() {
|
|||
nextArrow: '<span class="arrow right glyphicon glyphicon-chevron-right" aria-hidden="true">Next</span>',
|
||||
});
|
||||
|
||||
// Cookie notice via insites.com
|
||||
window.cookieconsent.initialise({
|
||||
"palette": {
|
||||
"popup": {
|
||||
"background": "#edeff5",
|
||||
"text": "#838391"
|
||||
},
|
||||
"button": {
|
||||
"background": "#4b81e8"
|
||||
}
|
||||
},
|
||||
"theme": "edgeless",
|
||||
"content": {
|
||||
"message": "Diese Website verwendet Cookies, um Ihnen eine optimale Nutzung unserer Website zu ermöglichen.",
|
||||
"dismiss": "Zustimmen",
|
||||
"link": "Weitere informationen",
|
||||
"href": "/privacy/"
|
||||
}
|
||||
});
|
||||
|
||||
// Pastel colors on live news
|
||||
// $('.feedpage-body .panel').each(function() {
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
<link rel="stylesheet" type="text/x-scss" href="{% static 'css/main.scss' %}">
|
||||
{% endcompress %}
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
|
@ -34,6 +33,7 @@
|
|||
<script src="{% static 'libs/bootstrap-sass/assets/javascripts/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'libs/slick-carousel/slick/slick.min.js' %}"></script>
|
||||
<script src="{% static 'libs/slick-lightbox/dist/slick-lightbox.js' %}"></script>
|
||||
<script src="{% static 'libs/cookieconsent/build/cookieconsent.min.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% compress js %}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load wagtailcore_tags navigation information %}
|
||||
{% load static wagtailcore_tags navigation information %}
|
||||
{% get_site_root as site_root %}
|
||||
|
||||
<!-- Footer -->
|
||||
|
@ -30,5 +30,11 @@
|
|||
</footer>
|
||||
|
||||
<div class="copyright">
|
||||
© 2017 Public Health Schweiz
|
||||
© 2018 Public Health Schweiz
|
||||
• <a href="/privacy">Privacy Terms</a>
|
||||
• Code by <a href="https://madewithwagtail.org/developers/dataletsch/" target="_blank">Datalets</a>
|
||||
</div>
|
||||
|
||||
{% if site_root.slug == "sphc" %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/sphc.css' %}">
|
||||
{% endif %}
|
||||
|
|
|
@ -1,33 +1,32 @@
|
|||
# Updated: 17.11.2017
|
||||
# Updated: 16.5.2018
|
||||
|
||||
# Core
|
||||
wagtail==1.13.1
|
||||
Django==1.11.7
|
||||
|
||||
# Database
|
||||
psycopg2==2.7.3.2
|
||||
dj-database-url==0.4.2
|
||||
|
||||
# Content
|
||||
puput==0.9.1
|
||||
guess-language-spirit==0.5.3
|
||||
|
||||
# Search
|
||||
wagtail>=1.13.1,<2.0.0
|
||||
Django>=1.11.7,<2.0.0
|
||||
elasticsearch>=2.0.0,<3.0.0
|
||||
|
||||
# Database
|
||||
psycopg2==2.7.4
|
||||
psycopg2-binary==2.7.4
|
||||
dj-database-url==0.5.0
|
||||
|
||||
# Content
|
||||
puput==0.9.2
|
||||
guess-language-spirit==0.5.3
|
||||
|
||||
# Caching
|
||||
django-redis==4.8.0
|
||||
django-redis==4.9.0
|
||||
|
||||
# Frontend
|
||||
django-libsass==0.7
|
||||
libsass==0.13.4
|
||||
Pillow==4.3.0
|
||||
libsass==0.14.5
|
||||
Pillow==5.1.0
|
||||
|
||||
# Development tools
|
||||
stellar==0.4.3
|
||||
stellar==0.4.5
|
||||
|
||||
# Production dependencies
|
||||
gunicorn==19.7.1
|
||||
gunicorn==19.8.1
|
||||
whitenoise==3.3.1
|
||||
ConcurrentLogHandler==0.9.1
|
||||
django-anymail==1.2
|
||||
django-anymail==2.2
|
||||
|
|
Loading…
Add table
Reference in a new issue