]> gitweb.fperrin.net Git - djsite.git/commitdiff
Add a 'work' concept
authorFrédéric Perrin <fred@fperrin.net>
Thu, 6 Oct 2016 00:16:35 +0000 (01:16 +0100)
committerFrédéric Perrin <fred@fperrin.net>
Sun, 9 Oct 2016 22:17:52 +0000 (23:17 +0100)
17 files changed:
.gitignore
quotes/admin.py
quotes/migrations/0001_initial.py
quotes/migrations/0002_context_author.py [moved from quotes/migrations/0002_author_notes.py with 62% similarity]
quotes/migrations/0003_auto_20160928_2223.py [deleted file]
quotes/migrations/0003_auto_20161009_2254.py [new file with mode: 0644]
quotes/migrations/0004_auto_20161009_2255.py [new file with mode: 0644]
quotes/models.py
quotes/static/quotes/quotes.js
quotes/static/quotes/style.css
quotes/templates/quotes/author.html
quotes/templates/quotes/display.html
quotes/templates/quotes/work.html [new file with mode: 0644]
quotes/templates/quotes/work_notes.html [new file with mode: 0644]
quotes/tests.py
quotes/urls.py
quotes/views.py

index abcb2da86af7143d37841f597f0c759d8bd01d2c..82f48d08d4961b0c1e8142105f84594660397d1b 100644 (file)
@@ -1,3 +1,4 @@
 *.pyc
 db.sqlite3
 *~
+.cache/v/cache/lastfailed
index 61caaab1fb8087db04286529e14af258dad8a6d8..7e8f1adfb3a778ba4174032b6b06b8215e300ecd 100644 (file)
@@ -1,7 +1,8 @@
 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)
index 9a4e831373cd4ae4077c0a564a547b1daa7347f9..c7f97c4a58b7a20b0ae950315b5a987e161ac993 100644 (file)
@@ -14,7 +14,21 @@ class Migration(migrations.Migration):
             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(
@@ -22,7 +36,9 @@ class Migration(migrations.Migration):
             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(
@@ -31,13 +47,20 @@ class Migration(migrations.Migration):
                 ('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),
+        ),
     ]
similarity index 62%
rename from quotes/migrations/0002_author_notes.py
rename to quotes/migrations/0002_context_author.py
index d7cc2f4e3cb457726851662a072b8e28ac9dc0ae..79ca5e31a0316e0d4186af9eac4bd3280c53dce8 100644 (file)
@@ -12,8 +12,9 @@ class Migration(migrations.Migration):
 
     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,
         ),
     ]
diff --git a/quotes/migrations/0003_auto_20160928_2223.py b/quotes/migrations/0003_auto_20160928_2223.py
deleted file mode 100644 (file)
index dc62f34..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- 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),
-        ),
-    ]
diff --git a/quotes/migrations/0003_auto_20161009_2254.py b/quotes/migrations/0003_auto_20161009_2254.py
new file mode 100644 (file)
index 0000000..b3b0366
--- /dev/null
@@ -0,0 +1,42 @@
+# -*- 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',
+        ),
+    ]
diff --git a/quotes/migrations/0004_auto_20161009_2255.py b/quotes/migrations/0004_auto_20161009_2255.py
new file mode 100644 (file)
index 0000000..cf435ab
--- /dev/null
@@ -0,0 +1,19 @@
+# -*- 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',
+        ),
+    ]
index 53afcbe246a20f61e2c1c03767de029739540fca..9c65abc3c42862939534360a08ba0456ed98b8a9 100644 (file)
@@ -2,29 +2,80 @@ from django.db import models
 
 # 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
index ea4ba8ae8bf5715ccc108cd59af471e6954c8567..4ae5cd99b868773ac4f6d0116f87abcccb3f1f80 100644 (file)
@@ -1,9 +1,13 @@
 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';
 }
index 6898422c211255b696c03672593501c7a5c26ca4..e49678241fcccf381491fd186e3b291c28bcbfae 100644 (file)
@@ -22,7 +22,7 @@ a:hover {
     text-align: right;
 }
 
-.quote .author .name {
+.quote .author .work_name {
     font-style: italic;
 }
 
@@ -33,7 +33,13 @@ a:hover {
 
 .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 {
index 60dbafa577c45b4cf83ff3ef9354d096b745b5ff..be5476e17b448194a922b1d0ffdde0aed06af73d 100644 (file)
 
 <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 %}
index 2b1f03c31a0881fd7cba983bbb4f8447ef65f339..fcf5906dc5ec720ac4909ce7310143ac90422f93 100644 (file)
          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">
diff --git a/quotes/templates/quotes/work.html b/quotes/templates/quotes/work.html
new file mode 100644 (file)
index 0000000..fca2548
--- /dev/null
@@ -0,0 +1,13 @@
+{% 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 %}
diff --git a/quotes/templates/quotes/work_notes.html b/quotes/templates/quotes/work_notes.html
new file mode 100644 (file)
index 0000000..995c89c
--- /dev/null
@@ -0,0 +1,5 @@
+{% if work.notes %}
+<div class="work_notes">
+  <p>{{ work.notes|safe }}</p>
+</div>
+{% endif %}
index 00237fa2ec9fdfc4e30e66309f6400354cec74f4..3836f4e5e829dce28360b52e25235f0b2215dc40 100644 (file)
@@ -2,19 +2,19 @@ import sys
 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):
@@ -26,21 +26,32 @@ class ViewsTest(TestCase):
 
     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")
@@ -53,8 +64,13 @@ class ViewsTest(TestCase):
         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)
 
@@ -73,7 +89,7 @@ class ViewsTest(TestCase):
         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)        
@@ -86,4 +102,4 @@ class ViewsTest(TestCase):
         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)
index 014b6901fae599d46c09fc4f429fddb6a04c78f1..3e905bc5393db6a99318fa8b9be39570922412dd 100644 (file)
@@ -4,8 +4,10 @@ from . import views
 
 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"),
 ]
index 981af054515b7817efb40daae2138b7d34de0b1a..bd7f6f605bb142cd44ad196368fa252162dd421c 100644 (file)
@@ -4,7 +4,7 @@ from django.template import loader
 
 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):
@@ -26,6 +26,11 @@ def author(request, author_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 }