*.pyc
db.sqlite3
*~
+.cache/v/cache/lastfailed
from django.contrib import admin
# Register your models here.
-from .models import Author, Quote, Tag
-admin.site.register(Author)
+from .models import Tag, Author, Work, Quote
admin.site.register(Tag)
+admin.site.register(Author)
+admin.site.register(Work)
admin.site.register(Quote)
name='Author',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('name', models.CharField(max_length=100)),
+ ('name', models.CharField(help_text=b'Name of the author', max_length=100)),
+ ('notes', models.TextField(help_text=b'Notes about the author; may be left blank. Will not be HTML-escaped.', blank=True)),
+ ('pvt_notes', models.TextField(help_text=b'Notes about the author; not displayed on the public interface', blank=True)),
+ ('birth_date', models.DateField(help_text=b'Date of birth', null=True, blank=True)),
+ ('death_date', models.DateField(help_text=b'Date of death (leave blank if still alive!)', null=True, blank=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Context',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(help_text=b'Name of the context for the quote (title of the work or speech it appears in)', max_length=100)),
+ ('date', models.DateField(help_text=b'Date of the quote', null=True, blank=True)),
+ ('notes', models.TextField(help_text=b'Notes about the work; may be left blank. Will not be HTML-escaped. XXX: offer a WYSIWYG editor', blank=True)),
+ ('pvt_notes', models.TextField(help_text=b'Notes about the work; not displayed on the public interface', blank=True)),
],
),
migrations.CreateModel(
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('text', models.TextField()),
- ('author', models.ForeignKey(to='quotes.Author')),
+ ('notes', models.TextField(help_text=b'Notes about the quote; may be left blank. Will not be HTML-escaped. XXX: offer a WYSIWYG editor', blank=True)),
+ ('pvt_notes', models.TextField(help_text=b'Notes about the quote; not displayed on the public interface', blank=True)),
+ ('context', models.ForeignKey(to='quotes.Context')),
],
),
migrations.CreateModel(
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('tag', models.CharField(max_length=100)),
],
- options={
- 'ordering': ('tag',),
- },
),
migrations.AddField(
model_name='quote',
name='tags',
field=models.ManyToManyField(to='quotes.Tag', blank=True),
),
+ migrations.AddField(
+ model_name='context',
+ name='tags',
+ field=models.ManyToManyField(to='quotes.Tag', blank=True),
+ ),
+ migrations.AddField(
+ model_name='author',
+ name='tags',
+ field=models.ManyToManyField(to='quotes.Tag', blank=True),
+ ),
]
operations = [
migrations.AddField(
- model_name='author',
- name='notes',
- field=models.TextField(blank=True),
+ model_name='context',
+ name='author',
+ field=models.ForeignKey(default=1, to='quotes.Author'),
+ preserve_default=False,
),
]
+++ /dev/null
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('quotes', '0002_author_notes'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='author',
- name='notes',
- field=models.TextField(help_text=b'Notes for the author; may be left blank. Will not be HTML-escaped.', blank=True),
- ),
- ]
--- /dev/null
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('quotes', '0002_context_author'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Work',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(help_text=b'Name of the context for the quote (title of the work or speech it appears in)', max_length=100)),
+ ('date', models.DateField(help_text=b'Date of the quote', null=True, blank=True)),
+ ('notes', models.TextField(help_text=b'Notes about the work; may be left blank. Will not be HTML-escaped. XXX: offer a WYSIWYG editor', blank=True)),
+ ('pvt_notes', models.TextField(help_text=b'Notes about the work; not displayed on the public interface', blank=True)),
+ ('author', models.ForeignKey(to='quotes.Author')),
+ ('tags', models.ManyToManyField(to='quotes.Tag', blank=True)),
+ ],
+ ),
+ migrations.RemoveField(
+ model_name='context',
+ name='author',
+ ),
+ migrations.RemoveField(
+ model_name='context',
+ name='tags',
+ ),
+ migrations.AlterField(
+ model_name='quote',
+ name='context',
+ field=models.ForeignKey(to='quotes.Work'),
+ ),
+ migrations.DeleteModel(
+ name='Context',
+ ),
+ ]
--- /dev/null
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('quotes', '0003_auto_20161009_2254'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='quote',
+ old_name='context',
+ new_name='work',
+ ),
+ ]
# Create your models here.
+class Tag(models.Model):
+ tag = models.CharField(max_length=100)
+ def __unicode__(self):
+ return self.tag
+
class Author(models.Model):
- name = models.CharField(max_length=100)
+ name = models.CharField(max_length=100,
+ help_text="Name of the author")
notes = models.TextField(blank=True, help_text= \
- "Notes for the author; may be left blank. Will \
+ "Notes about the author; may be left blank. Will \
not be HTML-escaped.")
+ pvt_notes = models.TextField(blank=True, help_text= \
+ "Notes about the author; not displayed on \
+ the public interface")
+ tags = models.ManyToManyField(Tag, blank=True)
+
+ birth_date = models.DateField(blank=True, null=True,
+ help_text="Date of birth")
+ death_date = models.DateField(blank=True, null=True,
+ help_text="Date of death (leave blank \
+ if still alive!)")
def __unicode__(self):
return self.name
-class Tag(models.Model):
- tag = models.CharField(max_length=100)
+# class DatePrecision(models.CharField):
+# DAY = "D"
+# MONTH = "M"
+# YEAR = "Y"
+# DECADE = "X"
+# CENTURY = "C"
+# UNKNOWN = "?"
+# PRECISION_CHOICES = [
+# (DAY, "Day"),
+# (MONTH, "Month"),
+# (YEAR, "Year"),
+# (DECADE, "Decade"),
+# (CENTURY, "Century"),
+# (UNKNOWN, "Unknown"),
+# ]
+# def __init__(self, **kwargs):
+# models.CharField(max_length=1, choices=self.PRECISION_CHOICES,
+# default=self.DAY, **kwargs)
- def __unicode__(self):
- return self.tag
+class Work(models.Model):
+ name = models.CharField(max_length=100,
+ help_text="Name of the context for the quote \
+ (title of the work or speech it appears in)")
+ author = models.ForeignKey(Author)
+ date = models.DateField(blank=True, null=True,
+ help_text="Date of the quote")
+ tags = models.ManyToManyField(Tag, blank=True)
+
+ notes = models.TextField(blank=True, help_text= \
+ "Notes about the work; may be left blank. Will \
+ not be HTML-escaped. XXX: offer a WYSIWYG editor")
+ pvt_notes = models.TextField(blank=True, help_text= \
+ "Notes about the work; not displayed on \
+ the public interface")
- class Meta:
- ordering = ("tag", )
+ def __unicode__(self):
+ return "%s: %s (%s)" % (self.author.name, self.name, self.date)
class Quote(models.Model):
text = models.TextField()
- author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag, blank=True)
+ work = models.ForeignKey(Work)
- def __unicode__(self):
- return self.author.name + ": " + self.text
+ notes = models.TextField(blank=True, help_text= \
+ "Notes about the quote; may be left blank. Will \
+ not be HTML-escaped. XXX: offer a WYSIWYG editor")
+ pvt_notes = models.TextField(blank=True, help_text= \
+ "Notes about the quote; not displayed on \
+ the public interface")
+ def __unicode__(self):
+ return self.work.author.name + ": " + self.text
function showDetails(id) {
- document.getElementById(id).style.display = 'block';
+ document.getElementById(id).style.height = 'auto';
+ // document.getElementById(id).style.visibility = 'visible';
+ // document.getElementById(id).style.opacity = '1';
document.getElementById(id + '_button').style.display = 'none';
}
function hideDetails(id) {
- document.getElementById(id).style.display = 'none';
+ document.getElementById(id).style.maxHeight = '0';
+ // document.getElementById(id).style.opacity = '0';
+ // document.getElementById(id).style.visibility = 'hidden';
document.getElementById(id + '_button').style.display = 'block';
}
text-align: right;
}
-.quote .author .name {
+.quote .author .work_name {
font-style: italic;
}
.hidden_details {
/* displayed through Javascript */
- display: none;
+ height: 0;
+ overflow: hidden;
+ transition: height 3s linear;
+ /* visibility: hidden; */
+ /* opacity: 0; */
+ /* transition: opacity 2s linear; */
+
}
.tag_link {
<p>All the quotes for {{ author.name }}:<p>
-{% for quote in author.quote_set.all %}
- {% include "quotes/display.html" with quote=quote skip_author_notes=True %}
+{% for work in author.work_set.all %}
+ {% for quote in work.quote_set.all %}
+ {% include "quotes/display.html" with quote=quote skip_author_notes=True %}
+ {% endfor %}
{% endfor %}
{% endblock %}
Hide details</a></p>
<p class="author">
— <span class="name">
- <a href="{% url 'quotes:author' quote.author.id %}">
- {{ quote.author.name }}
- </a>
+ <a href="{% url 'quotes:author' quote.work.author.id %}"
+ class="author_name">{{ quote.work.author.name }}</a>,
+ <a href="{% url 'quotes:work' quote.work.id %}"
+ class="work_name">{{ quote.work.name }}</a>
</span>
</p>
+ <div class="quote_notes">
+ <p>{{ quote.notes|safe }}</p>
+ </div>
+
+ {% if not skip_work_notes %}
+ {% include "quotes/work_notes.html" with work=quote.work %}
+ {% endif %}
{% if not skip_author_notes %}
- {% include "quotes/author_notes.html" with author=quote.author %}
+ {% include "quotes/author_notes.html" with author=quote.work.author %}
{% endif %}
+
{% if quote.tags.all %}
<p class="tags">
--- /dev/null
+{% extends 'quotes/base.html' %}
+{% block title %}Quotes for {{ work.name }} by {{ work.author.name }}{% endblock %}
+{% block body %}
+
+<h1>{{ work.name }} by {{ work.author.name }}</h1>
+{% include "quotes/work_notes.html" with work=work %}
+{% include "quotes/author_notes.html" with author=work.author %}
+
+<p>All the quotes for {{ work.name }}:<p>
+{% for quote in work.quote_set.all %}
+ {% include "quotes/display.html" with quote=quote skip_author_notes=True skip_work_notes=True %}
+{% endfor %}
+{% endblock %}
--- /dev/null
+{% if work.notes %}
+<div class="work_notes">
+ <p>{{ work.notes|safe }}</p>
+</div>
+{% endif %}
from django.test import TestCase, Client
# Create your tests here.
-from .models import Author, Tag, Quote
+from .models import Tag, Author, Work, Quote
class QuoteTest(TestCase):
def setUp(self):
a1 = Author.objects.create(name="JFK")
- q1 = Quote.objects.create(text="Ich bin...", author=a1)
+ w1 = Work.objects.create(name="Berlin speech", author=a1)
+ q1 = Quote.objects.create(text="Ich bin...", work=w1)
def test_one(self):
q = Quote.objects.filter(text__startswith="Ich")
self.assertEqual(q.count(), 1)
q = q[0]
- self.assertEqual(q.author.name, "JFK")
-
+ self.assertEqual(q.work.author.name, "JFK")
class ViewsTest(TestCase):
def getPage(self, url, exp_status=200):
def setUp(self):
a1 = Author.objects.create(name="Author with notes",
- notes="Some notes")
+ notes="Some notes for the author")
+ w1 = Work.objects.create(name="Context with some notes",
+ author=a1,
+ notes="Some notes for the work")
+ q1 = Quote.objects.create(text="Quote01, two tags",
+ work=w1,
+ notes="Some notes for the quote")
t1 = Tag.objects.create(tag="tag01-1")
t2 = Tag.objects.create(tag="tag01-2")
- q1 = Quote.objects.create(text="Quote01, two tags", author=a1)
q1.tags.add(t1, t2)
a2 = Author.objects.create(name="Author without notes")
- q2= Quote.objects.create(text="Quote02, no tags", author=a2)
+ w2 = Work.objects.create(name="Work without notes", author=a2)
+ q2 = Quote.objects.create(text="Quote02, no tags", work=w2)
def test_all(self):
content = self.getPage('all/')
for a in Author.objects.all():
- self.assertTrue(a.name in content)
+ self.assertTrue(a.name in content, msg=content)
+ self.assertTrue(a.notes in content, msg=content)
+ for w in Work.objects.all():
+ self.assertTrue(w.name in content)
+ self.assertTrue(w.notes in content, content)
for q in Quote.objects.all():
- self.assertTrue(q.text in content)
+ self.assertTrue(q.text in content, content)
+ self.assertTrue(q.notes in content, content)
def test_views_all_data(self):
q = Quote.objects.filter(text__startswith="Quote01")
self.assertTrue("author_notes" in quotepage)
self.assertEqual(quotepage.count("tag_link"), 2)
+ # check the work page
+ workpage = self.getPage('work/%s/' % q.work.id)
+ self.assertTrue(q.text in workpage)
+ self.assertTrue("work_notes" in workpage)
+
# check the author page
- authorpage = self.getPage('author/%s/' % q.author.id)
+ authorpage = self.getPage('author/%s/' % q.work.author.id)
self.assertTrue(q.text in authorpage)
self.assertTrue("author_notes" in authorpage)
self.assertFalse("author_notes" in quotepage)
self.assertEqual(quotepage.count("tag_link"), 0)
- authorpage = self.getPage('author/%s/' % q.author.id)
+ authorpage = self.getPage('author/%s/' % q.work.author.id)
self.assertTrue(q.text in authorpage)
self.assertFalse("author_notes" in authorpage)
self.assertFalse('Quote01, two tags' in authorpage)
a = a[0]
authorpage = self.getPage('author/%s/' % a.id)
- self.assertEqual(authorpage.count("Some notes"), 1)
+ self.assertEqual(authorpage.count("Some notes for the author"), 1)
urlpatterns = [
url(r'^$', views.random, name='random'),
+ url(r'^random$', views.random, name='random'),
url(r'^show/(?P<quote_id>[0-9]+)/$', views.onequote, name="onequote"),
url(r'^tag/(?P<tag_id>[0-9]+)/$', views.tags, name="tags"),
url(r'^author/(?P<author_id>[0-9]+)/$', views.author, name="author"),
+ url(r'^work/(?P<work_id>[0-9]+)/$', views.work, name="work"),
url(r'^all/$', views.all, name="all"),
]
from random import randint
-from .models import Author, Quote, Tag
+from .models import Author, Work, Quote, Tag
# Create your views here.
def onequote(request, quote_id):
context = { 'author' : author }
return render(request, 'quotes/author.html', context)
+def work(request, work_id):
+ work = Work.objects.get(id=work_id)
+ context = { 'work': work }
+ return render(request, 'quotes/work.html', context)
+
def all(request):
quotes = Quote.objects.all()
context = { 'quotes' : quotes }