diff --git a/.dockerignore b/.dockerignore
index a144add..c52a67f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -3,6 +3,8 @@
settings.pyc
settings_local.py
*.ged
-tmp/
-files/
-data/
+.env
+.tmp/
+.files/
+.data/
+.tmp/
diff --git a/.gitignore b/.gitignore
index eb8d2c2..e18b7e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,10 @@
.DS_Store
*.py[co]
*.zip
+.env
+.tmp/
+.data/
+.files/
# Packages
*.egg
diff --git a/Dockerfile b/Dockerfile
index d7e33e7..1e85bd4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,12 +1,20 @@
-FROM python:2.7-alpine3.7
+FROM python:3.7-alpine3.7
WORKDIR /app/
-COPY ./reqs.frozen.pip /app/
+COPY ./reqs.pip /app/
ENV LIBRARY_PATH=/lib:/usr/lib
-RUN apk --update add jpeg-dev zlib-dev build-base mariadb-dev && \
- pip install -r reqs.frozen.pip && \
- apk add mariadb-client-libs && \
- apk del build-base mariadb-dev
+RUN apk --update add jpeg-dev zlib-dev build-base && \
+ pip install -r reqs.pip && \
+ apk del build-base
+
+# Create a non-root user
+RUN addgroup -S appgroup && adduser -S app -G appgroup
COPY ./ /app/
-RUN mkdir -p /static && python manage.py collectstatic -c --noinput
+RUN mkdir -p /static && \
+ chown app /static /app && \
+ python manage.py collectstatic -c --noinput
+
+USER app
+
+CMD uvicorn --host=0.0.0.0 asgi:application
diff --git a/asgi.py b/asgi.py
new file mode 100644
index 0000000..3c2ab47
--- /dev/null
+++ b/asgi.py
@@ -0,0 +1,8 @@
+import os
+from django.core.asgi import get_asgi_application
+from django_simple_task import django_simple_task_middlware
+
+os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+
+app = get_asgi_application()
+application = django_simple_task_middlware(app)
diff --git a/docker-compose.yml b/docker-compose.yml
index 8e1fc8d..c2def98 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,63 +1,24 @@
version: '2.2'
services:
- # Dev services
- avahi:
- container_name: 'avahi'
- image: 'enernoclabs/avahi:latest'
- network_mode: 'host'
- logging:
- driver: 'none'
- db:
- container_name: 'db'
- image: 'mysql:5.7'
- ports:
- - '3306:3306'
- environment:
- MYSQL_ROOT_PASSWORD: 'docker'
- MYSQL_DATABASE: 'gedgo'
- MYSQL_USER: 'gedgo'
- MYSQL_PASSWORD: 'gedgo'
- volumes:
- - './tmp:/gedgo_tmp'
- logging:
- driver: 'none'
- redis:
- container_name: 'redis'
- image: 'redis'
- ports:
- - '6379'
- logging:
- driver: 'none'
- # Application
app:
build: '.'
image: 'gedgo_app'
+ env_file: '.env'
container_name: 'gedgo_app'
- command: ['python', 'manage.py', 'runserver', '0.0.0.0:8000']
+ # command: ['python', 'manage.py', 'runserver', '0.0.0.0:8000']
+ command: ['uvicorn', '--host=0.0.0.0', '--reload', 'asgi:application']
+ ports:
+ - '8000:8000'
volumes:
- './:/app'
- links:
- - 'db'
- - 'redis'
+ - '.data/:/data'
web:
image: 'nginx:alpine'
command: 'nginx -g "daemon off;"'
volumes:
- './gedgo-web.conf:/etc/nginx/conf.d/default.conf:ro'
- - './files:/src/files:ro'
+ - './.files:/src/files:ro'
ports:
- '80:80'
links:
- 'app'
- worker:
- container_name: 'gedgo_worker'
- image: 'gedgo_app'
- command: ['celery', '-A', 'gedgo.tasks', 'worker', '--loglevel=debug']
- volumes:
- - './:/app'
- links:
- - 'app'
- - 'db'
- - 'redis'
- depends_on:
- - 'app'
diff --git a/gedgo/__init__.py b/gedgo/__init__.py
index 9e11c72..e69de29 100644
--- a/gedgo/__init__.py
+++ b/gedgo/__init__.py
@@ -1,10 +0,0 @@
-from django.conf import settings
-import redis as Redis
-
-redis = None
-if hasattr(settings, 'GEDGO_REDIS_SERVER'):
- try:
- redis = Redis.StrictRedis(host=settings.GEDGO_REDIS_SERVER)
- redis.ping()
- except Exception as e:
- print e
diff --git a/gedgo/gedcom_parser.py b/gedgo/gedcom_parser.py
index 4d051ea..4bf2f82 100644
--- a/gedgo/gedcom_parser.py
+++ b/gedgo/gedcom_parser.py
@@ -17,7 +17,7 @@ class GedcomParser(object):
)
def __init__(self, file_name_or_stream):
- if isinstance(file_name_or_stream, basestring):
+ if isinstance(file_name_or_stream, str):
self.file = open(file_name_or_stream, 'rU')
else:
self.file = file_name_or_stream
@@ -70,9 +70,7 @@ def __parse_element(self, line):
break
# Keep the entry trimmed down
- for key in entry.keys():
- if not entry[key]:
- del entry[key]
+ entry = dict((key, entry[key]) for key in entry.keys() if entry[key])
return tag, entry
diff --git a/gedgo/gedcom_update.py b/gedgo/gedcom_update.py
index a140e6a..4712bbc 100644
--- a/gedgo/gedcom_update.py
+++ b/gedgo/gedcom_update.py
@@ -1,5 +1,5 @@
-from gedcom_parser import GedcomParser
-from models import Gedcom, Person, Family, Note, Document, Event
+from gedgo.gedcom_parser import GedcomParser
+from gedgo.models import Gedcom, Person, Family, Note, Document, Event
from django.core.files.storage import default_storage
from django.db import transaction
@@ -8,15 +8,19 @@
from datetime import datetime
from re import findall
from os import path
+import sys
from gedgo.storages import gedcom_storage, resize_thumb
+# For logging
+sys.stdout = sys.stderr
+
@transaction.atomic
def update(g, file_name, verbose=True):
# Prevent circular dependencies
if verbose:
- print 'Parsing content'
+ print('Parsing content')
parsed = GedcomParser(file_name)
if g is None:
@@ -24,11 +28,11 @@ def update(g, file_name, verbose=True):
title=__child_value_by_tags(parsed.header, 'TITL', default=''),
last_updated=datetime(1920, 1, 1) # TODO: Fix.
)
- if verbose:
- print 'Gedcom id=%s' % g.id
if verbose:
- print 'Importing entries to models'
+ print('Gedcom id=%s' % g.id)
+ print('Importing entries to models')
+
person_counter = family_counter = note_counter = 0
entries = parsed.entries.values()
for index, entry in enumerate(entries):
@@ -45,26 +49,29 @@ def update(g, file_name, verbose=True):
note_counter += 1
if verbose and (index + 1) % 100 == 0:
- print ' ... %d / %d' % (index + 1, len(entries))
+ print(' ... %d / %d' % (index + 1, len(entries)))
if verbose:
- print 'Found %d people, %d families, %d notes, and %d documents' % (
+ print('Found %d people, %d families, %d notes, and %d documents' % (
person_counter, family_counter, note_counter,
- Document.objects.count())
+ Document.objects.count()))
if verbose:
- print 'Creating ForeignKey links'
+ print('Creating ForeignKey links')
__process_all_relations(g, parsed, verbose)
g.last_updated = timezone.now()
g.save()
+ if verbose:
+ print('Done updating gedcom')
+
# --- Second Level script functions
def __process_all_relations(gedcom, parsed, verbose=True):
if verbose:
- print ' Starting Person objects.'
+ print(' Starting Person objects.')
# Process Person objects
for index, person in enumerate(gedcom.person_set.iterator()):
@@ -75,7 +82,7 @@ def __process_all_relations(gedcom, parsed, verbose=True):
else:
person.delete()
if verbose:
- print ' Finished Person objects, starting Family objects.'
+ print(' Finished Person objects, starting Family objects.')
# Process Family objects
for family in gedcom.family_set.iterator():
@@ -86,7 +93,7 @@ def __process_all_relations(gedcom, parsed, verbose=True):
else:
family.delete()
if verbose:
- print ' Finished Family objects.'
+ print(' Finished Family objects.')
def __process_person_relations(gedcom, person, entry):
@@ -94,7 +101,7 @@ def __process_person_relations(gedcom, person, entry):
notes = gedcom.note_set
# "FAMS"
- person.spousal_families = []
+ person.spousal_families.clear()
person.spousal_families.add(
*__objects_from_entry_tag(families, entry, 'FAMS')
)
@@ -106,7 +113,7 @@ def __process_person_relations(gedcom, person, entry):
person.child_family = child_family[0]
# "NOTE"
- person.notes = []
+ person.notes.clear()
person.notes.add(*__objects_from_entry_tag(notes, entry, 'NOTE'))
person.save()
@@ -117,19 +124,19 @@ def __process_family_relations(gedcom, family, entry):
notes = gedcom.note_set
# "HUSB"
- family.husbands = []
+ family.husbands.clear()
family.husbands.add(*__objects_from_entry_tag(people, entry, 'HUSB'))
# "WIFE"
- family.wives = []
+ family.wives.clear()
family.wives.add(*__objects_from_entry_tag(people, entry, 'WIFE'))
# "CHIL"
- family.children = []
+ family.children.clear()
family.children.add(*__objects_from_entry_tag(people, entry, 'CHIL'))
# "NOTE"
- family.notes = []
+ family.notes.clear()
family.notes.add(*__objects_from_entry_tag(notes, entry, 'NOTE'))
family.save()
@@ -257,7 +264,7 @@ def __process_Note(entry, g):
def __process_Document(entry, obj, g):
full_name = __child_value_by_tags(entry, 'FILE')
- name = path.basename(full_name).decode('utf-8').strip()
+ name = path.basename(full_name).strip()
known = Document.objects.filter(docfile=name).exists()
if not known and not gedcom_storage.exists(name):
@@ -275,8 +282,8 @@ def __process_Document(entry, obj, g):
make_thumbnail(name, 'w128h128')
make_thumbnail(name, 'w640h480')
except Exception as e:
- print e
- print ' Warning: failed to make or find thumbnail: %s' % name
+ print(e)
+ print(' Warning: failed to make or find thumbnail: %s' % name)
return None # Bail on document creation if thumb fails
m.save()
@@ -352,7 +359,7 @@ def __objects_from_entry_tag(qset, entry, tag):
def __child_value_by_tags(entry, tags, default=None):
- if isinstance(tags, basestring):
+ if isinstance(tags, str):
tags = [tags]
tags.reverse()
next = entry
diff --git a/gedgo/middleware.py b/gedgo/middleware.py
index bd1ed43..e60554a 100644
--- a/gedgo/middleware.py
+++ b/gedgo/middleware.py
@@ -1,4 +1,3 @@
-from gedgo import redis
import json
import time
import re
@@ -16,40 +15,5 @@ class SimpleTrackerMiddleware(object):
"""
def process_response(self, request, response):
- # Don't process if redis isn't configured or non-200 response
- if redis is None or response.status_code != 200:
- return response
-
- # Only track non-superuser visitors
- if request.user is None or request.user.is_superuser \
- or not request.user.username:
- return response
-
- for pattern in IGNORE_PATTERNS:
- if pattern.match(request.path_info):
- return response
-
- # Increment counters and record pageview.
- # This is pretty fast, but could be done in a celery task to reduce
- # per-page overhead.
- id_ = request.user.id
- _increment_key('gedgo_page_view_count')
- _increment_key('gedgo_user_%d_page_view_count' % id_)
-
- page_view = {
- 'ip': request.META['REMOTE_ADDR'],
- 'path': request.path_info,
- 'time': int(time.time())
- }
- redis.lpush('gedgo_user_%d_page_views' % id_, json.dumps(page_view))
- redis.ltrim('gedgo_user_%d_page_views' % id_, 0, 100)
-
+ # TODO: Add user tracking
return response
-
-
-def _increment_key(key_name):
- try:
- pvc = int(redis.get(key_name))
- except TypeError:
- pvc = 0
- redis.set(key_name, pvc + 1)
diff --git a/gedgo/migrations/0004_auto_20210526_1717.py b/gedgo/migrations/0004_auto_20210526_1717.py
new file mode 100644
index 0000000..2eb02ea
--- /dev/null
+++ b/gedgo/migrations/0004_auto_20210526_1717.py
@@ -0,0 +1,38 @@
+# Generated by Django 3.0 on 2021-05-26 17:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('gedgo', '0003_comment'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='comment',
+ name='upload',
+ field=models.FileField(blank=True, null=True, upload_to='uploads/comments'),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='docfile',
+ field=models.FileField(upload_to='uploads'),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='kind',
+ field=models.CharField(choices=[('DOCUM', 'Document'), ('VIDEO', 'Video'), ('PHOTO', 'Image')], max_length=5),
+ ),
+ migrations.AlterField(
+ model_name='event',
+ name='date_approxQ',
+ field=models.BooleanField(verbose_name='Date is approximate'),
+ ),
+ migrations.AlterField(
+ model_name='family',
+ name='kind',
+ field=models.CharField(blank=True, max_length=10, null=True, verbose_name='Event'),
+ ),
+ ]
diff --git a/gedgo/models/__init__.py b/gedgo/models/__init__.py
index 3945ab1..553cac7 100644
--- a/gedgo/models/__init__.py
+++ b/gedgo/models/__init__.py
@@ -1,12 +1,12 @@
-from gedcom import Gedcom
-from comment import Comment
-from person import Person
-from family import Family
-from event import Event
-from note import Note
-from document import Document
-from documentary import Documentary
-from blogpost import BlogPost
+from .gedcom import Gedcom
+from .comment import Comment
+from .person import Person
+from .family import Family
+from .event import Event
+from .note import Note
+from .document import Document
+from .documentary import Documentary
+from .blogpost import BlogPost
__all__ = [
'Gedcom', 'Comment', 'Person', 'Family', 'Event',
diff --git a/gedgo/models/blogpost.py b/gedgo/models/blogpost.py
index 22bcc01..3893582 100644
--- a/gedgo/models/blogpost.py
+++ b/gedgo/models/blogpost.py
@@ -20,5 +20,5 @@ class Meta:
blank=True
)
- def __unicode__(self):
+ def str(self):
return 'Blogpost "%s"' % self.title
diff --git a/gedgo/models/comment.py b/gedgo/models/comment.py
index 38ce10b..9018a09 100644
--- a/gedgo/models/comment.py
+++ b/gedgo/models/comment.py
@@ -6,15 +6,14 @@ class Comment(models.Model):
class Meta:
app_label = 'gedgo'
- user = models.ForeignKey(User)
+ user = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
posted = models.DateTimeField(auto_now_add=True)
- upload = models.FileField(upload_to='uploads/comments', null=True,
- blank=True)
+ upload = models.FileField(upload_to='uploads/comments', null=True, blank=True)
- gedcom = models.ForeignKey('Gedcom', null=True, blank=True)
- person = models.ForeignKey('Person', null=True, blank=True)
- blogpost = models.ForeignKey('BlogPost', null=True, blank=True)
+ gedcom = models.ForeignKey('Gedcom', null=True, blank=True, on_delete=models.CASCADE)
+ person = models.ForeignKey('Person', null=True, blank=True, on_delete=models.CASCADE)
+ blogpost = models.ForeignKey('BlogPost', null=True, blank=True, on_delete=models.CASCADE)
@property
def noun(self):
@@ -24,5 +23,5 @@ def noun(self):
return self.person
return self.gedcom
- def __unicode__(self):
+ def __str__(self):
return 'Comment about %s by %s (%d)' % (self.noun, self.user, self.id)
diff --git a/gedgo/models/document.py b/gedgo/models/document.py
index 6210889..ad5e5ed 100644
--- a/gedgo/models/document.py
+++ b/gedgo/models/document.py
@@ -10,7 +10,7 @@ class Meta:
description = models.TextField(null=True, blank=True)
docfile = models.FileField(upload_to='uploads')
last_updated = models.DateTimeField(auto_now_add=True)
- gedcom = models.ForeignKey('Gedcom', null=True, blank=True)
+ gedcom = models.ForeignKey('Gedcom', null=True, blank=True, on_delete=models.CASCADE)
kind = models.CharField(
max_length=5,
@@ -29,7 +29,7 @@ class Meta:
related_name='media_tagged_families', blank=True
)
- def __unicode__(self):
+ def __str__(self):
return path.basename(self.docfile.path)
@property
diff --git a/gedgo/models/documentary.py b/gedgo/models/documentary.py
index bf2d000..1e6bf1a 100644
--- a/gedgo/models/documentary.py
+++ b/gedgo/models/documentary.py
@@ -10,13 +10,14 @@ class Meta:
tagline = models.CharField(max_length=100)
location = models.CharField(max_length=100, null=True, blank=True)
description = models.TextField(null=True, blank=True)
- gedcom = models.ForeignKey('Gedcom')
+ gedcom = models.ForeignKey('Gedcom', on_delete=models.CASCADE)
last_updated = models.DateTimeField(auto_now_add=True)
thumb = models.ForeignKey(
'Document',
related_name='documentaries_thumb',
- blank=True
+ blank=True,
+ on_delete=models.CASCADE,
)
tagged_people = models.ManyToManyField(
'Person',
@@ -29,5 +30,5 @@ class Meta:
blank=True
)
- def __unicode__(self):
+ def __str__(self):
return self.title
diff --git a/gedgo/models/event.py b/gedgo/models/event.py
index dc4b018..7a147a0 100644
--- a/gedgo/models/event.py
+++ b/gedgo/models/event.py
@@ -5,7 +5,7 @@
class Event(models.Model):
class Meta:
app_label = 'gedgo'
- gedcom = models.ForeignKey('Gedcom')
+ gedcom = models.ForeignKey('Gedcom', on_delete=models.CASCADE)
# Can't use DateFields because sometimes only a Year is known, and
# we don't want to show those as January 01, , and datetime
@@ -28,5 +28,5 @@ def date_string(self):
new_date = date(self.date.year, self.date.month, self.date.day)
return new_date.strftime(self.date_format)
- def __unicode__(self):
+ def __str__(self):
return '%s (%s)' % (self.date_string. self.id)
diff --git a/gedgo/models/family.py b/gedgo/models/family.py
index 4bf90ff..9459dae 100644
--- a/gedgo/models/family.py
+++ b/gedgo/models/family.py
@@ -1,14 +1,14 @@
from django.db import models
-from document import Document
-from documentary import Documentary
+from .document import Document
+from .documentary import Documentary
class Family(models.Model):
class Meta:
app_label = 'gedgo'
pointer = models.CharField(max_length=10, primary_key=True)
- gedcom = models.ForeignKey('Gedcom')
+ gedcom = models.ForeignKey('Gedcom', on_delete=models.CASCADE)
last_changed = models.DateField(null=True, blank=True)
husbands = models.ManyToManyField('Person', related_name='family_husbands')
@@ -22,16 +22,18 @@ class Meta:
'Event',
related_name='family_joined',
blank=True,
- null=True
+ null=True,
+ on_delete=models.CASCADE,
)
separated = models.ForeignKey(
'Event',
related_name='family_separated',
blank=True,
- null=True
+ null=True,
+ on_delete=models.CASCADE,
)
- def __unicode__(self):
+ def __str__(self):
return '%s (%s)' % (self.family_name, self.pointer)
@property
diff --git a/gedgo/models/gedcom.py b/gedgo/models/gedcom.py
index e6cd9ee..02a6d6e 100644
--- a/gedgo/models/gedcom.py
+++ b/gedgo/models/gedcom.py
@@ -1,5 +1,5 @@
from django.db import models
-from person import Person
+from gedgo.models.person import Person
class Gedcom(models.Model):
@@ -22,7 +22,7 @@ class Meta:
blank=True
)
- def __unicode__(self):
+ def __str__(self):
if not self.title:
return 'Gedcom #%d' % self.id
return '%s (%d)' % (self.title, self.id)
diff --git a/gedgo/models/note.py b/gedgo/models/note.py
index 0055392..f6c1a0a 100644
--- a/gedgo/models/note.py
+++ b/gedgo/models/note.py
@@ -6,9 +6,9 @@ class Meta:
app_label = 'gedgo'
pointer = models.CharField(max_length=10, primary_key=True)
text = models.TextField()
- gedcom = models.ForeignKey('Gedcom')
+ gedcom = models.ForeignKey('Gedcom', on_delete=models.CASCADE)
- def __unicode__(self):
+ def __str__(self):
return 'Note (%s)' % self.pointer
@property
diff --git a/gedgo/models/person.py b/gedgo/models/person.py
index 60f2304..8c43ff6 100644
--- a/gedgo/models/person.py
+++ b/gedgo/models/person.py
@@ -1,7 +1,7 @@
from django.db import models
-from document import Document
-from documentary import Documentary
+from gedgo.models.document import Document
+from gedgo.models.documentary import Documentary
import re
@@ -10,7 +10,7 @@ class Meta:
app_label = 'gedgo'
verbose_name_plural = 'People'
pointer = models.CharField(max_length=10, primary_key=True)
- gedcom = models.ForeignKey('Gedcom')
+ gedcom = models.ForeignKey('Gedcom', on_delete=models.CASCADE)
last_changed = models.DateField(null=True, blank=True)
# Name
@@ -28,13 +28,15 @@ class Meta:
'Event',
related_name='person_birth',
null=True,
- blank=True
+ blank=True,
+ on_delete=models.CASCADE,
)
death = models.ForeignKey(
'Event',
related_name='person_death',
null=True,
- blank=True
+ blank=True,
+ on_delete=models.CASCADE,
)
# Family
@@ -42,7 +44,8 @@ class Meta:
'Family',
related_name='person_child_family',
null=True,
- blank=True
+ blank=True,
+ on_delete=models.CASCADE,
)
spousal_families = models.ManyToManyField(
'Family',
@@ -55,7 +58,7 @@ class Meta:
# Profile
profile = models.ManyToManyField('Document', blank=True)
- def __unicode__(self):
+ def __str__(self):
return '%s, %s (%s)' % (self.last_name, self.first_name, self.pointer)
@property
diff --git a/gedgo/static/js/pedigree.js b/gedgo/static/js/pedigree.js
index 7ab8008..d52af7a 100644
--- a/gedgo/static/js/pedigree.js
+++ b/gedgo/static/js/pedigree.js
@@ -1,70 +1,71 @@
/* global d3 */
'use strict';
-const gid = d3.select("#pedigree-tree").attr("data-gid"),
- pid = d3.select("#pedigree-tree").attr("data-pid");
+(function() {
+ const gid = d3.select("#pedigree-tree").attr("data-gid"),
+ pid = d3.select("#pedigree-tree").attr("data-pid");
-d3.json("/gedgo/" + gid + "/pedigree/" + pid + "/", (treeData) => {
+ d3.json("/gedgo/" + gid + "/pedigree/" + pid + "/", (treeData) => {
- // Create a svg canvas
- const vis = d3.select("#pedigree-tree").append("svg:svg")
- .attr("width", 480)
- .attr("height", 600)
- .append("svg:g")
- .attr("transform", "translate(40, -100)");
+ // Create a svg canvas
+ const vis = d3.select("#pedigree-tree").append("svg:svg")
+ .attr("width", 480)
+ .attr("height", 600)
+ .append("svg:g")
+ .attr("transform", "translate(40, -100)");
- // Create a tree "canvas"
- const gid = treeData.gid,
- tree = d3.layout.tree()
- .size([800,230]);
+ // Create a tree "canvas"
+ const gid = treeData.gid,
+ tree = d3.layout.tree()
+ .size([800,230]);
- const diagonal = d3.svg.diagonal()
- // change x and y (for the left to right tree)
- .projection(d => [d.y, d.x]);
+ const diagonal = d3.svg.diagonal()
+ // change x and y (for the left to right tree)
+ .projection(d => [d.y, d.x]);
- // Preparing the data for the tree layout, convert data into an array of nodes
- const nodes = tree.nodes(treeData);
+ // Preparing the data for the tree layout, convert data into an array of nodes
+ const nodes = tree.nodes(treeData);
- // Create an array with all the links
- const links = tree.links(nodes);
+ // Create an array with all the links
+ const links = tree.links(nodes);
- vis.selectAll("pathlink")
- .data(links)
- .enter().append("svg:path")
- .attr("d", diagonal);
+ vis.selectAll("pathlink")
+ .data(links)
+ .enter().append("svg:path")
+ .attr("d", diagonal);
- const node = vis.selectAll("g.node")
- .data(nodes)
- .enter().append("svg:g")
- .attr("transform", d => "translate(" + d.y + "," + d.x + ")");
+ const node = vis.selectAll("g.node")
+ .data(nodes)
+ .enter().append("svg:g")
+ .attr("transform", d => "translate(" + d.y + "," + d.x + ")");
- // Add the dot at every node
- node.append("svg:rect")
- .attr("rx", 10)
- .attr("ry", 10)
- .attr("y", -30)
- .attr("x", -20)
- .attr("width", 200)
- .attr("height", 50);
+ // Add the dot at every node
+ node.append("svg:rect")
+ .attr("rx", 10)
+ .attr("ry", 10)
+ .attr("y", -30)
+ .attr("x", -20)
+ .attr("width", 200)
+ .attr("height", 50);
- // place the name atribute left or right depending if children
- node.append("svg:a")
- .attr("xlink:href", d => "/gedgo/" + gid + "/" + d.id)
- .append("text")
- .attr("dx", -10)
- .attr("dy", -10)
- .attr("text-anchor", "start")
- .text(d => d.name)
- .attr("font-family", "Baskerville")
- .attr("font-size", "11pt");
-
- node.append("svg:text")
- .attr("dx", -10)
- .attr("dy", 8)
- .attr("text-anchor", "start")
- .text(d => d.span)
- .attr("font-family", "Baskerville")
- .attr("font-size", "11pt")
- .attr("fill", "gray");
-});
+ // place the name atribute left or right depending if children
+ node.append("svg:a")
+ .attr("xlink:href", d => "/gedgo/" + gid + "/" + d.id)
+ .append("text")
+ .attr("dx", -10)
+ .attr("dy", -10)
+ .attr("text-anchor", "start")
+ .text(d => d.name)
+ .attr("font-family", "Baskerville")
+ .attr("font-size", "11pt");
+ node.append("svg:text")
+ .attr("dx", -10)
+ .attr("dy", 8)
+ .attr("text-anchor", "start")
+ .text(d => d.span)
+ .attr("font-family", "Baskerville")
+ .attr("font-size", "11pt")
+ .attr("fill", "gray");
+ });
+})();
diff --git a/gedgo/static/js/timeline.js b/gedgo/static/js/timeline.js
index be12c4d..4670049 100644
--- a/gedgo/static/js/timeline.js
+++ b/gedgo/static/js/timeline.js
@@ -1,62 +1,64 @@
/* global d3 */
'use strict';
-const gid = d3.select("#timeline").attr("data-gid"),
- pid = d3.select("#timeline").attr("data-pid");
+(function() {
+ const gid = d3.select("#timeline").attr("data-gid"),
+ pid = d3.select("#timeline").attr("data-pid");
-d3.json("/gedgo/" + gid + "/timeline/" + pid + "/", (data) => {
- const events = data.events;
- if (events.length < 1) {
- $("#timeline-pod").remove();
- return;
- }
- const birthyear = data.start,
- deathyear = data.end,
- hscale = d3.scale.linear()
- .domain([0, 35])
- .range([20, 400]);
+ d3.json("/gedgo/" + gid + "/timeline/" + pid + "/", (data) => {
+ const events = data.events;
+ if (events.length < 1) {
+ $("#timeline-pod").remove();
+ return;
+ }
+ const birthyear = data.start,
+ deathyear = data.end,
+ hscale = d3.scale.linear()
+ .domain([0, 35])
+ .range([20, 400]);
- //Width and height
- const w = 480,
- h = hscale(deathyear - birthyear),
- scale = d3.scale.linear()
- .domain([birthyear, deathyear])
- .range([10, h - 10]);
+ //Width and height
+ const w = 480,
+ h = hscale(deathyear - birthyear),
+ scale = d3.scale.linear()
+ .domain([birthyear, deathyear])
+ .range([10, h - 10]);
- // Create SVG element
- const svg = d3.select("#timeline")
- .append("svg:svg")
- .attr("width", w)
- .attr("height", h);
+ // Create SVG element
+ const svg = d3.select("#timeline")
+ .append("svg:svg")
+ .attr("width", w)
+ .attr("height", h);
- svg.selectAll("line")
- .data([1])
- .enter()
- .append("line")
- .attr("x1", w/2).attr("y1", 10)
- .attr("x2", w/2).attr("y2", h - 10)
- .attr("stroke", "teal");
+ svg.selectAll("line")
+ .data([1])
+ .enter()
+ .append("line")
+ .attr("x1", w/2).attr("y1", 10)
+ .attr("x2", w/2).attr("y2", h - 10)
+ .attr("stroke", "teal");
- svg.selectAll("circle")
- .data(events)
- .enter()
- .append("circle")
- .attr("cx", w/2)
- .attr("cy", (d) => scale(d.year))
- .attr("r", 5)
- .attr("fill", d => (d.year === birthyear || d.year === deathyear) ? "teal" : "white")
- .attr("stroke-width", 3)
- .attr("stroke", d => (d.type === 'personal') ? "teal" : "orange");
+ svg.selectAll("circle")
+ .data(events)
+ .enter()
+ .append("circle")
+ .attr("cx", w/2)
+ .attr("cy", (d) => scale(d.year))
+ .attr("r", 5)
+ .attr("fill", d => (d.year === birthyear || d.year === deathyear) ? "teal" : "white")
+ .attr("stroke-width", 3)
+ .attr("stroke", d => (d.type === 'personal') ? "teal" : "orange");
- svg.selectAll("text")
- .data(events)
- .enter()
- .append("text")
- .text((d) => d.year + ': ' + d.text)
- .attr("x", d => (d.type === 'personal') ? w/2 + 20 : w/2 - 20)
- .attr("y", (d) => scale(d.year) + 5)
- .attr("text-anchor", d => (d.type === 'personal') ? "start" : "end")
- .attr("font-family", "Baskerville")
- .attr("font-size", "9pt")
- .attr("fill", "gray");
-});
+ svg.selectAll("text")
+ .data(events)
+ .enter()
+ .append("text")
+ .text((d) => d.year + ': ' + d.text)
+ .attr("x", d => (d.type === 'personal') ? w/2 + 20 : w/2 - 20)
+ .attr("y", (d) => scale(d.year) + 5)
+ .attr("text-anchor", d => (d.type === 'personal') ? "start" : "end")
+ .attr("font-family", "Baskerville")
+ .attr("font-size", "9pt")
+ .attr("fill", "gray");
+ });
+})();
diff --git a/gedgo/storages.py b/gedgo/storages.py
index 805281f..de04f83 100644
--- a/gedgo/storages.py
+++ b/gedgo/storages.py
@@ -6,8 +6,8 @@
import re
import os
from PIL import Image
-from cStringIO import StringIO
-from dropbox.dropbox import Dropbox
+from io import StringIO, BytesIO
+from dropbox import Dropbox
from dropbox.files import FileMetadata, FolderMetadata, ThumbnailFormat, \
ThumbnailSize
@@ -26,7 +26,8 @@ def exists(self, name):
self.client.files_get_metadata(self.path(name)),
(FileMetadata, FolderMetadata)
)
- except Exception:
+ except Exception as e:
+ print(e)
return False
def listdir(self, name):
@@ -64,7 +65,7 @@ def search(self, query, name='', start=0):
return (directories, files)
def preview(self, name, size='w128h128'):
- file_ = StringIO(self.client.files_get_thumbnail(
+ file_ = BytesIO(self.client.files_get_thumbnail(
self.path(name),
format=ThumbnailFormat('jpeg', None),
size=ThumbnailSize(size, None)
@@ -110,10 +111,10 @@ def resize_thumb(file_, size='w128h128', crop=None):
if size in ('w64h64', 'w128h128'):
if width > height:
- offset = (width - height) / 2
+ offset = (width - height) // 2
box = (offset, 0, offset + height, height)
else:
- offset = ((height - width) * 3) / 10
+ offset = ((height - width) * 3) // 10
box = (0, offset, width, offset + width)
im = im.crop(box)
@@ -121,7 +122,7 @@ def resize_thumb(file_, size='w128h128', crop=None):
new_size = [int(d) for d in m.groups()]
im.thumbnail(new_size, Image.ANTIALIAS)
- output = StringIO()
+ output = BytesIO()
im.save(output, 'JPEG')
return output
diff --git a/gedgo/tasks.py b/gedgo/tasks.py
index d75cff7..d7e34ea 100644
--- a/gedgo/tasks.py
+++ b/gedgo/tasks.py
@@ -1,37 +1,24 @@
-# flake8: noqa: E402
-from __future__ import absolute_import
-import os
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
-
-import django
-django.setup()
-
from gedgo.gedcom_update import update
-from gedgo import redis
from gedgo.models import Gedcom
-from celery import Celery
from django.conf import settings
from datetime import datetime
from django.core.mail import send_mail, send_mass_mail
from django.contrib.auth.models import User
import traceback
-import requests
-import json
-
-app = Celery('gedgo')
-app.config_from_object(settings)
-@app.task
def async_update(gedcom_id, file_name, recipient_ids,
message, domain, sender_id):
+ print('OK!')
+
start = datetime.now()
gedcom = Gedcom.objects.get(id=gedcom_id)
errstr = ''
try:
- update(gedcom, file_name, verbose=False)
- except Exception:
+ update(gedcom, file_name, verbose=True)
+ except Exception as e:
+ print(e)
errstr = traceback.format_exc()
end = datetime.now()
@@ -61,15 +48,3 @@ def async_update(gedcom_id, file_name, recipient_ids,
send_mass_mail(datatuple)
-
-@app.task
-def geo_resolve_ip(ip_address):
- if redis is None:
- return
- try:
- response = requests.get('ipinfo.io/%s/json' % ip_address)
- j = response.json()
- j['requested'] = datetime.utcnow().isoformat()
- redis.set('gedgo_ip_%s', json.dumps(j))
- except (requests.exceptions.RequestsException, ValueError):
- return
diff --git a/gedgo/templates/default/dashboard.html b/gedgo/templates/default/dashboard.html
index a9ff142..9c504cf 100644
--- a/gedgo/templates/default/dashboard.html
+++ b/gedgo/templates/default/dashboard.html
@@ -42,37 +42,6 @@ Users to notify:
After finishing the update, the website will send an email to the site owner indicating that the update is complete.
-
-
-
Traffic Stats
- {% if total %}
-
-
{{ total }} total page views
- since {{ tracking_start }}
-
- {% endif %}
- {% if user_views %}
-
User Activity
-
- Username | Page views | Last View |
- {% for user_view in user_views %}
-
- {{ user_view.user.username }} |
- {{ user_view.count }} |
- {{ user_view.last_view }} |
-
- {% endfor %}
-
- {% endif %}
- {% if not total and not user_views %}
-
No activity recorded yet.
-
- {% endif %}
-
-
{% endblock %}
{% block javascript %}
diff --git a/gedgo/templates/default/gedcom.html b/gedgo/templates/default/gedcom.html
index ee1b964..aca2363 100644
--- a/gedgo/templates/default/gedcom.html
+++ b/gedgo/templates/default/gedcom.html
@@ -4,7 +4,7 @@
{% block leftsidebar %}
{% for photo in gedcom.photo_sample %}
-
+
diff --git a/gedgo/templates/default/person.html b/gedgo/templates/default/person.html
index b916b45..bb54125 100644
--- a/gedgo/templates/default/person.html
+++ b/gedgo/templates/default/person.html
@@ -4,7 +4,7 @@
{% block leftsidebar %}
{% for photo in photos %}
-
+
@@ -63,7 +63,7 @@
Siblings
{% for family in person.spousal_families.iterator %}
-
{% if family.kind = "MARR" %}Marital Family{% else %}Domestic Relationship{% endif %}
+
{% if family.kind == "MARR" %}Marital Family{% else %}Domestic Relationship{% endif %}
{% for somebody in family.spouses %}
{% if not somebody == person %}
@@ -78,9 +78,9 @@
{% if family.kind = "MARR" %}Marital Family{% else %}Domestic Relationship{%
{% if family.joined.date or family.joined.place or family.separated.date %}
{% if family.joined.date %}
- {% if family.kind = "MARR" %}Married{% else %}Domestic Partners{% endif %}: {{ family.joined.date_string }}{% if family.joined.date_approxQ %} (approximate){% endif %}{% endif %}{% if family.joined.place %}
{{ family.joined.place }}{% endif %}
+ {% if family.kind == "MARR" %}Married{% else %}Domestic Partners{% endif %}: {{ family.joined.date_string }}{% if family.joined.date_approxQ %} (approximate){% endif %}{% endif %}{% if family.joined.place %}
{{ family.joined.place }}{% endif %}
{% if family.separated.date %}
- {% if family.kind = "MARR" %}Divorced{% else %}Separated{% endif %}: {{ family.separated.date_string }} {% if family.separated.date_approxQ %}(approximate){% endif %}
+ {% if family.kind == "MARR" %}Divorced{% else %}Separated{% endif %}: {{ family.separated.date_string }} {% if family.separated.date_approxQ %}(approximate){% endif %}
{% endif %}
{% if family.separated.place %}
diff --git a/gedgo/templates/default/user_tracking.html b/gedgo/templates/default/user_tracking.html
deleted file mode 100644
index 57e50ae..0000000
--- a/gedgo/templates/default/user_tracking.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-
{{ user.username }} Recent Activity
-
-
-{% endblock %}
diff --git a/gedgo/templates/auth/login.html b/gedgo/templates/registration/login.html
similarity index 100%
rename from gedgo/templates/auth/login.html
rename to gedgo/templates/registration/login.html
diff --git a/gedgo/urls.py b/gedgo/urls.py
index 792f229..12d083a 100644
--- a/gedgo/urls.py
+++ b/gedgo/urls.py
@@ -1,7 +1,6 @@
from django.conf.urls import url
from django.shortcuts import redirect
-from django.contrib.auth.views import password_reset, password_reset_done, \
- password_reset_confirm
+from django.contrib.auth.views import PasswordResetView
from gedgo import views
@@ -27,29 +26,14 @@
url(r'^research/(?P.*)$', views.research),
url(r'^search/$', views.search),
url(r'^dashboard/$', views.dashboard),
- url(r'^dashboard/user/(?P\d+)/$', views.user_tracking),
# Auth
url(r'^logout/$', views.logout_view),
- url(r'^password_reset/$',
- password_reset,
- {
- 'template_name': 'auth/login.html',
- 'email_template_name': 'auth/password_reset_email.html',
- 'post_reset_redirect': '/gedgo/password_reset/done/'
- }),
- url(r'^password_reset/done/$',
- password_reset_done,
- {
- 'template_name': 'auth/password_reset_done.html'
- }),
- url(r'^password_reset/(?P[0-9A-Za-z]+)-(?P.+)/$',
- password_reset_confirm,
- {
- 'post_reset_redirect': '/',
- 'template_name': 'auth/password_reset_confirm.html'
- }),
-
+ url(r'^password_reset/$', PasswordResetView.as_view(
+ subject_template_name='email/password_reset_subject.txt',
+ email_template_name='auth/password_reset_email.txt',
+ html_email_template_name='email/password_reset.html',
+ ), name='auth_password_reset'),
# Authenticated media fileserve view
url(r'^media/(?P\w+)/(?P.*)$', views.media),
diff --git a/gedgo/views/__init__.py b/gedgo/views/__init__.py
index 793e10e..1796beb 100644
--- a/gedgo/views/__init__.py
+++ b/gedgo/views/__init__.py
@@ -1,16 +1,16 @@
-from search import search
-from model_views import person, gedcom, documentaries, document, \
+from .search import search
+from .model_views import person, gedcom, documentaries, document, \
documentary_by_id
-from dashboard import dashboard, user_tracking, worker_status
-from blog import blog, blog_list, blogpost
-from research import research
-from visualizations import pedigree, timeline
-from util import logout_view
-from media import media
+from .dashboard import dashboard, worker_status
+from .blog import blog, blog_list, blogpost
+from .research import research
+from .visualizations import pedigree, timeline
+from .util import logout_view
+from .media import media
__all__ = [
'person', 'gedcom', 'dashboard', 'search',
'documentaries', 'blogpost', 'document', 'documentary_by_id',
'blog', 'blog_list', 'media', 'research', 'logout_view',
- 'pedigree', 'timeline', 'user_tracking', 'worker_status'
+ 'pedigree', 'timeline', 'worker_status'
]
diff --git a/gedgo/views/dashboard.py b/gedgo/views/dashboard.py
index 526484e..c74cd25 100644
--- a/gedgo/views/dashboard.py
+++ b/gedgo/views/dashboard.py
@@ -1,8 +1,9 @@
from gedgo.forms import UpdateForm
-from gedgo.tasks import app, async_update
+from gedgo.tasks import async_update
from gedgo.views.util import render
from gedgo.models import Gedcom
-from gedgo import redis
+
+from django_simple_task import defer
from django.http import Http404, HttpResponse
from django.contrib.auth.decorators import login_required
@@ -25,10 +26,7 @@ def dashboard(request):
raise Http404
form = None
- if request.POST and request.GET.get('reset_tracking'):
- _reset_tracking()
- return redirect('/gedgo/dashboard/')
- elif request.POST:
+ if request.POST:
form = UpdateForm(request.POST, request.FILES)
_handle_upload(request, form)
return redirect('/gedgo/dashboard/')
@@ -36,19 +34,13 @@ def dashboard(request):
if form is None:
form = UpdateForm()
- # Collect tracking stats from redis storage
- tracking_start, user_views, total = _page_view_stats()
-
# Render list page with the documents and the form
return render(
request,
'dashboard.html',
{
'form': form,
- 'tracking_start': tracking_start,
'users': User.objects.filter(email__contains='@').iterator(),
- 'user_views': user_views,
- 'total': total,
'gedcoms': Gedcom.objects.iterator()
}
)
@@ -60,7 +52,7 @@ def worker_status(request):
XHR view for whether the celery worker is up
"""
try:
- status = app.control.ping() or []
+ status = [True]
except Exception:
# TODO: What celery exceptions are we catching here?
status = []
@@ -70,47 +62,25 @@ def worker_status(request):
)
-@login_required
-def user_tracking(request, user_id):
- if not request.user.is_superuser:
- raise Http404
-
- user = get_object_or_404(User, id=user_id)
- count = redis.keys('gedgo_user_%d_page_view_count' % user.id)
- if not count:
- raise Http404
-
- views = redis.lrange('gedgo_user_%d_page_views' % user.id, 0, -1)
- views = [_load_page_view(v) for v in views]
-
- return render(
- request,
- 'user_tracking.html',
- {
- 'user': user,
- 'count': count,
- 'views': views
- }
- )
-
-
def _handle_upload(request, form):
if form.is_valid():
file_name = 'uploads/gedcoms/%d_%s' % (
time.time(), form.cleaned_data['gedcom_file'].name)
default_storage.save(file_name, form.cleaned_data['gedcom_file'])
- async_update.delay(
- form.cleaned_data['gedcom_id'],
- os.path.join(settings.MEDIA_ROOT, file_name),
- form.cleaned_data['email_users'],
- form.cleaned_data['message'],
- request.get_host(),
- request.user.id
- )
+ defer(async_update, {
+ 'args': [
+ form.cleaned_data['gedcom_id'],
+ os.path.join(settings.MEDIA_ROOT, file_name),
+ form.cleaned_data['email_users'],
+ form.cleaned_data['message'],
+ request.get_host(),
+ request.user.id,
+ ]
+ })
messages.success(
request,
'Your gedcom file has been uploaded and the database will '
- 'be updated momentarily.'
+ 'be processed shortly.'
)
else:
error_message = ('Something went wrong with your upload, '
@@ -118,53 +88,3 @@ def _handle_upload(request, form):
if hasattr(form, 'error_message'):
error_message = form.error_message
messages.error(request, error_message)
-
-
-def _reset_tracking():
- if redis is None:
- return {}
-
- keys = redis.keys('gedgo_*')
- for key in keys:
- redis.delete(key)
-
- redis.set('gedgo_tracking_start', int(time.time()))
-
-
-def _page_view_stats():
- if redis is None:
- return datetime.datetime.utcnow(), {}, 0
-
- user_keys = redis.keys('gedgo_user_*_page_view_count')
- users = User.objects.filter(
- id__in=[int(k.split('_')[2]) for k in user_keys]
- )
-
- user_views = []
- for user in users:
- last = redis.lrange('gedgo_user_%d_page_views' % user.id, 0, 0)[0]
- pvc = redis.get('gedgo_user_%d_page_view_count' % user.id)
- user_views.append({
- 'user': user,
- 'last_view': _load_page_view(last)['timestamp'],
- 'count': pvc
- })
- user_views = sorted(user_views, key=lambda x: x['last_view'], reverse=True)
-
- tracking_start = _timestamp_from_redis('gedgo_tracking_start')
-
- return tracking_start, user_views, redis.get('gedgo_page_view_count')
-
-
-def _load_page_view(json_str):
- view = json.loads(json_str)
- view['timestamp'] = datetime.datetime.fromtimestamp(int(view['time']))
- return view
-
-
-def _timestamp_from_redis(key):
- try:
- timestamp = redis.get(key)
- return datetime.datetime.fromtimestamp(int(timestamp))
- except Exception:
- pass
diff --git a/gedgo/views/media.py b/gedgo/views/media.py
index 00930db..81570dd 100644
--- a/gedgo/views/media.py
+++ b/gedgo/views/media.py
@@ -52,13 +52,13 @@ def serve_thumbnail(request, storage_name, storage, size, name):
try:
if not default_storage.exists(cache_name):
- print 'generating cache: ' + cache_name
+ print('generating cache: ' + cache_name)
content = storage.preview(name, size)
assert content
default_storage.save(cache_name, content)
return serve_content(default_storage, cache_name)
except Exception as e:
- print e
+ print(e)
return HttpResponseRedirect(settings.STATIC_URL + 'img/question.jpg')
@@ -83,7 +83,7 @@ def serve_content(storage, name):
# Set various file headers and return
base = path.basename(name)
response['Content-Type'] = mimetypes.guess_type(base)[0]
- response['Content-Length'] = storage.size(name)
+ # response['Content-Length'] = storage.size(name)
response['Cache-Control'] = 'public, max-age=31536000'
if response['Content-Type'] is None:
response['Content-Disposition'] = "attachment; filename=%s;" % (base)
diff --git a/gedgo/views/util.py b/gedgo/views/util.py
index 4a5ad30..9337594 100644
--- a/gedgo/views/util.py
+++ b/gedgo/views/util.py
@@ -2,7 +2,7 @@
from django.shortcuts import redirect
from django.contrib.auth import logout
from django.contrib import messages
-from django.shortcuts import render_to_response
+from django.shortcuts import render as render_to_response
from django.template import RequestContext
from gedgo.models import BlogPost, Documentary
@@ -30,7 +30,7 @@ def process_comments(request):
'Your comment has been sent. Thank you!'
)
except Exception as e:
- print e
+ print(e)
messages.error(
request,
"We're sorry, we couldn't process your comment."
@@ -39,10 +39,11 @@ def process_comments(request):
def render(request, template, context):
+ context.update(site_context(request))
return render_to_response(
+ request,
template,
context,
- context_instance=RequestContext(request, site_context(request))
)
@@ -54,10 +55,7 @@ def site_context(request):
"""
show_blog = BlogPost.objects.exists()
show_documentaries = Documentary.objects.exists()
- show_researchfiles = isinstance(
- getattr(settings, 'GEDGO_RESEARCH_FILE_ROOT', None),
- basestring
- )
+ show_researchfiles = getattr(settings, 'GEDGO_RESEARCH_FILE_ROOT', None)
show_file_uploads = getattr(
settings, 'GEDGO_ALLOW_FILE_UPLOADS', True) is True
site_title = settings.GEDGO_SITE_TITLE
diff --git a/gedgo/views/visualizations.py b/gedgo/views/visualizations.py
index 76d912f..31a34bf 100644
--- a/gedgo/views/visualizations.py
+++ b/gedgo/views/visualizations.py
@@ -220,5 +220,7 @@ def __gatherby(inlist, func):
['Wikipedia founded', 2001],
['Human genome project completed', 2003],
['Barack Obama sworn US President', 2009],
- ['population reaches 7 billion', 2011]
+ ['population reaches 7 billion', 2011],
+ ['NASA Flies by Pluto', 2015],
+ ['COVID-19 Pandemic', 2020],
]
diff --git a/reqs.frozen.pip b/reqs.frozen.pip
index b1bd0c1..99ce993 100644
--- a/reqs.frozen.pip
+++ b/reqs.frozen.pip
@@ -1,31 +1,31 @@
-amqp==2.2.2
-anyjson==0.3.3
-billiard==3.5.0.3
-celery==4.1.0
-certifi==2018.1.18
-chardet==3.0.4
-configparser==3.5.0
-dj-static==0.0.6
-Django==1.9.2
-dropbox==8.6.0
-enum34==1.1.6
-flake8==3.5.0
-funcsigs==1.0.2
-gunicorn==19.7.1
-idna==2.6
-kombu==4.1.0
-mccabe==0.6.1
-mock==2.0.0
-MySQL-python==1.2.5
-pbr==3.1.1
-Pillow==3.1.1
-pycodestyle==2.3.1
-pyflakes==1.6.0
-pytz==2017.3
-redis==2.10.6
-requests==2.18.4
-six==1.11.0
-static3==0.7.0
-urllib3==1.22
-vine==1.1.4
-virtualenv==15.1.0
+[33mDEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7.[0m
+amqp==2.2.2
+anyjson==0.3.3
+billiard==3.5.0.3
+celery==4.1.0
+certifi==2018.1.18
+chardet==3.0.4
+configparser==3.5.0
+dj-static==0.0.6
+Django==1.9.2
+dropbox==8.6.0
+enum34==1.1.6
+flake8==3.5.0
+funcsigs==1.0.2
+gunicorn==19.7.1
+idna==2.6
+kombu==4.1.0
+mccabe==0.6.1
+mock==2.0.0
+MySQL-python==1.2.5
+pbr==3.1.1
+Pillow==3.1.1
+pycodestyle==2.3.1
+pyflakes==1.6.0
+pytz==2017.3
+requests==2.18.4
+six==1.11.0
+static3==0.7.0
+urllib3==1.22
+vine==1.1.4
+virtualenv==15.1.0
diff --git a/reqs.pip b/reqs.pip
index 3b4c884..d10e30d 100644
--- a/reqs.pip
+++ b/reqs.pip
@@ -1,12 +1,10 @@
-django==1.9.2
-celery==4.1.0
+django==3.2.3
pillow==3.1.1
-dj-static==0.0.6
-gunicorn
-anyjson
-dropbox
-mysql-python
-redis
-flake8
-mock
+python-dateutil
+uvicorn
requests
+mock
+flake8
+anyjson
+dropbox==9.0.0
+django-simple-task
diff --git a/settings.py b/settings.py
index 939ff33..14c5817 100644
--- a/settings.py
+++ b/settings.py
@@ -1,35 +1,20 @@
import os
-import sys
-project_root = os.path.dirname(__file__)
-# Django settings for gedgo project.
-
-DEBUG = True
-
-ADMINS = ()
-MANAGERS = ADMINS
+# Django settings for gedgo project.
DATABASES = {
'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'gedgo',
- 'USER': 'gedgo',
- 'PASSWORD': 'gedgo',
- 'HOST': 'db',
- 'PORT': '',
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': '/data/db.sqllite',
}
}
-ALLOWED_HOSTS = []
-TIME_ZONE = 'America/New_York'
-LANGUAGE_CODE = 'en-us'
-
SITE_ID = 1
USE_I18N = True
USE_L10N = True
USE_TZ = True
-MEDIA_ROOT = '/app/files/default/'
+MEDIA_ROOT = '/app/.files/default/'
MEDIA_URL = '/gedgo/media/default/'
STATIC_ROOT = '/static/'
@@ -46,8 +31,8 @@
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
- os.path.join(project_root, 'gedgo/templates'),
- os.path.join(project_root, 'gedgo/templates/default'),
+ '/app/gedgo/templates',
+ '/app/gedgo/templates/default',
],
'APP_DIRS': True,
'OPTIONS': {
@@ -61,13 +46,14 @@
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
+ 'django.template.context_processors.request',
],
},
},
]
-MIDDLEWARE_CLASSES = (
+MIDDLEWARE = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@@ -78,7 +64,7 @@
)
ROOT_URLCONF = 'urls'
-WSGI_APPLICATION = 'wsgi.application'
+ASGI_APPLICATION = 'asgi.application'
INSTALLED_APPS = (
'django.contrib.auth',
@@ -88,13 +74,15 @@
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
- 'gedgo'
+ 'django_simple_task',
+ 'gedgo',
)
+
CACHES = {
'research_preview': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
- 'LOCATION': '/app/files/research_preview',
+ 'LOCATION': '/app/.files/research_preview',
},
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
@@ -104,27 +92,9 @@
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
-# Just send emails to the console.
-EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
-SERVER_EMAIL = ['noreply@example.com']
-
-GEDGO_ALLOW_FILE_UPLOADS = True
-GEDGO_SENDFILE_HEADER = 'X-Accel-Redirect'
-GEDGO_SENDFILE_PREFIX = '/protected/'
-GEDGO_SITE_TITLE = 'My Genealogy Site'
-GEDGO_REDIS_SERVER = 'redis'
-GEDGO_RESEARCH_FILE_STORAGE = 'gedgo.storages.FileSystemSearchableStorage'
-GEDGO_RESEARCH_FILE_ROOT = '/app/files/gedcom/'
-GEDGO_DOCUMENTARY_STORAGE = 'gedgo.storages.FileSystemSearchableStorage'
-GEDGO_DOCUMENTARY_ROOT = '/app/files/documentaries/'
-GEDGO_GEDCOM_FILE_STORAGE = 'gedgo.storages.FileSystemSearchableStorage'
-GEDGO_GEDCOM_FILE_ROOT = '/app/files/research/'
-GEDGO_SHOW_RESEARCH_FILES = True
-
-BROKER_BACKEND = 'redis'
-BROKER_URL = 'redis://redis:6379/0'
-CELERY_RESULT_BACKEND = 'redis://redis:6379/0'
-CELERY_ACCEPT_CONTENT = ["json"]
+# BROKER_URL = 'django://'
+# CELERY_RESULT_BACKEND = 'djcelery.backends.database'
+# CELERY_ACCEPT_CONTENT = ["json"]
LOGGING = {
'version': 1,
@@ -149,12 +119,52 @@
},
}
}
+DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
+
-if 'test' in sys.argv:
- DATABASES['default']['USER'] = 'root'
- DATABASES['default']['PASSWORD'] = 'docker'
+#
+# Environment-variable overrides
+#
+
+TIME_ZONE = os.environ.get('TIME_ZONE', 'America/New_York')
+LANGUAGE_CODE = os.environ.get('LANGUAGE_CODE', 'en-us')
+ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '*').split(',')
+DEBUG = (os.environ.get('DEBUG') == 'True')
+SECRET_KEY = os.environ.get('SECRET_KEY', 'foo')
+if os.environ.get('ADMINS'):
+ ADMINS = [a.split(':') for a in os.environ['ADMINS'].split(',')]
else:
- try:
- from settings_local import * # noqa
- except ImportError:
- pass
+ ADMINS = []
+MANAGERS = ADMINS
+
+#
+# Gedgo-specific settings
+#
+
+DROPBOX_ACCESS_TOKEN = os.environ.get('DROPBOX_ACCESS_TOKEN', None)
+GEDGO_ALLOW_FILE_UPLOADS = os.environ.get('GEDGO_ALLOW_FILE_UPLOADS', 'False') == 'True'
+GEDGO_SENDFILE_HEADER = os.environ.get('GEDGO_SENDIFLE_HEADER', 'X-Accel-Redirect')
+GEDGO_SENDFILE_PREFIX = os.environ.get('GEDOG_SENDFILE_PREFIX', '/protected/')
+GEDGO_SITE_TITLE = os.environ.get('GEDGO_SITE_TITLE', 'My Genealogy Site')
+GEDGO_RESEARCH_FILE_STORAGE = os.environ.get('GEDGO_RESEARCH_FILE_STORAGE', 'gedgo.storages.FileSystemSearchableStorage')
+GEDGO_RESEARCH_FILE_ROOT = os.environ.get('GEDGO_RESEARCH_FILE_ROOT', '/app/.files/gedcom/')
+GEDGO_DOCUMENTARY_STORAGE = os.environ.get('GEDGO_DOCUMENTARY_STORAGE', 'gedgo.storages.FileSystemSearchableStorage')
+GEDGO_DOCUMENTARY_ROOT = os.environ.get('GEDGO_DOCUMENTARY_ROOT', '/app/.files/documentaries/')
+GEDGO_GEDCOM_FILE_STORAGE = os.environ.get('GEDGO_GEDCOM_FILE_STORAGE', 'gedgo.storages.FileSystemSearchableStorage')
+GEDGO_GEDCOM_FILE_ROOT = os.environ.get('GEDGO_GEDCOM_FILE_ROOT', '/app/.files/research/')
+GEDGO_SHOW_RESEARCH_FILES = os.environ.get('GEDGO_SHOW_RESEARCH_FILES', 'True') == 'True'
+
+EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+SERVER_EMAIL = ['noreply@example.com']
+if os.environ.get('EMAIL_HOST') and not DEBUG:
+ EMAIL_BACKEND = os.environ.get(
+ 'EMAIL_BACKEND',
+ 'django.core.mail.backends.smtp.EmailBackend'
+ )
+ EMAIL_USE_TLS = True
+ EMAIL_PORT = 587
+ EMAIL_HOST = os.environ.get('EMAIL_HOST')
+ EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
+ EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
+ DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', EMAIL_HOST_USER)
+ SERVER_EMAIL = os.environ.get('SERVER_EMAIL', EMAIL_HOST_USER)
diff --git a/urls.py b/urls.py
index 10c6c96..dc76d98 100644
--- a/urls.py
+++ b/urls.py
@@ -2,17 +2,17 @@
from django.shortcuts import redirect
from django.http import HttpResponse
from django.contrib import admin
-from django.contrib.auth.views import login
+from django.contrib.auth.views import LoginView
admin.autodiscover()
urlpatterns = [
url(r'^$', lambda r: redirect('/gedgo/')),
url(r'^gedgo/', include('gedgo.urls')),
- url(r'^admin/', include(admin.site.urls)),
- url(r'^accounts/login/$', login,
+ url(r'^admin/', admin.site.urls),
+ url(r'^accounts/login/$', LoginView.as_view(),
{'template_name': 'auth/login.html'}),
- url(r'^login/$', login,
+ url(r'^login/$', LoginView.as_view(),
{'template_name': 'auth/login.html'}),
url(r'^robots\.txt$',
lambda r: HttpResponse(
diff --git a/wsgi.py b/wsgi.py
deleted file mode 100644
index 6205d83..0000000
--- a/wsgi.py
+++ /dev/null
@@ -1,7 +0,0 @@
-import os
-from django.core.wsgi import get_wsgi_application
-from dj_static import Cling
-
-os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
-
-application = Cling(get_wsgi_application())