From 5a4ee961abcd09b127a898587a8b2e530b9064e2 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 18 May 2018 19:53:24 +1200 Subject: [PATCH 01/35] Update psycopg2 from 2.7.3.1 to 2.7.4 --- requirements/production.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/production.txt b/requirements/production.txt index eddb40e..aa20ba8 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -3,7 +3,7 @@ -r base.txt # Python-PostgreSQL Database Adapter -psycopg2==2.7.3.1 +psycopg2==2.7.4 gunicorn==19.7.1 # WSGI Handler From 5aa24a7570456e3c85557af024da6425490152b6 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 18 May 2018 19:53:26 +1200 Subject: [PATCH 02/35] Update gunicorn from 19.7.1 to 19.8.1 --- requirements/production.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/production.txt b/requirements/production.txt index aa20ba8..d5d3c75 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -5,5 +5,5 @@ # Python-PostgreSQL Database Adapter psycopg2==2.7.4 -gunicorn==19.7.1 +gunicorn==19.8.1 # WSGI Handler From 893e5fe6c1cf41733baa8a1b9ddaa1ad89011434 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 18 May 2018 19:53:27 +1200 Subject: [PATCH 03/35] Update django from 2.0.4 to 2.0.5 --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index dc4fd48..f405c50 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,7 +1,7 @@ # Base dependencies go here # Django -django==2.0.4 +django==2.0.5 whitenoise==3.3.1 django-heroku==0.3.1 django-environ==0.4.4 From d272d3475f7ee0aaa6f9c5f939c32c23360a28ee Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 18 May 2018 19:53:29 +1200 Subject: [PATCH 04/35] Update python-markdown-math from 0.3 to 0.5 --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index f405c50..be93c27 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -9,7 +9,7 @@ django-bootstrap-breadcrumbs==0.9.1 # Content Loading verto==0.7.4 -python-markdown-math==0.3 +python-markdown-math==0.5 PyYAML==3.12 # Password storage From adf9c8f4c68a3bced541d3eeadc0b026bdbb6c8a Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 18 May 2018 19:53:30 +1200 Subject: [PATCH 05/35] Update django-allauth from 0.35.0 to 0.36.0 --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index be93c27..8e1ab9d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -16,6 +16,6 @@ PyYAML==3.12 argon2-cffi==18.1.0 # Users -django-allauth==0.35.0 +django-allauth==0.36.0 django-crispy-forms==1.7.2 django-anymail==2.0 From d168d296d9c7cf5bb2b2f294e6e56cc8660ad278 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 18 May 2018 19:53:32 +1200 Subject: [PATCH 06/35] Update django-anymail from 2.0 to 2.2 --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 8e1ab9d..1d8a80d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -18,4 +18,4 @@ argon2-cffi==18.1.0 # Users django-allauth==0.36.0 django-crispy-forms==1.7.2 -django-anymail==2.0 +django-anymail==2.2 From 4040cebfffdb59012816f102671d689fdf69242c Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 18 May 2018 19:53:33 +1200 Subject: [PATCH 07/35] Update django-debug-toolbar from 1.8 to 1.9.1 --- requirements/local.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/local.txt b/requirements/local.txt index db41787..c0254a6 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -3,4 +3,4 @@ -r testing.txt # Debugging Tools -django-debug-toolbar==1.8 +django-debug-toolbar==1.9.1 From 3bebf612d67d55ac6d56c4d11a503e0318a01b8b Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Fri, 18 May 2018 22:37:10 +1200 Subject: [PATCH 08/35] Add basic files application --- config/settings/base.py | 1 + config/urls.py | 1 + files/__init__.py | 1 + files/admin.py | 11 ++++++ files/apps.py | 9 +++++ files/migrations/0001_initial.py | 41 +++++++++++++++++++++++ files/migrations/__init__.py | 1 + files/models.py | 47 ++++++++++++++++++++++++++ files/urls.py | 29 ++++++++++++++++ files/views.py | 15 +++++++++ templates/base.html | 3 ++ templates/files/index.html | 57 ++++++++++++++++++++++++++++++++ 12 files changed, 216 insertions(+) create mode 100644 files/__init__.py create mode 100644 files/admin.py create mode 100644 files/apps.py create mode 100644 files/migrations/0001_initial.py create mode 100644 files/migrations/__init__.py create mode 100644 files/models.py create mode 100644 files/urls.py create mode 100644 files/views.py create mode 100644 templates/files/index.html diff --git a/config/settings/base.py b/config/settings/base.py index d685df6..79a4eec 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -34,6 +34,7 @@ # Apps specific for this project go here. LOCAL_APPS = [ "pikau.apps.PikauConfig", + "files.apps.FilesConfig", ] # See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps diff --git a/config/urls.py b/config/urls.py index bdd5a1c..81a9570 100644 --- a/config/urls.py +++ b/config/urls.py @@ -26,6 +26,7 @@ path("faq/", views.FAQView.as_view(), name="faq"), path("contact/", views.ContactView.as_view(), name="contact"), path("pikau/", include("pikau.urls")), + path("files/", include("files.urls")), path("accounts/", include("allauth.urls")), path("admin/", admin.site.urls), ] diff --git a/files/__init__.py b/files/__init__.py new file mode 100644 index 0000000..3ecb6e1 --- /dev/null +++ b/files/__init__.py @@ -0,0 +1 @@ +"""Module for the files application.""" diff --git a/files/admin.py b/files/admin.py new file mode 100644 index 0000000..c0d78a2 --- /dev/null +++ b/files/admin.py @@ -0,0 +1,11 @@ +"""Admin configuration for the files application.""" + +from django.contrib import admin +from files.models import ( + File, + Licence, +) + + +admin.site.register(File) +admin.site.register(Licence) diff --git a/files/apps.py b/files/apps.py new file mode 100644 index 0000000..472caa4 --- /dev/null +++ b/files/apps.py @@ -0,0 +1,9 @@ +"""Application configuration for the files application.""" + +from django.apps import AppConfig + + +class FilesConfig(AppConfig): + """Configuration object for the files application.""" + + name = "files" diff --git a/files/migrations/0001_initial.py b/files/migrations/0001_initial.py new file mode 100644 index 0000000..e65d660 --- /dev/null +++ b/files/migrations/0001_initial.py @@ -0,0 +1,41 @@ +# Generated by Django 2.0.4 on 2018-05-18 09:16 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='File', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(unique=True)), + ('filename', models.CharField(max_length=200)), + ('description', models.TextField(blank=True)), + ('location', models.URLField()), + ], + ), + migrations.CreateModel( + name='Licence', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200, unique=True)), + ('url', models.URLField()), + ], + options={ + 'ordering': ('name',), + }, + ), + migrations.AddField( + model_name='file', + name='licence', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='files', to='files.Licence'), + ), + ] diff --git a/files/migrations/__init__.py b/files/migrations/__init__.py new file mode 100644 index 0000000..b9e1c84 --- /dev/null +++ b/files/migrations/__init__.py @@ -0,0 +1 @@ +"""Migrations for the files application.""" diff --git a/files/models.py b/files/models.py new file mode 100644 index 0000000..8307101 --- /dev/null +++ b/files/models.py @@ -0,0 +1,47 @@ +"""Models for the files application.""" + +from django.db import models + + +class Licence(models.Model): + """Model for licence.""" + + name = models.CharField(max_length=200, unique=True) + url = models.URLField() + + class Meta: + """Set consistent ordering of licences.""" + + ordering = ("name", ) + + def __str__(self): + """Text representation of Licence object. + + Returns: + String describing licence. + """ + return self.name + + +class File(models.Model): + """Model for file.""" + + slug = models.SlugField(unique=True) + filename = models.CharField(max_length=200) + description = models.TextField(blank=True) + location = models.URLField() + licence = models.ForeignKey( + Licence, + on_delete=models.CASCADE, + related_name="files", + blank=True, + null=True, + ) + + def __str__(self): + """Text representation of File object. + + Returns: + String describing file. + """ + return self.filename diff --git a/files/urls.py b/files/urls.py new file mode 100644 index 0000000..fd220d2 --- /dev/null +++ b/files/urls.py @@ -0,0 +1,29 @@ +"""URL routing for the files application. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" + +from django.urls import path +from . import views + +app_name = "files" + +urlpatterns = [ + # eg: /files/ + path( + "", + views.FileList.as_view(), + name="index" + ), +] diff --git a/files/views.py b/files/views.py new file mode 100644 index 0000000..7027323 --- /dev/null +++ b/files/views.py @@ -0,0 +1,15 @@ +"""Views for the files application.""" + +from django.views import generic +from django.contrib.auth.mixins import LoginRequiredMixin +from files.models import ( + File, +) + + +class FileList(LoginRequiredMixin, generic.ListView): + """View for the file list page.""" + + template_name = "files/index.html" + context_object_name = "files" + model = File diff --git a/templates/base.html b/templates/base.html index 2165100..94fd338 100644 --- a/templates/base.html +++ b/templates/base.html @@ -36,6 +36,9 @@ {% trans "Pīkau" %} + + {% trans "Files" %} + + + +
+ +
+

Files

+

Lists files used within this project with their licences.

+ + View files
From 5afc2fe28a9b1d47cdc11a862a25d6ddcf7f425e Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Mon, 21 May 2018 16:20:17 +1200 Subject: [PATCH 23/35] Squash files migrations --- files/migrations/0001_initial.py | 5 +++-- files/migrations/0002_auto_20180521_1341.py | 20 -------------------- files/migrations/0003_auto_20180521_1503.py | 20 -------------------- 3 files changed, 3 insertions(+), 42 deletions(-) delete mode 100644 files/migrations/0002_auto_20180521_1341.py delete mode 100644 files/migrations/0003_auto_20180521_1503.py diff --git a/files/migrations/0001_initial.py b/files/migrations/0001_initial.py index e65d660..2db3918 100644 --- a/files/migrations/0001_initial.py +++ b/files/migrations/0001_initial.py @@ -1,7 +1,8 @@ -# Generated by Django 2.0.4 on 2018-05-18 09:16 +# Generated by Django 2.0.5 on 2018-05-21 04:19 from django.db import migrations, models import django.db.models.deletion +import files.models class Migration(migrations.Migration): @@ -36,6 +37,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='file', name='licence', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='files', to='files.Licence'), + field=models.ForeignKey(default=files.models.default_licence, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='files', to='files.Licence'), ), ] diff --git a/files/migrations/0002_auto_20180521_1341.py b/files/migrations/0002_auto_20180521_1341.py deleted file mode 100644 index b117154..0000000 --- a/files/migrations/0002_auto_20180521_1341.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.0.5 on 2018-05-21 01:41 - -from django.db import migrations, models -import django.db.models.deletion -import files.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('files', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='file', - name='licence', - field=models.ForeignKey(default=files.models.default_licence, on_delete=django.db.models.deletion.CASCADE, related_name='files', to='files.Licence'), - ), - ] diff --git a/files/migrations/0003_auto_20180521_1503.py b/files/migrations/0003_auto_20180521_1503.py deleted file mode 100644 index 1c18b55..0000000 --- a/files/migrations/0003_auto_20180521_1503.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.0.5 on 2018-05-21 03:03 - -from django.db import migrations, models -import django.db.models.deletion -import files.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('files', '0002_auto_20180521_1341'), - ] - - operations = [ - migrations.AlterField( - model_name='file', - name='licence', - field=models.ForeignKey(default=files.models.default_licence, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='files', to='files.Licence'), - ), - ] From 23e4f1291a5381e6369d28c49a3596d229909652 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Mon, 21 May 2018 21:43:35 +1200 Subject: [PATCH 24/35] Restructuring of CRUD views, urls, etc --- pikau/forms.py | 19 ---- pikau/mixins.py | 41 +++++++ pikau/urls.py | 76 ++++++++----- pikau/views.py | 106 +++++++++++++----- templates/base.html | 12 ++ templates/pikau/glossary_form_create.html | 22 ++++ templates/pikau/glossary_form_delete.html | 22 ++++ templates/pikau/glossary_form_update.html | 22 ++++ templates/pikau/index.html | 2 +- templates/pikau/pikaucourse_detail.html | 2 +- ...topic_form.html => topic_form_create.html} | 7 +- ...irm_delete.html => topic_form_delete.html} | 0 templates/pikau/topic_form_update.html | 22 ++++ 13 files changed, 275 insertions(+), 78 deletions(-) delete mode 100644 pikau/forms.py create mode 100644 pikau/mixins.py create mode 100644 templates/pikau/glossary_form_create.html create mode 100644 templates/pikau/glossary_form_delete.html create mode 100644 templates/pikau/glossary_form_update.html rename templates/pikau/{topic_form.html => topic_form_create.html} (76%) rename templates/pikau/{topic_confirm_delete.html => topic_form_delete.html} (100%) create mode 100644 templates/pikau/topic_form_update.html diff --git a/pikau/forms.py b/pikau/forms.py deleted file mode 100644 index f0fc6b6..0000000 --- a/pikau/forms.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.forms import ModelForm, ValidationError -from django.utils.text import slugify -from pikau.models import Topic - -class TopicForm(ModelForm): - """Base form for editing a topic.""" - - class Meta: - model = Topic - fields = ["name"] - - def save(self): - instance = super().save(commit=False) - slug = slugify(instance.name) - if Topic.objects.filter(slug=slug): - raise ValidationError("Topic already exists with matching slug, please change topic name") - instance.slug = slug - instance.save() - return instance diff --git a/pikau/mixins.py b/pikau/mixins.py new file mode 100644 index 0000000..60886ec --- /dev/null +++ b/pikau/mixins.py @@ -0,0 +1,41 @@ +"""Mixins for pikau application.""" + +from django.contrib import messages + + +class SuccessMessageDeleteMixin(object): + + def delete(self, request, *args, **kwargs): + messages.success(self.request, self.success_message) + return super().delete(request, *args, **kwargs) + + +class GlossaryActionMixin(object): + """Sets fields for pages relating to actions of glossary items.""" + + fields = ("term", "description", "slug") + + +# class MilestoneActionMixin(SuccessMessageMixin): +# +# fields = ("name", "date") + + +# class GoalActionMixin(SuccessMessageMixin): +# +# fields = ("description", "slug") + + +# class TagActionMixin(SuccessMessageMixin): +# +# fields = ("name", "description", "slug") +# + +class TopicActionMixin(object): + + fields = ("name", "slug") + + +# class LevelActionMixin(SuccessMessageMixin): +# +# fields = ("name", "slug") diff --git a/pikau/urls.py b/pikau/urls.py index 7855801..e3b0cab 100644 --- a/pikau/urls.py +++ b/pikau/urls.py @@ -35,37 +35,55 @@ # eg: /pikau/glossary/ path( "glossary/", - views.GlossaryList.as_view(), - name="glossary" + views.GlossaryListView.as_view(), + name="glossary_list" + ), + # eg: /pikau/glossary/create/ + path( + "glossary/create/", + views.GlossaryCreateView.as_view(), + name="glossary_create" + ), + # eg: /pikau/glossary/update/term-1/ + path( + "glossary/update//", + views.GlossaryUpdateView.as_view(), + name="glossary_update" + ), + # eg: /pikau/glossary/delete/term-1/ + path( + "glossary/delete//", + views.GlossaryDeleteView.as_view(), + name="glossary_delete" ), # eg: /pikau/goals/ path( "goals/", - views.GoalList.as_view(), + views.GoalListView.as_view(), name="goal_list" ), # eg: /pikau/levels/ path( "levels/", - views.LevelList.as_view(), + views.LevelListView.as_view(), name="level_list" ), # eg: /pikau/levels/level-1/ path( "levels//", - views.LevelDetail.as_view(), + views.LevelDetailView.as_view(), name="level" ), # eg: /pikau/milestones/ path( "milestones/", - views.MilestoneList.as_view(), + views.MilestoneListView.as_view(), name="milestone_list" ), # eg: /pikau/milestones/1/ path( "milestones//", - views.MilestoneDetail.as_view(), + views.MilestoneDetailView.as_view(), name="milestone" ), # eg: /pikau/pathways/ @@ -77,85 +95,91 @@ # eg: /pikau/pikau-courses/ path( "pikau-courses/", - views.PikauCourseList.as_view(), + views.PikauCourseListView.as_view(), name="pikau_course_list" ), # eg: /pikau/pikau-courses/pikau-1/ path( "pikau-courses//", - views.PikauCourseDetail.as_view(), + views.PikauCourseDetailView.as_view(), name="pikau_course" ), # eg: /pikau/pikau-courses/pikau-1/content/ path( "pikau-courses//content/", - views.PikauCourseContent.as_view(), + views.PikauCourseContentView.as_view(), name="pikau_content" ), # eg: /pikau/pikau-courses/pikau-1/content/unit-1/ path( "pikau-courses//content//", - views.PikauUnitDetail.as_view(), + views.PikauUnitDetailView.as_view(), name="pikau_unit" ), # eg: /pikau/progress-outcomes/ path( "progress-outcomes/", - views.ProgressOutcomeList.as_view(), + views.ProgressOutcomeListView.as_view(), name="progress_outcome_list" ), # eg: /pikau/progress-outcomes/progress-outcome-1/ path( "progress-outcomes//", - views.ProgressOutcomeDetail.as_view(), + views.ProgressOutcomeDetailView.as_view(), name="progress_outcome" ), # eg: /pikau/readiness-levels/ path( "readiness-levels/", - views.ReadinessLevelList.as_view(), + views.ReadinessLevelListView.as_view(), name="readiness_level_list" ), # eg: /pikau/readiness-levels/1/ path( "readiness-levels//", - views.ReadinessLevelDetail.as_view(), + views.ReadinessLevelDetailView.as_view(), name="readiness_level" ), # eg: /pikau/tags/ path( "tags/", - views.TagList.as_view(), + views.TagListView.as_view(), name="tag_list" ), # eg: /pikau/tags/tag-1/ path( "tags//", - views.TagDetail.as_view(), + views.TagDetailView.as_view(), name="tag" ), # eg: /pikau/topics/ path( "topics/", - views.TopicList.as_view(), + views.TopicListView.as_view(), name="topic_list" ), + # eg: /pikau/topics/topic-1/ + path( + "topics/view//", + views.TopicDetailView.as_view(), + name="topic" + ), # eg: /pikau/topics/create/ path( "topics/create/", - views.TopicCreate.as_view(), + views.TopicCreateView.as_view(), name="topic_create" ), - # eg: /pikau/topics/topic-1/ + # eg: /pikau/topics/update/topic-1/ path( - "topics//", - views.TopicDetail.as_view(), - name="topic" + "topics/update//", + views.TopicUpdateView.as_view(), + name="topic_update" ), - # eg: /pikau/topics/topic-1/delete/ + # eg: /pikau/topics/delete/topic-1/ path( - "topics//delete/", - views.TopicDelete.as_view(), + "topics/delete//", + views.TopicDeleteView.as_view(), name="topic_delete" ), ] diff --git a/pikau/views.py b/pikau/views.py index dba3430..d638c76 100644 --- a/pikau/views.py +++ b/pikau/views.py @@ -1,10 +1,18 @@ """Views for the pikau application.""" from re import sub -from django.views import generic +from django.views.generic import ( + TemplateView, + ListView, + DetailView, + CreateView, + UpdateView, + DeleteView, +) from django.db.models import F from django.shortcuts import get_object_or_404 from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.messages.views import SuccessMessageMixin from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse_lazy from django.http import Http404 @@ -22,18 +30,22 @@ READINESS_LEVELS, ) from pikau.utils import pathways -from pikau.forms import TopicForm +from pikau.mixins import ( + SuccessMessageDeleteMixin, + GlossaryActionMixin, + TopicActionMixin, +) NUMBER_OF_FLAME_STAGES = 7 -class IndexView(LoginRequiredMixin, generic.TemplateView): +class IndexView(LoginRequiredMixin, TemplateView): """View for the pikau homepage that renders from a template.""" template_name = "pikau/index.html" -class DocumentationView(LoginRequiredMixin, generic.TemplateView): +class DocumentationView(LoginRequiredMixin, TemplateView): """View for the pikau documentation that renders from a template.""" template_name = "pikau/documentation.html" @@ -54,7 +66,7 @@ def get_context_data(self, **kwargs): return context -class GlossaryList(LoginRequiredMixin, generic.ListView): +class GlossaryListView(LoginRequiredMixin, ListView): """View for the glossary list page.""" template_name = "pikau/glossary.html" @@ -63,7 +75,34 @@ class GlossaryList(LoginRequiredMixin, generic.ListView): ordering = "term" -class GoalList(LoginRequiredMixin, generic.ListView): +class GlossaryCreateView(LoginRequiredMixin, SuccessMessageMixin, GlossaryActionMixin, CreateView): + """View for creating a glossary definition.""" + + model = GlossaryTerm + template_name = "pikau/glossary_form_create.html" + success_message = "Glossary definition created!" + success_url = reverse_lazy("pikau:glossary_list") + + +class GlossaryUpdateView(LoginRequiredMixin, SuccessMessageMixin, GlossaryActionMixin, UpdateView): + """View for updating a glossary definition.""" + + model = GlossaryTerm + template_name = "pikau/glossary_form_update.html" + success_message = "Glossary definition updated!" + success_url = reverse_lazy("pikau:glossary_list") + + +class GlossaryDeleteView(LoginRequiredMixin, SuccessMessageDeleteMixin, GlossaryActionMixin, DeleteView): + """View for deleting a glossary definition.""" + + model = GlossaryTerm + template_name = "pikau/glossary_form_delete.html" + success_message = "Glossary definition deleted!" + success_url = reverse_lazy("pikau:glossary_list") + + +class GoalListView(LoginRequiredMixin, ListView): """View for the goal list page.""" context_object_name = "goals" @@ -71,7 +110,7 @@ class GoalList(LoginRequiredMixin, generic.ListView): ordering = "slug" -class LevelList(LoginRequiredMixin, generic.ListView): +class LevelListView(LoginRequiredMixin, ListView): """View for the level list page.""" context_object_name = "levels" @@ -79,14 +118,14 @@ class LevelList(LoginRequiredMixin, generic.ListView): ordering = "name" -class LevelDetail(LoginRequiredMixin, generic.DetailView): +class LevelDetailView(LoginRequiredMixin, DetailView): """View for a level.""" context_object_name = "level" model = Level -class MilestoneList(LoginRequiredMixin, generic.ListView): +class MilestoneListView(LoginRequiredMixin, ListView): """View for the level list page.""" context_object_name = "milestones" @@ -122,14 +161,14 @@ def get_context_data(self, **kwargs): return context -class MilestoneDetail(LoginRequiredMixin, generic.DetailView): +class MilestoneDetailView(LoginRequiredMixin, DetailView): """View for a level.""" context_object_name = "milestone" model = Milestone -class PathwaysView(LoginRequiredMixin, generic.TemplateView): +class PathwaysView(LoginRequiredMixin, TemplateView): """View for the pikau pathway that renders from a template.""" template_name = "pikau/pathways.html" @@ -146,7 +185,7 @@ def get_context_data(self, **kwargs): return context -class PikauCourseList(LoginRequiredMixin, generic.ListView): +class PikauCourseListView(LoginRequiredMixin, ListView): """View for the pīkau course list page.""" context_object_name = "pikau_courses" @@ -164,14 +203,14 @@ def get_queryset(self): ) -class PikauCourseDetail(LoginRequiredMixin, generic.DetailView): +class PikauCourseDetailView(LoginRequiredMixin, DetailView): """View for a pīkau course.""" context_object_name = "pikau_course" model = PikauCourse -class PikauCourseContent(LoginRequiredMixin, generic.DetailView): +class PikauCourseContentView(LoginRequiredMixin, DetailView): """View for a pīkau course's content.""" context_object_name = "pikau_course" @@ -179,7 +218,7 @@ class PikauCourseContent(LoginRequiredMixin, generic.DetailView): template_name = "pikau/pikaucourse_content.html" -class PikauUnitDetail(LoginRequiredMixin, generic.DetailView): +class PikauUnitDetailView(LoginRequiredMixin, DetailView): """View for a pīkau unit.""" context_object_name = "pikau_unit" @@ -221,7 +260,7 @@ def get_context_data(self, **kwargs): return context -class ProgressOutcomeList(LoginRequiredMixin, generic.ListView): +class ProgressOutcomeListView(LoginRequiredMixin, ListView): """View for the progress outcome list page.""" context_object_name = "progress_outcomes" @@ -279,14 +318,14 @@ def get_context_data(self, **kwargs): return context -class ProgressOutcomeDetail(LoginRequiredMixin, generic.DetailView): +class ProgressOutcomeDetailView(LoginRequiredMixin, DetailView): """View for a progress outcome.""" context_object_name = "progress_outcome" model = ProgressOutcome -class ReadinessLevelList(LoginRequiredMixin, generic.TemplateView): +class ReadinessLevelListView(LoginRequiredMixin, TemplateView): """View for the readiness level list page.""" template_name = "pikau/readiness_level_list.html" @@ -305,7 +344,7 @@ def get_context_data(self, **kwargs): return context -class ReadinessLevelDetail(LoginRequiredMixin, generic.TemplateView): +class ReadinessLevelDetailView(LoginRequiredMixin, TemplateView): """View for a readiness level.""" template_name = "pikau/readiness_level_detail.html" @@ -327,7 +366,7 @@ def get_context_data(self, **kwargs): return context -class TagList(LoginRequiredMixin, generic.ListView): +class TagListView(LoginRequiredMixin, ListView): """View for the tag list page.""" context_object_name = "tags" @@ -335,14 +374,14 @@ class TagList(LoginRequiredMixin, generic.ListView): ordering = "name" -class TagDetail(LoginRequiredMixin, generic.DetailView): +class TagDetailView(LoginRequiredMixin, DetailView): """View for a tag.""" context_object_name = "tag" model = Tag -class TopicList(LoginRequiredMixin, generic.ListView): +class TopicListView(LoginRequiredMixin, ListView): """View for the topic list page.""" context_object_name = "topics" @@ -350,22 +389,33 @@ class TopicList(LoginRequiredMixin, generic.ListView): ordering = "name" -class TopicDetail(LoginRequiredMixin, generic.DetailView): +class TopicDetailView(LoginRequiredMixin, DetailView): """View for a topic.""" context_object_name = "topic" model = Topic -class TopicCreate(LoginRequiredMixin, generic.edit.CreateView): +class TopicCreateView(LoginRequiredMixin, SuccessMessageMixin, TopicActionMixin, CreateView): """View for creating a topic.""" - form_class = TopicForm - template_name = "pikau/topic_form.html" + model = Topic + template_name = "pikau/topic_form_create.html" + success_message = "Topic created!" + + +class TopicUpdateView(LoginRequiredMixin, SuccessMessageMixin, TopicActionMixin, UpdateView): + """View for updating a topic.""" + + model = Topic + template_name = "pikau/topic_form_update.html" + success_message = "Topic updated!" -class TopicDelete(LoginRequiredMixin, generic.edit.DeleteView): - """View for delete a topic.""" +class TopicDeleteView(LoginRequiredMixin, SuccessMessageDeleteMixin, TopicActionMixin, DeleteView): + """View for deleting a topic.""" model = Topic + template_name = "pikau/topic_form_delete.html" + success_message = "Topic deleted!" success_url = reverse_lazy("pikau:topic_list") diff --git a/templates/base.html b/templates/base.html index f7c74d9..85e5c04 100644 --- a/templates/base.html +++ b/templates/base.html @@ -63,6 +63,18 @@
{% render_breadcrumbs %} + + {% if messages %} + {% for message in messages %} +
+ {{ message }} + +
+ {% endfor %} + {% endif %} + {% block custom_page_heading %}

{% block page_heading %} diff --git a/templates/pikau/glossary_form_create.html b/templates/pikau/glossary_form_create.html new file mode 100644 index 0000000..33a5e4e --- /dev/null +++ b/templates/pikau/glossary_form_create.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} +{% load crispy_forms_tags %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Pīkau" "pikau:index" %} + {% breadcrumb "Glossary" "pikau:glossary_list" %} + {% breadcrumb "Create glossary definition" "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + Create new glossary definition +{% endblock page_heading %} + +{% block content %} +
{% csrf_token %} + {{ form|crispy }} + +
+{% endblock content %} diff --git a/templates/pikau/glossary_form_delete.html b/templates/pikau/glossary_form_delete.html new file mode 100644 index 0000000..bdffc03 --- /dev/null +++ b/templates/pikau/glossary_form_delete.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} +{% load crispy_forms_tags %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Pīkau" "pikau:index" %} + {% breadcrumb "Glossary" "pikau:glossary_list" %} + {% breadcrumb "Delete glossary definition" "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + Delete glossary definition +{% endblock page_heading %} + +{% block content %} +
{% csrf_token %} +

Are you sure you want to delete "{{ object }}"?

+ +
+{% endblock content %} diff --git a/templates/pikau/glossary_form_update.html b/templates/pikau/glossary_form_update.html new file mode 100644 index 0000000..6ccd707 --- /dev/null +++ b/templates/pikau/glossary_form_update.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} +{% load crispy_forms_tags %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Pīkau" "pikau:index" %} + {% breadcrumb "Glossary" "pikau:glossary_list" %} + {% breadcrumb "Update glossary definition" "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + Update glossary definition +{% endblock page_heading %} + +{% block content %} +
{% csrf_token %} + {{ form|crispy }} + +
+{% endblock content %} diff --git a/templates/pikau/index.html b/templates/pikau/index.html index d724049..dbd6810 100644 --- a/templates/pikau/index.html +++ b/templates/pikau/index.html @@ -29,7 +29,7 @@

View metadata

- Glossary + Glossary Goals Levels Milestones diff --git a/templates/pikau/pikaucourse_detail.html b/templates/pikau/pikaucourse_detail.html index e274997..d440f66 100644 --- a/templates/pikau/pikaucourse_detail.html +++ b/templates/pikau/pikaucourse_detail.html @@ -141,7 +141,7 @@

Metadata

    {% for glossary_term in pikau_course.glossary_terms.all %}
  • - + {{ glossary_term.term }}
  • diff --git a/templates/pikau/topic_form.html b/templates/pikau/topic_form_create.html similarity index 76% rename from templates/pikau/topic_form.html rename to templates/pikau/topic_form_create.html index 4238e00..74f2b9a 100644 --- a/templates/pikau/topic_form.html +++ b/templates/pikau/topic_form_create.html @@ -1,6 +1,7 @@ {% extends "base.html" %} {% load django_bootstrap_breadcrumbs %} +{% load crispy_forms_tags %} {% block breadcrumbs %} {% breadcrumb "Home" "/" %} @@ -10,12 +11,12 @@ {% endblock breadcrumbs %} {% block page_heading %} - Create Topic + Create new topic {% endblock page_heading %} {% block content %}
    {% csrf_token %} - {{ form.as_p }} - + {{ form|crispy }} +
    {% endblock content %} diff --git a/templates/pikau/topic_confirm_delete.html b/templates/pikau/topic_form_delete.html similarity index 100% rename from templates/pikau/topic_confirm_delete.html rename to templates/pikau/topic_form_delete.html diff --git a/templates/pikau/topic_form_update.html b/templates/pikau/topic_form_update.html new file mode 100644 index 0000000..98cc4f8 --- /dev/null +++ b/templates/pikau/topic_form_update.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} +{% load crispy_forms_tags %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Pīkau" "pikau:index" %} + {% breadcrumb "Topics" "pikau:topic_list" %} + {% breadcrumb "Update Topic" "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + Update topic +{% endblock page_heading %} + +{% block content %} +
    {% csrf_token %} + {{ form|crispy }} + +
    +{% endblock content %} From a59b6f4223038c05cd12dbb76aacb3c47c88c753 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Tue, 22 May 2018 15:42:38 +1200 Subject: [PATCH 25/35] (WIP) Updating glossary CRUD pages --- pikau/models.py | 5 ++- pikau/tables.py | 22 ++++++++++++ pikau/urls.py | 14 +++++--- pikau/views.py | 28 +++++++++------ templates/pikau/glossary.html | 36 ------------------- templates/pikau/glossaryterm_detail.html | 33 +++++++++++++++++ ...ate.html => glossaryterm_form_create.html} | 2 +- ...ete.html => glossaryterm_form_delete.html} | 2 +- ...ate.html => glossaryterm_form_update.html} | 2 +- templates/pikau/glossaryterm_list.html | 26 ++++++++++++++ templates/pikau/index.html | 2 +- templates/pikau/pikaucourse_detail.html | 2 +- templates/snippets/add_button.html | 5 +++ templates/snippets/delete_button.html | 5 +++ templates/snippets/update_button.html | 5 +++ 15 files changed, 132 insertions(+), 57 deletions(-) create mode 100644 pikau/tables.py delete mode 100644 templates/pikau/glossary.html create mode 100644 templates/pikau/glossaryterm_detail.html rename templates/pikau/{glossary_form_create.html => glossaryterm_form_create.html} (90%) rename templates/pikau/{glossary_form_delete.html => glossaryterm_form_delete.html} (90%) rename templates/pikau/{glossary_form_update.html => glossaryterm_form_update.html} (90%) create mode 100644 templates/pikau/glossaryterm_list.html create mode 100644 templates/snippets/add_button.html create mode 100644 templates/snippets/delete_button.html create mode 100644 templates/snippets/update_button.html diff --git a/pikau/models.py b/pikau/models.py index 60ca342..c634426 100644 --- a/pikau/models.py +++ b/pikau/models.py @@ -46,7 +46,10 @@ class GlossaryTerm(models.Model): """Model for glossary term.""" # Auto-incrementing 'id' field is automatically set by Django - slug = models.SlugField(unique=True) + slug = models.SlugField( + unique=True, + help_text="A unique readable identifier", + ) term = models.CharField(max_length=200, unique=True) description = models.TextField() diff --git a/pikau/tables.py b/pikau/tables.py new file mode 100644 index 0000000..7c84e14 --- /dev/null +++ b/pikau/tables.py @@ -0,0 +1,22 @@ +"""Tables for the pikau application.""" + +from django_tables2 import A +import django_tables2 as tables +from pikau.models import ( + GlossaryTerm, +) + + +class GlossaryTermTable(tables.Table): + """Table to display all glossary terms.""" + + term = tables.LinkColumn("pikau:glossaryterm_detail", args=[A("slug")]) + slug = tables.TemplateColumn(template_code="{{ record.slug }}") + + class Meta: + """Meta attributes for GlossaryTermTable class.""" + + model = GlossaryTerm + fields = ("term", "slug", "description") + order_by = "term" + attrs = {"class": "table table-hover"} diff --git a/pikau/urls.py b/pikau/urls.py index e3b0cab..c3df88d 100644 --- a/pikau/urls.py +++ b/pikau/urls.py @@ -36,25 +36,31 @@ path( "glossary/", views.GlossaryListView.as_view(), - name="glossary_list" + name="glossaryterm_list" + ), + # eg: /pikau/glossary/view/slug-1/ + path( + "glossary/view/", + views.GlossaryDetailView.as_view(), + name="glossaryterm_detail" ), # eg: /pikau/glossary/create/ path( "glossary/create/", views.GlossaryCreateView.as_view(), - name="glossary_create" + name="glossaryterm_create" ), # eg: /pikau/glossary/update/term-1/ path( "glossary/update//", views.GlossaryUpdateView.as_view(), - name="glossary_update" + name="glossaryterm_update" ), # eg: /pikau/glossary/delete/term-1/ path( "glossary/delete//", views.GlossaryDeleteView.as_view(), - name="glossary_delete" + name="glossaryterm_delete" ), # eg: /pikau/goals/ path( diff --git a/pikau/views.py b/pikau/views.py index d638c76..2b60303 100644 --- a/pikau/views.py +++ b/pikau/views.py @@ -16,6 +16,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse_lazy from django.http import Http404 +from django_tables2 import SingleTableView from pikau.models import ( GlossaryTerm, Goal, @@ -30,12 +31,12 @@ READINESS_LEVELS, ) from pikau.utils import pathways +from pikau import tables from pikau.mixins import ( SuccessMessageDeleteMixin, GlossaryActionMixin, TopicActionMixin, ) - NUMBER_OF_FLAME_STAGES = 7 @@ -66,40 +67,45 @@ def get_context_data(self, **kwargs): return context -class GlossaryListView(LoginRequiredMixin, ListView): +class GlossaryListView(LoginRequiredMixin, SingleTableView): """View for the glossary list page.""" - template_name = "pikau/glossary.html" - context_object_name = "glossary_terms" model = GlossaryTerm - ordering = "term" + table_class = tables.GlossaryTermTable + + +class GlossaryDetailView(LoginRequiredMixin, DetailView): + """View for a glossary term.""" + + context_object_name = "glossary_term" + model = GlossaryTerm class GlossaryCreateView(LoginRequiredMixin, SuccessMessageMixin, GlossaryActionMixin, CreateView): """View for creating a glossary definition.""" model = GlossaryTerm - template_name = "pikau/glossary_form_create.html" + template_name = "pikau/glossaryterm_form_create.html" success_message = "Glossary definition created!" - success_url = reverse_lazy("pikau:glossary_list") + success_url = reverse_lazy("pikau:glossaryterm_list") class GlossaryUpdateView(LoginRequiredMixin, SuccessMessageMixin, GlossaryActionMixin, UpdateView): """View for updating a glossary definition.""" model = GlossaryTerm - template_name = "pikau/glossary_form_update.html" + template_name = "pikau/glossaryterm_form_update.html" success_message = "Glossary definition updated!" - success_url = reverse_lazy("pikau:glossary_list") + success_url = reverse_lazy("pikau:glossaryterm_list") class GlossaryDeleteView(LoginRequiredMixin, SuccessMessageDeleteMixin, GlossaryActionMixin, DeleteView): """View for deleting a glossary definition.""" model = GlossaryTerm - template_name = "pikau/glossary_form_delete.html" + template_name = "pikau/glossaryterm_form_delete.html" success_message = "Glossary definition deleted!" - success_url = reverse_lazy("pikau:glossary_list") + success_url = reverse_lazy("pikau:glossaryterm_list") class GoalListView(LoginRequiredMixin, ListView): diff --git a/templates/pikau/glossary.html b/templates/pikau/glossary.html deleted file mode 100644 index 14ac919..0000000 --- a/templates/pikau/glossary.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "base.html" %} - -{% load django_bootstrap_breadcrumbs %} - -{% block breadcrumbs %} - {% breadcrumb "Home" "/" %} - {% breadcrumb "Pīkau" "pikau:index" %} - {% breadcrumb "Glossary" "" %} -{% endblock breadcrumbs %} - -{% block page_heading %} - Glossary -{% endblock page_heading %} - -{% block content %} -

    This page lists all the glossary terms available within Pīkau.

    - - - - - - - - - - - {% for glossary_term in glossary_terms %} - - - - - - {% endfor %} - -
    TermSlugDescription
    {{ glossary_term.term }}{{ glossary_term.slug }}{{ glossary_term.description }}
    -{% endblock content %} diff --git a/templates/pikau/glossaryterm_detail.html b/templates/pikau/glossaryterm_detail.html new file mode 100644 index 0000000..b8be937 --- /dev/null +++ b/templates/pikau/glossaryterm_detail.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} +{% load render_html_field %} +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Pīkau" "pikau:index" %} + {% breadcrumb "Glossary" "pikau:glossaryterm_list" %} + {% breadcrumb level.name "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + {{ glossary_term.term }} +
    + {% with text='Update glossary term' %} + {% url 'pikau:glossaryterm_update' glossary_term.slug as update_url %} + {% include "snippets/update_button.html" %} + {% endwith %} +
    +{% endblock page_heading %} + +{% block content %} + {% render_html_field glossary_term.description %} + +

    Slug: {{ glossary_term.slug }}

    + +

    Pikau Courses: {{ glossary_term.pikau_courses.count }}

    +
    + {% for pikau_course in level.pikau_courses.all %} + {% include "pikau/snippets/pikau_course_card.html" %} + {% endfor %} +
    +{% endblock content %} diff --git a/templates/pikau/glossary_form_create.html b/templates/pikau/glossaryterm_form_create.html similarity index 90% rename from templates/pikau/glossary_form_create.html rename to templates/pikau/glossaryterm_form_create.html index 33a5e4e..05c3fab 100644 --- a/templates/pikau/glossary_form_create.html +++ b/templates/pikau/glossaryterm_form_create.html @@ -6,7 +6,7 @@ {% block breadcrumbs %} {% breadcrumb "Home" "/" %} {% breadcrumb "Pīkau" "pikau:index" %} - {% breadcrumb "Glossary" "pikau:glossary_list" %} + {% breadcrumb "Glossary" "pikau:glossaryterm_list" %} {% breadcrumb "Create glossary definition" "" %} {% endblock breadcrumbs %} diff --git a/templates/pikau/glossary_form_delete.html b/templates/pikau/glossaryterm_form_delete.html similarity index 90% rename from templates/pikau/glossary_form_delete.html rename to templates/pikau/glossaryterm_form_delete.html index bdffc03..cc60d12 100644 --- a/templates/pikau/glossary_form_delete.html +++ b/templates/pikau/glossaryterm_form_delete.html @@ -6,7 +6,7 @@ {% block breadcrumbs %} {% breadcrumb "Home" "/" %} {% breadcrumb "Pīkau" "pikau:index" %} - {% breadcrumb "Glossary" "pikau:glossary_list" %} + {% breadcrumb "Glossary" "pikau:glossaryterm_list" %} {% breadcrumb "Delete glossary definition" "" %} {% endblock breadcrumbs %} diff --git a/templates/pikau/glossary_form_update.html b/templates/pikau/glossaryterm_form_update.html similarity index 90% rename from templates/pikau/glossary_form_update.html rename to templates/pikau/glossaryterm_form_update.html index 6ccd707..34275bc 100644 --- a/templates/pikau/glossary_form_update.html +++ b/templates/pikau/glossaryterm_form_update.html @@ -6,7 +6,7 @@ {% block breadcrumbs %} {% breadcrumb "Home" "/" %} {% breadcrumb "Pīkau" "pikau:index" %} - {% breadcrumb "Glossary" "pikau:glossary_list" %} + {% breadcrumb "Glossary" "pikau:glossaryterm_list" %} {% breadcrumb "Update glossary definition" "" %} {% endblock breadcrumbs %} diff --git a/templates/pikau/glossaryterm_list.html b/templates/pikau/glossaryterm_list.html new file mode 100644 index 0000000..354c481 --- /dev/null +++ b/templates/pikau/glossaryterm_list.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} + +{% load render_table from django_tables2 %} +{% load django_bootstrap_breadcrumbs %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Pīkau" "pikau:index" %} + {% breadcrumb "Glossary" "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + Glossary +
    + {% with text='Add glossary term' %} + {% url 'pikau:glossaryterm_create' as add_url %} + {% include "snippets/add_button.html" %} + {% endwith %} +
    +{% endblock page_heading %} + +{% block content %} +

    This page lists all the glossary terms available within Pīkau.

    + + {% render_table table %} +{% endblock content %} diff --git a/templates/pikau/index.html b/templates/pikau/index.html index dbd6810..3690dd4 100644 --- a/templates/pikau/index.html +++ b/templates/pikau/index.html @@ -29,7 +29,7 @@

    View metadata

    - Glossary + Glossary Goals Levels Milestones diff --git a/templates/pikau/pikaucourse_detail.html b/templates/pikau/pikaucourse_detail.html index d440f66..bd75cc0 100644 --- a/templates/pikau/pikaucourse_detail.html +++ b/templates/pikau/pikaucourse_detail.html @@ -141,7 +141,7 @@

    Metadata

      {% for glossary_term in pikau_course.glossary_terms.all %}
    • - + {{ glossary_term.term }}
    • diff --git a/templates/snippets/add_button.html b/templates/snippets/add_button.html new file mode 100644 index 0000000..351dd27 --- /dev/null +++ b/templates/snippets/add_button.html @@ -0,0 +1,5 @@ +{% load static %} + + + {{ text|default:"Add" }} + diff --git a/templates/snippets/delete_button.html b/templates/snippets/delete_button.html new file mode 100644 index 0000000..ef465d0 --- /dev/null +++ b/templates/snippets/delete_button.html @@ -0,0 +1,5 @@ +{% load static %} + + + {{ text|default:"Delete" }} + diff --git a/templates/snippets/update_button.html b/templates/snippets/update_button.html new file mode 100644 index 0000000..57181ec --- /dev/null +++ b/templates/snippets/update_button.html @@ -0,0 +1,5 @@ +{% load static %} + + + {{ text|default:"Update" }} + From bb59a0e7a7f076c51fd7fe55c055effb21317698 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Tue, 22 May 2018 16:56:23 +1200 Subject: [PATCH 26/35] Use default context name --- pikau/views.py | 1 - templates/pikau/glossaryterm_detail.html | 13 ++++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/pikau/views.py b/pikau/views.py index 2b60303..2cfa4e6 100644 --- a/pikau/views.py +++ b/pikau/views.py @@ -77,7 +77,6 @@ class GlossaryListView(LoginRequiredMixin, SingleTableView): class GlossaryDetailView(LoginRequiredMixin, DetailView): """View for a glossary term.""" - context_object_name = "glossary_term" model = GlossaryTerm diff --git a/templates/pikau/glossaryterm_detail.html b/templates/pikau/glossaryterm_detail.html index b8be937..980c0b1 100644 --- a/templates/pikau/glossaryterm_detail.html +++ b/templates/pikau/glossaryterm_detail.html @@ -1,30 +1,29 @@ {% extends "base.html" %} {% load django_bootstrap_breadcrumbs %} -{% load render_html_field %} {% block breadcrumbs %} {% breadcrumb "Home" "/" %} {% breadcrumb "Pīkau" "pikau:index" %} {% breadcrumb "Glossary" "pikau:glossaryterm_list" %} - {% breadcrumb level.name "" %} + {% breadcrumb glossaryterm.term "" %} {% endblock breadcrumbs %} {% block page_heading %} - {{ glossary_term.term }} + {{ glossaryterm.term }}
      {% with text='Update glossary term' %} - {% url 'pikau:glossaryterm_update' glossary_term.slug as update_url %} + {% url 'pikau:glossaryterm_update' glossaryterm.slug as update_url %} {% include "snippets/update_button.html" %} {% endwith %}
      {% endblock page_heading %} {% block content %} - {% render_html_field glossary_term.description %} +

      {{ glossaryterm.description }}

      -

      Slug: {{ glossary_term.slug }}

      +

      Slug: {{ glossaryterm.slug }}

      -

      Pikau Courses: {{ glossary_term.pikau_courses.count }}

      +

      Pikau Courses: {{ glossaryterm.pikau_courses.count }}

      {% for pikau_course in level.pikau_courses.all %} {% include "pikau/snippets/pikau_course_card.html" %} From eb0526aaecc078798879dc3a72ff490b7592eda8 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Tue, 22 May 2018 16:56:38 +1200 Subject: [PATCH 27/35] Update model relationship defaults --- pikau/migrations/0027_auto_20180522_1652.py | 45 +++++++++++++++++++++ pikau/models.py | 10 ++--- 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 pikau/migrations/0027_auto_20180522_1652.py diff --git a/pikau/migrations/0027_auto_20180522_1652.py b/pikau/migrations/0027_auto_20180522_1652.py new file mode 100644 index 0000000..98c0b27 --- /dev/null +++ b/pikau/migrations/0027_auto_20180522_1652.py @@ -0,0 +1,45 @@ +# Generated by Django 2.0.5 on 2018-05-22 04:52 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('pikau', '0026_auto_20180516_1331'), + ] + + operations = [ + migrations.AlterField( + model_name='glossaryterm', + name='slug', + field=models.SlugField(help_text='A unique readable identifier', unique=True), + ), + migrations.AlterField( + model_name='pikaucourse', + name='level', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pikau_courses', to='pikau.Level'), + ), + migrations.AlterField( + model_name='pikaucourse', + name='manager', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pikau_courses', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='pikaucourse', + name='milestone', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pikau_courses', to='pikau.Milestone'), + ), + migrations.AlterField( + model_name='pikaucourse', + name='topic', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pikau_courses', to='pikau.Topic'), + ), + migrations.AlterField( + model_name='pikauunit', + name='pikau_course', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='content', to='pikau.PikauCourse'), + ), + ] diff --git a/pikau/models.py b/pikau/models.py index c634426..1c51797 100644 --- a/pikau/models.py +++ b/pikau/models.py @@ -194,14 +194,14 @@ class PikauCourse(models.Model): ) topic = models.ForeignKey( Topic, - on_delete=models.CASCADE, + on_delete=models.PROTECT, related_name="pikau_courses", blank=True, null=True, ) level = models.ForeignKey( Level, - on_delete=models.CASCADE, + on_delete=models.PROTECT, related_name="pikau_courses", blank=True, null=True, @@ -245,14 +245,14 @@ class PikauCourse(models.Model): __previous_status = None manager = models.ForeignKey( User, - on_delete=models.CASCADE, + on_delete=models.SET_NULL, related_name="pikau_courses", blank=True, null=True, ) milestone = models.ForeignKey( Milestone, - on_delete=models.CASCADE, + on_delete=models.PROTECT, related_name="pikau_courses", blank=True, null=True, @@ -287,7 +287,7 @@ class PikauUnit(models.Model): number = models.PositiveSmallIntegerField() pikau_course = models.ForeignKey( PikauCourse, - on_delete=models.CASCADE, + on_delete=models.PROTECT, related_name="content" ) name = models.CharField(max_length=200) From ed93e83a65b2aa3e6355a700a9fa7a62b6370f91 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Tue, 22 May 2018 20:08:48 +1200 Subject: [PATCH 28/35] Add link to licence on files table --- files/models.py | 8 ++++++++ files/tables.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/files/models.py b/files/models.py index 24c080a..866c5fd 100644 --- a/files/models.py +++ b/files/models.py @@ -28,6 +28,14 @@ class Meta: ordering = ("name", ) + def get_absolute_url(self): + """Return the URL for a licence. + + Returns: + URL as string. + """ + return self.url + def __str__(self): """Text representation of Licence object. diff --git a/files/tables.py b/files/tables.py index 1399574..82c7f5c 100644 --- a/files/tables.py +++ b/files/tables.py @@ -9,6 +9,8 @@ class FileTable(tables.Table): """Table to display all files.""" + licence = tables.RelatedLinkColumn() + class Meta: """Meta attributes for FileTable class.""" From 29d8ce8b04f996532513b15da3a5fce00c5d29d9 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Tue, 22 May 2018 21:18:42 +1200 Subject: [PATCH 29/35] Use crispy-forms and slugify to customise Glossary forms --- pikau/forms.py | 29 +++++++++++++++++++ pikau/mixins.py | 28 ++---------------- pikau/views.py | 19 +++++++----- static/css/website.css | 3 ++ static/js/speakingurl.min.js | 7 +++++ templates/pikau/glossaryterm_form_create.html | 9 +++--- templates/pikau/glossaryterm_form_update.html | 9 +++--- templates/snippets/slugify.html | 10 +++++++ 8 files changed, 73 insertions(+), 41 deletions(-) create mode 100644 pikau/forms.py create mode 100644 static/js/speakingurl.min.js create mode 100644 templates/snippets/slugify.html diff --git a/pikau/forms.py b/pikau/forms.py new file mode 100644 index 0000000..20ab2d2 --- /dev/null +++ b/pikau/forms.py @@ -0,0 +1,29 @@ +"""Forms for pikau application.""" + +from django.forms import ModelForm +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit, Field +from pikau.models import ( + GlossaryTerm +) + + +class GlossaryForm(ModelForm): + """Form for pages relating to actions of glossary terms.""" + + def __init__(self, *args, **kwargs): + """Set helper for form layout.""" + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Field("term", css_class="slug-source"), + "description", + "slug", + Submit("submit", "Submit"), + ) + + class Meta: + """Meta attributes of GlossaryForm.""" + + model = GlossaryTerm + fields = ("term", "description", "slug") diff --git a/pikau/mixins.py b/pikau/mixins.py index 60886ec..f237d85 100644 --- a/pikau/mixins.py +++ b/pikau/mixins.py @@ -4,38 +4,14 @@ class SuccessMessageDeleteMixin(object): + """Allow success message for delete view.""" def delete(self, request, *args, **kwargs): + """Set success string in message.""" messages.success(self.request, self.success_message) return super().delete(request, *args, **kwargs) -class GlossaryActionMixin(object): - """Sets fields for pages relating to actions of glossary items.""" - - fields = ("term", "description", "slug") - - -# class MilestoneActionMixin(SuccessMessageMixin): -# -# fields = ("name", "date") - - -# class GoalActionMixin(SuccessMessageMixin): -# -# fields = ("description", "slug") - - -# class TagActionMixin(SuccessMessageMixin): -# -# fields = ("name", "description", "slug") -# - class TopicActionMixin(object): fields = ("name", "slug") - - -# class LevelActionMixin(SuccessMessageMixin): -# -# fields = ("name", "slug") diff --git a/pikau/views.py b/pikau/views.py index 2cfa4e6..358384e 100644 --- a/pikau/views.py +++ b/pikau/views.py @@ -34,9 +34,12 @@ from pikau import tables from pikau.mixins import ( SuccessMessageDeleteMixin, - GlossaryActionMixin, TopicActionMixin, ) +from pikau.forms import ( + GlossaryForm, +) + NUMBER_OF_FLAME_STAGES = 7 @@ -80,25 +83,27 @@ class GlossaryDetailView(LoginRequiredMixin, DetailView): model = GlossaryTerm -class GlossaryCreateView(LoginRequiredMixin, SuccessMessageMixin, GlossaryActionMixin, CreateView): +class GlossaryCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): """View for creating a glossary definition.""" model = GlossaryTerm + form_class = GlossaryForm template_name = "pikau/glossaryterm_form_create.html" success_message = "Glossary definition created!" success_url = reverse_lazy("pikau:glossaryterm_list") -class GlossaryUpdateView(LoginRequiredMixin, SuccessMessageMixin, GlossaryActionMixin, UpdateView): +class GlossaryUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): """View for updating a glossary definition.""" model = GlossaryTerm + form_class = GlossaryForm template_name = "pikau/glossaryterm_form_update.html" success_message = "Glossary definition updated!" success_url = reverse_lazy("pikau:glossaryterm_list") -class GlossaryDeleteView(LoginRequiredMixin, SuccessMessageDeleteMixin, GlossaryActionMixin, DeleteView): +class GlossaryDeleteView(LoginRequiredMixin, SuccessMessageDeleteMixin, DeleteView): """View for deleting a glossary definition.""" model = GlossaryTerm @@ -149,7 +154,7 @@ def get_context_data(self, **kwargs): Returns: Dictionary of context data. """ - context = super(MilestoneList, self).get_context_data(**kwargs) + context = super(MilestoneListView, self).get_context_data(**kwargs) line_broken_status_stages = [] for status_num, status_name in STATUS_CHOICES: line_broken_status_stages.append( @@ -247,7 +252,7 @@ def get_context_data(self, **kwargs): Returns: Dictionary of context data. """ - context = super(PikauUnitDetail, self).get_context_data(**kwargs) + context = super(PikauUnitDetailView, self).get_context_data(**kwargs) try: context["previous_unit"] = PikauUnit.objects.get( pikau_course=self.object.pikau_course, @@ -284,7 +289,7 @@ def get_context_data(self, **kwargs): Returns: Dictionary of context data. """ - context = super(ProgressOutcomeList, self).get_context_data(**kwargs) + context = super(ProgressOutcomeListView, self).get_context_data(**kwargs) topics = Topic.objects.order_by("name") context["topics"] = topics max_count = NUMBER_OF_FLAME_STAGES diff --git a/static/css/website.css b/static/css/website.css index 248c419..96166b0 100644 --- a/static/css/website.css +++ b/static/css/website.css @@ -122,3 +122,6 @@ footer > .container { .permalink { margin: 4rem 0; } +.asteriskField { + color: #dc3545; +} diff --git a/static/js/speakingurl.min.js b/static/js/speakingurl.min.js new file mode 100644 index 0000000..c621c22 --- /dev/null +++ b/static/js/speakingurl.min.js @@ -0,0 +1,7 @@ +/** + * speakingurl + * @version v14.0.1 + * @link http://pid.github.io/speakingurl/ + * @license BSD + * @author + */!function(a){"use strict";var e={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"Ae","Å":"A","Æ":"AE","Ç":"C","È":"E","É":"E","Ê":"E","Ë":"E","Ì":"I","Í":"I","Î":"I","Ï":"I","Ð":"D","Ñ":"N","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"Oe","Ő":"O","Ø":"O","Ù":"U","Ú":"U","Û":"U","Ü":"Ue","Ű":"U","Ý":"Y","Þ":"TH","ß":"ss","à":"a","á":"a","â":"a","ã":"a","ä":"ae","å":"a","æ":"ae","ç":"c","è":"e","é":"e","ê":"e","ë":"e","ì":"i","í":"i","î":"i","ï":"i","ð":"d","ñ":"n","ò":"o","ó":"o","ô":"o","õ":"o","ö":"oe","ő":"o","ø":"o","ù":"u","ú":"u","û":"u","ü":"ue","ű":"u","ý":"y","þ":"th","ÿ":"y","ẞ":"SS","ا":"a","أ":"a","إ":"i","آ":"aa","ؤ":"u","ئ":"e","ء":"a","ب":"b","ت":"t","ث":"th","ج":"j","ح":"h","خ":"kh","د":"d","ذ":"th","ر":"r","ز":"z","س":"s","ش":"sh","ص":"s","ض":"dh","ط":"t","ظ":"z","ع":"a","غ":"gh","ف":"f","ق":"q","ك":"k","ل":"l","م":"m","ن":"n","ه":"h","و":"w","ي":"y","ى":"a","ة":"h","ﻻ":"la","ﻷ":"laa","ﻹ":"lai","ﻵ":"laa","گ":"g","چ":"ch","پ":"p","ژ":"zh","ک":"k","ی":"y","َ":"a","ً":"an","ِ":"e","ٍ":"en","ُ":"u","ٌ":"on","ْ":"","٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","۰":"0","۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","က":"k","ခ":"kh","ဂ":"g","ဃ":"ga","င":"ng","စ":"s","ဆ":"sa","ဇ":"z","စျ":"za","ည":"ny","ဋ":"t","ဌ":"ta","ဍ":"d","ဎ":"da","ဏ":"na","တ":"t","ထ":"ta","ဒ":"d","ဓ":"da","န":"n","ပ":"p","ဖ":"pa","ဗ":"b","ဘ":"ba","မ":"m","ယ":"y","ရ":"ya","လ":"l","ဝ":"w","သ":"th","ဟ":"h","ဠ":"la","အ":"a","ြ":"y","ျ":"ya","ွ":"w","ြွ":"yw","ျွ":"ywa","ှ":"h","ဧ":"e","၏":"-e","ဣ":"i","ဤ":"-i","ဉ":"u","ဦ":"-u","ဩ":"aw","သြော":"aw","ဪ":"aw","၀":"0","၁":"1","၂":"2","၃":"3","၄":"4","၅":"5","၆":"6","၇":"7","၈":"8","၉":"9","္":"","့":"","း":"","č":"c","ď":"d","ě":"e","ň":"n","ř":"r","š":"s","ť":"t","ů":"u","ž":"z","Č":"C","Ď":"D","Ě":"E","Ň":"N","Ř":"R","Š":"S","Ť":"T","Ů":"U","Ž":"Z","ހ":"h","ށ":"sh","ނ":"n","ރ":"r","ބ":"b","ޅ":"lh","ކ":"k","އ":"a","ވ":"v","މ":"m","ފ":"f","ދ":"dh","ތ":"th","ލ":"l","ގ":"g","ޏ":"gn","ސ":"s","ޑ":"d","ޒ":"z","ޓ":"t","ޔ":"y","ޕ":"p","ޖ":"j","ޗ":"ch","ޘ":"tt","ޙ":"hh","ޚ":"kh","ޛ":"th","ޜ":"z","ޝ":"sh","ޞ":"s","ޟ":"d","ޠ":"t","ޡ":"z","ޢ":"a","ޣ":"gh","ޤ":"q","ޥ":"w","ަ":"a","ާ":"aa","ި":"i","ީ":"ee","ު":"u","ޫ":"oo","ެ":"e","ޭ":"ey","ޮ":"o","ޯ":"oa","ް":"","ა":"a","ბ":"b","გ":"g","დ":"d","ე":"e","ვ":"v","ზ":"z","თ":"t","ი":"i","კ":"k","ლ":"l","მ":"m","ნ":"n","ო":"o","პ":"p","ჟ":"zh","რ":"r","ს":"s","ტ":"t","უ":"u","ფ":"p","ქ":"k","ღ":"gh","ყ":"q","შ":"sh","ჩ":"ch","ც":"ts","ძ":"dz","წ":"ts","ჭ":"ch","ხ":"kh","ჯ":"j","ჰ":"h","α":"a","β":"v","γ":"g","δ":"d","ε":"e","ζ":"z","η":"i","θ":"th","ι":"i","κ":"k","λ":"l","μ":"m","ν":"n","ξ":"ks","ο":"o","π":"p","ρ":"r","σ":"s","τ":"t","υ":"y","φ":"f","χ":"x","ψ":"ps","ω":"o","ά":"a","έ":"e","ί":"i","ό":"o","ύ":"y","ή":"i","ώ":"o","ς":"s","ϊ":"i","ΰ":"y","ϋ":"y","ΐ":"i","Α":"A","Β":"B","Γ":"G","Δ":"D","Ε":"E","Ζ":"Z","Η":"I","Θ":"TH","Ι":"I","Κ":"K","Λ":"L","Μ":"M","Ν":"N","Ξ":"KS","Ο":"O","Π":"P","Ρ":"R","Σ":"S","Τ":"T","Υ":"Y","Φ":"F","Χ":"X","Ψ":"PS","Ω":"O","Ά":"A","Έ":"E","Ί":"I","Ό":"O","Ύ":"Y","Ή":"I","Ώ":"O","Ϊ":"I","Ϋ":"Y","ā":"a","ē":"e","ģ":"g","ī":"i","ķ":"k","ļ":"l","ņ":"n","ū":"u","Ā":"A","Ē":"E","Ģ":"G","Ī":"I","Ķ":"k","Ļ":"L","Ņ":"N","Ū":"U","Ќ":"Kj","ќ":"kj","Љ":"Lj","љ":"lj","Њ":"Nj","њ":"nj","Тс":"Ts","тс":"ts","ą":"a","ć":"c","ę":"e","ł":"l","ń":"n","ś":"s","ź":"z","ż":"z","Ą":"A","Ć":"C","Ę":"E","Ł":"L","Ń":"N","Ś":"S","Ź":"Z","Ż":"Z","Є":"Ye","І":"I","Ї":"Yi","Ґ":"G","є":"ye","і":"i","ї":"yi","ґ":"g","ă":"a","Ă":"A","ș":"s","Ș":"S","ț":"t","Ț":"T","ţ":"t","Ţ":"T","а":"a","б":"b","в":"v","г":"g","д":"d","е":"e","ё":"yo","ж":"zh","з":"z","и":"i","й":"i","к":"k","л":"l","м":"m","н":"n","о":"o","п":"p","р":"r","с":"s","т":"t","у":"u","ф":"f","х":"kh","ц":"c","ч":"ch","ш":"sh","щ":"sh","ъ":"","ы":"y","ь":"","э":"e","ю":"yu","я":"ya","А":"A","Б":"B","В":"V","Г":"G","Д":"D","Е":"E","Ё":"Yo","Ж":"Zh","З":"Z","И":"I","Й":"I","К":"K","Л":"L","М":"M","Н":"N","О":"O","П":"P","Р":"R","С":"S","Т":"T","У":"U","Ф":"F","Х":"Kh","Ц":"C","Ч":"Ch","Ш":"Sh","Щ":"Sh","Ъ":"","Ы":"Y","Ь":"","Э":"E","Ю":"Yu","Я":"Ya","ђ":"dj","ј":"j","ћ":"c","џ":"dz","Ђ":"Dj","Ј":"j","Ћ":"C","Џ":"Dz","ľ":"l","ĺ":"l","ŕ":"r","Ľ":"L","Ĺ":"L","Ŕ":"R","ş":"s","Ş":"S","ı":"i","İ":"I","ğ":"g","Ğ":"G","ả":"a","Ả":"A","ẳ":"a","Ẳ":"A","ẩ":"a","Ẩ":"A","đ":"d","Đ":"D","ẹ":"e","Ẹ":"E","ẽ":"e","Ẽ":"E","ẻ":"e","Ẻ":"E","ế":"e","Ế":"E","ề":"e","Ề":"E","ệ":"e","Ệ":"E","ễ":"e","Ễ":"E","ể":"e","Ể":"E","ỏ":"o","ọ":"o","Ọ":"o","ố":"o","Ố":"O","ồ":"o","Ồ":"O","ổ":"o","Ổ":"O","ộ":"o","Ộ":"O","ỗ":"o","Ỗ":"O","ơ":"o","Ơ":"O","ớ":"o","Ớ":"O","ờ":"o","Ờ":"O","ợ":"o","Ợ":"O","ỡ":"o","Ỡ":"O","Ở":"o","ở":"o","ị":"i","Ị":"I","ĩ":"i","Ĩ":"I","ỉ":"i","Ỉ":"i","ủ":"u","Ủ":"U","ụ":"u","Ụ":"U","ũ":"u","Ũ":"U","ư":"u","Ư":"U","ứ":"u","Ứ":"U","ừ":"u","Ừ":"U","ự":"u","Ự":"U","ữ":"u","Ữ":"U","ử":"u","Ử":"ư","ỷ":"y","Ỷ":"y","ỳ":"y","Ỳ":"Y","ỵ":"y","Ỵ":"Y","ỹ":"y","Ỹ":"Y","ạ":"a","Ạ":"A","ấ":"a","Ấ":"A","ầ":"a","Ầ":"A","ậ":"a","Ậ":"A","ẫ":"a","Ẫ":"A","ắ":"a","Ắ":"A","ằ":"a","Ằ":"A","ặ":"a","Ặ":"A","ẵ":"a","Ẵ":"A","⓪":"0","①":"1","②":"2","③":"3","④":"4","⑤":"5","⑥":"6","⑦":"7","⑧":"8","⑨":"9","⑩":"10","⑪":"11","⑫":"12","⑬":"13","⑭":"14","⑮":"15","⑯":"16","⑰":"17","⑱":"18","⑲":"18","⑳":"18","⓵":"1","⓶":"2","⓷":"3","⓸":"4","⓹":"5","⓺":"6","⓻":"7","⓼":"8","⓽":"9","⓾":"10","⓿":"0","⓫":"11","⓬":"12","⓭":"13","⓮":"14","⓯":"15","⓰":"16","⓱":"17","⓲":"18","⓳":"19","⓴":"20","Ⓐ":"A","Ⓑ":"B","Ⓒ":"C","Ⓓ":"D","Ⓔ":"E","Ⓕ":"F","Ⓖ":"G","Ⓗ":"H","Ⓘ":"I","Ⓙ":"J","Ⓚ":"K","Ⓛ":"L","Ⓜ":"M","Ⓝ":"N","Ⓞ":"O","Ⓟ":"P","Ⓠ":"Q","Ⓡ":"R","Ⓢ":"S","Ⓣ":"T","Ⓤ":"U","Ⓥ":"V","Ⓦ":"W","Ⓧ":"X","Ⓨ":"Y","Ⓩ":"Z","ⓐ":"a","ⓑ":"b","ⓒ":"c","ⓓ":"d","ⓔ":"e","ⓕ":"f","ⓖ":"g","ⓗ":"h","ⓘ":"i","ⓙ":"j","ⓚ":"k","ⓛ":"l","ⓜ":"m","ⓝ":"n","ⓞ":"o","ⓟ":"p","ⓠ":"q","ⓡ":"r","ⓢ":"s","ⓣ":"t","ⓤ":"u","ⓦ":"v","ⓥ":"w","ⓧ":"x","ⓨ":"y","ⓩ":"z","“":'"',"”":'"',"‘":"'","’":"'","∂":"d","ƒ":"f","™":"(TM)","©":"(C)","œ":"oe","Œ":"OE","®":"(R)","†":"+","℠":"(SM)","…":"...","˚":"o","º":"o","ª":"a","•":"*","၊":",","။":".",$:"USD","€":"EUR","₢":"BRN","₣":"FRF","£":"GBP","₤":"ITL","₦":"NGN","₧":"ESP","₩":"KRW","₪":"ILS","₫":"VND","₭":"LAK","₮":"MNT","₯":"GRD","₱":"ARS","₲":"PYG","₳":"ARA","₴":"UAH","₵":"GHS","¢":"cent","¥":"CNY","元":"CNY","円":"YEN","﷼":"IRR","₠":"EWE","฿":"THB","₨":"INR","₹":"INR","₰":"PF","₺":"TRY","؋":"AFN","₼":"AZN","лв":"BGN","៛":"KHR","₡":"CRC","₸":"KZT","ден":"MKD","zł":"PLN","₽":"RUB","₾":"GEL"},n=["်","ް"],t={"ာ":"a","ါ":"a","ေ":"e","ဲ":"e","ိ":"i","ီ":"i","ို":"o","ု":"u","ူ":"u","ေါင်":"aung","ော":"aw","ော်":"aw","ေါ":"aw","ေါ်":"aw","်":"်","က်":"et","ိုက်":"aik","ောက်":"auk","င်":"in","ိုင်":"aing","ောင်":"aung","စ်":"it","ည်":"i","တ်":"at","ိတ်":"eik","ုတ်":"ok","ွတ်":"ut","ေတ်":"it","ဒ်":"d","ိုဒ်":"ok","ုဒ်":"ait","န်":"an","ာန်":"an","ိန်":"ein","ုန်":"on","ွန်":"un","ပ်":"at","ိပ်":"eik","ုပ်":"ok","ွပ်":"ut","န်ုပ်":"nub","မ်":"an","ိမ်":"ein","ုမ်":"on","ွမ်":"un","ယ်":"e","ိုလ်":"ol","ဉ်":"in","ံ":"an","ိံ":"ein","ုံ":"on","ައް":"ah","ަށް":"ah"},i={en:{},az:{"ç":"c","ə":"e","ğ":"g","ı":"i","ö":"o","ş":"s","ü":"u","Ç":"C","Ə":"E","Ğ":"G","İ":"I","Ö":"O","Ş":"S","Ü":"U"},cs:{"č":"c","ď":"d","ě":"e","ň":"n","ř":"r","š":"s","ť":"t","ů":"u","ž":"z","Č":"C","Ď":"D","Ě":"E","Ň":"N","Ř":"R","Š":"S","Ť":"T","Ů":"U","Ž":"Z"},fi:{"ä":"a","Ä":"A","ö":"o","Ö":"O"},hu:{"ä":"a","Ä":"A","ö":"o","Ö":"O","ü":"u","Ü":"U","ű":"u","Ű":"U"},lt:{"ą":"a","č":"c","ę":"e","ė":"e","į":"i","š":"s","ų":"u","ū":"u","ž":"z","Ą":"A","Č":"C","Ę":"E","Ė":"E","Į":"I","Š":"S","Ų":"U","Ū":"U"},lv:{"ā":"a","č":"c","ē":"e","ģ":"g","ī":"i","ķ":"k","ļ":"l","ņ":"n","š":"s","ū":"u","ž":"z","Ā":"A","Č":"C","Ē":"E","Ģ":"G","Ī":"i","Ķ":"k","Ļ":"L","Ņ":"N","Š":"S","Ū":"u","Ž":"Z"},pl:{"ą":"a","ć":"c","ę":"e","ł":"l","ń":"n","ó":"o","ś":"s","ź":"z","ż":"z","Ą":"A","Ć":"C","Ę":"e","Ł":"L","Ń":"N","Ó":"O","Ś":"S","Ź":"Z","Ż":"Z"},sv:{"ä":"a","Ä":"A","ö":"o","Ö":"O"},sk:{"ä":"a","Ä":"A"},sr:{"љ":"lj","њ":"nj","Љ":"Lj","Њ":"Nj","đ":"dj","Đ":"Dj"},tr:{"Ü":"U","Ö":"O","ü":"u","ö":"o"}},o={ar:{"∆":"delta","∞":"la-nihaya","♥":"hob","&":"wa","|":"aw","<":"aqal-men",">":"akbar-men","∑":"majmou","¤":"omla"},az:{},ca:{"∆":"delta","∞":"infinit","♥":"amor","&":"i","|":"o","<":"menys que",">":"mes que","∑":"suma dels","¤":"moneda"},cs:{"∆":"delta","∞":"nekonecno","♥":"laska","&":"a","|":"nebo","<":"mensi nez",">":"vetsi nez","∑":"soucet","¤":"mena"},de:{"∆":"delta","∞":"unendlich","♥":"Liebe","&":"und","|":"oder","<":"kleiner als",">":"groesser als","∑":"Summe von","¤":"Waehrung"},dv:{"∆":"delta","∞":"kolunulaa","♥":"loabi","&":"aai","|":"noonee","<":"ah vure kuda",">":"ah vure bodu","∑":"jumula","¤":"faisaa"},en:{"∆":"delta","∞":"infinity","♥":"love","&":"and","|":"or","<":"less than",">":"greater than","∑":"sum","¤":"currency"},es:{"∆":"delta","∞":"infinito","♥":"amor","&":"y","|":"u","<":"menos que",">":"mas que","∑":"suma de los","¤":"moneda"},fa:{"∆":"delta","∞":"bi-nahayat","♥":"eshgh","&":"va","|":"ya","<":"kamtar-az",">":"bishtar-az","∑":"majmooe","¤":"vahed"},fi:{"∆":"delta","∞":"aarettomyys","♥":"rakkaus","&":"ja","|":"tai","<":"pienempi kuin",">":"suurempi kuin","∑":"summa","¤":"valuutta"},fr:{"∆":"delta","∞":"infiniment","♥":"Amour","&":"et","|":"ou","<":"moins que",">":"superieure a","∑":"somme des","¤":"monnaie"},ge:{"∆":"delta","∞":"usasruloba","♥":"siqvaruli","&":"da","|":"an","<":"naklebi",">":"meti","∑":"jami","¤":"valuta"},gr:{},hu:{"∆":"delta","∞":"vegtelen","♥":"szerelem","&":"es","|":"vagy","<":"kisebb mint",">":"nagyobb mint","∑":"szumma","¤":"penznem"},it:{"∆":"delta","∞":"infinito","♥":"amore","&":"e","|":"o","<":"minore di",">":"maggiore di","∑":"somma","¤":"moneta"},lt:{"∆":"delta","∞":"begalybe","♥":"meile","&":"ir","|":"ar","<":"maziau nei",">":"daugiau nei","∑":"suma","¤":"valiuta"},lv:{"∆":"delta","∞":"bezgaliba","♥":"milestiba","&":"un","|":"vai","<":"mazak neka",">":"lielaks neka","∑":"summa","¤":"valuta"},my:{"∆":"kwahkhyaet","∞":"asaonasme","♥":"akhyait","&":"nhin","|":"tho","<":"ngethaw",">":"kyithaw","∑":"paungld","¤":"ngwekye"},mk:{},nl:{"∆":"delta","∞":"oneindig","♥":"liefde","&":"en","|":"of","<":"kleiner dan",">":"groter dan","∑":"som","¤":"valuta"},pl:{"∆":"delta","∞":"nieskonczonosc","♥":"milosc","&":"i","|":"lub","<":"mniejsze niz",">":"wieksze niz","∑":"suma","¤":"waluta"},pt:{"∆":"delta","∞":"infinito","♥":"amor","&":"e","|":"ou","<":"menor que",">":"maior que","∑":"soma","¤":"moeda"},ro:{"∆":"delta","∞":"infinit","♥":"dragoste","&":"si","|":"sau","<":"mai mic ca",">":"mai mare ca","∑":"suma","¤":"valuta"},ru:{"∆":"delta","∞":"beskonechno","♥":"lubov","&":"i","|":"ili","<":"menshe",">":"bolshe","∑":"summa","¤":"valjuta"},sk:{"∆":"delta","∞":"nekonecno","♥":"laska","&":"a","|":"alebo","<":"menej ako",">":"viac ako","∑":"sucet","¤":"mena"},sr:{},tr:{"∆":"delta","∞":"sonsuzluk","♥":"ask","&":"ve","|":"veya","<":"kucuktur",">":"buyuktur","∑":"toplam","¤":"para birimi"},uk:{"∆":"delta","∞":"bezkinechnist","♥":"lubov","&":"i","|":"abo","<":"menshe",">":"bilshe","∑":"suma","¤":"valjuta"},vn:{"∆":"delta","∞":"vo cuc","♥":"yeu","&":"va","|":"hoac","<":"nho hon",">":"lon hon","∑":"tong","¤":"tien te"}},u=[";","?",":","@","&","=","+","$",",","/"].join(""),s=[";","?",":","@","&","=","+","$",","].join(""),l=[".","!","~","*","'","(",")"].join(""),r=function(a,r){var m,d,g,k,y,f,p,z,b,A,v,E,O,j,S="-",w="",U="",C=!0,N={},R="";if("string"!=typeof a)return"";if("string"==typeof r&&(S=r),p=o.en,z=i.en,"object"==typeof r){m=r.maintainCase||!1,N=r.custom&&"object"==typeof r.custom?r.custom:N,g=+r.truncate>1&&r.truncate||!1,k=r.uric||!1,y=r.uricNoSlash||!1,f=r.mark||!1,C=!1!==r.symbols&&!1!==r.lang,S=r.separator||S,k&&(R+=u),y&&(R+=s),f&&(R+=l),p=r.lang&&o[r.lang]&&C?o[r.lang]:C?o.en:{},z=r.lang&&i[r.lang]?i[r.lang]:!1===r.lang||!0===r.lang?{}:i.en,r.titleCase&&"number"==typeof r.titleCase.length&&Array.prototype.toString.call(r.titleCase)?(r.titleCase.forEach(function(a){N[a+""]=a+""}),d=!0):d=!!r.titleCase,r.custom&&"number"==typeof r.custom.length&&Array.prototype.toString.call(r.custom)&&r.custom.forEach(function(a){N[a+""]=a+""}),Object.keys(N).forEach(function(e){var n;n=e.length>1?new RegExp("\\b"+h(e)+"\\b","gi"):new RegExp(h(e),"gi"),a=a.replace(n,N[e])});for(v in N)R+=v}for(R=h(R+=S),O=!1,j=!1,A=0,E=(a=a.replace(/(^\s+|\s+$)/g,"")).length;A=0?(U+=v,v=""):!0===j?(v=t[U]+e[v],U=""):v=O&&e[v].match(/[A-Za-z0-9]/)?" "+e[v]:e[v],O=!1,j=!1):v in t?(U+=v,v="",A===E-1&&(v=t[U]),j=!0):!p[v]||k&&-1!==u.indexOf(v)||y&&-1!==s.indexOf(v)?(!0===j?(v=t[U]+v,U="",j=!1):O&&(/[A-Za-z0-9]/.test(v)||w.substr(-1).match(/A-Za-z0-9]/))&&(v=" "+v),O=!1):(v=O||w.substr(-1).match(/[A-Za-z0-9]/)?S+p[v]:p[v],v+=void 0!==a[A+1]&&a[A+1].match(/[A-Za-z0-9]/)?S:"",O=!0),w+=v.replace(new RegExp("[^\\w\\s"+R+"_-]","g"),S);return d&&(w=w.replace(/(\w)(\S*)/g,function(a,e,n){var t=e.toUpperCase()+(null!==n?n:"");return Object.keys(N).indexOf(t.toLowerCase())<0?t:t.toLowerCase()})),w=w.replace(/\s+/g,S).replace(new RegExp("\\"+S+"+","g"),S).replace(new RegExp("(^\\"+S+"+|\\"+S+"+$)","g"),""),g&&w.length>g&&(b=w.charAt(g)===S,w=w.slice(0,g),b||(w=w.slice(0,w.lastIndexOf(S)))),m||d||(w=w.toLowerCase()),w},m=function(a){return function(e){return r(e,a)}},h=function(a){return a.replace(/[-\\^$*+?.()|[\]{}\/]/g,"\\$&")},c=function(a,e){for(var n in e)if(e[n]===a)return!0};if("undefined"!=typeof module&&module.exports)module.exports=r,module.exports.createSlug=m;else if("undefined"!=typeof define&&define.amd)define([],function(){return r});else try{if(a.getSlug||a.createSlug)throw"speakingurl: globals exists /(getSlug|createSlug)/";a.getSlug=r,a.createSlug=m}catch(a){}}(this); \ No newline at end of file diff --git a/templates/pikau/glossaryterm_form_create.html b/templates/pikau/glossaryterm_form_create.html index 05c3fab..221f634 100644 --- a/templates/pikau/glossaryterm_form_create.html +++ b/templates/pikau/glossaryterm_form_create.html @@ -15,8 +15,9 @@ {% endblock page_heading %} {% block content %} -
      {% csrf_token %} - {{ form|crispy }} - -
      + {% crispy form %} {% endblock content %} + +{% block scripts %} + {% include 'snippets/slugify.html' %} +{% endblock scripts %} diff --git a/templates/pikau/glossaryterm_form_update.html b/templates/pikau/glossaryterm_form_update.html index 34275bc..c8fa16d 100644 --- a/templates/pikau/glossaryterm_form_update.html +++ b/templates/pikau/glossaryterm_form_update.html @@ -15,8 +15,9 @@ {% endblock page_heading %} {% block content %} -
      {% csrf_token %} - {{ form|crispy }} - -
      + {% crispy form %} {% endblock content %} + +{% block scripts %} + {% include 'snippets/slugify.html' %} +{% endblock scripts %} diff --git a/templates/snippets/slugify.html b/templates/snippets/slugify.html new file mode 100644 index 0000000..8f7a9d7 --- /dev/null +++ b/templates/snippets/slugify.html @@ -0,0 +1,10 @@ +{% load static %} + + + From d78031710efac4501910d685c77ecb9de93432ea Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Tue, 22 May 2018 22:26:28 +1200 Subject: [PATCH 30/35] Add create/update views for files --- files/forms.py | 37 ++++++++++++++++++ files/models.py | 9 +++++ files/tables.py | 1 + files/urls.py | 20 +++++++++- files/views.py | 38 ++++++++++++++++++- pikau/mixins.py | 1 + pikau/models.py | 8 ++++ pikau/tables.py | 3 +- pikau/urls.py | 2 +- templates/base.html | 4 +- templates/files/file_detail.html | 29 ++++++++++++++ templates/files/file_form_create.html | 22 +++++++++++ templates/files/file_form_update.html | 22 +++++++++++ .../files/{index.html => file_list.html} | 6 +++ templates/index.html | 2 +- tests/files/views/test_index_view.py | 6 +-- 16 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 files/forms.py create mode 100644 templates/files/file_detail.html create mode 100644 templates/files/file_form_create.html create mode 100644 templates/files/file_form_update.html rename templates/files/{index.html => file_list.html} (81%) diff --git a/files/forms.py b/files/forms.py new file mode 100644 index 0000000..da50294 --- /dev/null +++ b/files/forms.py @@ -0,0 +1,37 @@ +"""Forms for files application.""" + +from django.forms import ModelForm +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit, Field +from files.models import ( + File +) + + +class FileForm(ModelForm): + """Form for pages relating to actions of files.""" + + def __init__(self, *args, **kwargs): + """Set helper for form layout.""" + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Field("filename", css_class="slug-source"), + "description", + "location", + "licence", + "slug", + Submit("submit", "Submit"), + ) + + class Meta: + """Meta attributes of GlossaryForm.""" + + model = File + fields = ( + "filename", + "description", + "location", + "licence", + "slug", + ) diff --git a/files/models.py b/files/models.py index 866c5fd..eb0a2a7 100644 --- a/files/models.py +++ b/files/models.py @@ -2,6 +2,7 @@ from django.db import models from django.core.exceptions import ObjectDoesNotExist +from django.urls import reverse def default_licence(): @@ -60,6 +61,14 @@ class File(models.Model): null=True, ) + def get_absolute_url(self): + """Return the URL for a file. + + Returns: + URL as string. + """ + return reverse("files:file_detail", args=[self.slug]) + def __str__(self): """Text representation of File object. diff --git a/files/tables.py b/files/tables.py index 82c7f5c..53bb4ad 100644 --- a/files/tables.py +++ b/files/tables.py @@ -9,6 +9,7 @@ class FileTable(tables.Table): """Table to display all files.""" + filename = tables.LinkColumn() licence = tables.RelatedLinkColumn() class Meta: diff --git a/files/urls.py b/files/urls.py index fd220d2..a8a8b20 100644 --- a/files/urls.py +++ b/files/urls.py @@ -24,6 +24,24 @@ path( "", views.FileList.as_view(), - name="index" + name="file_list" + ), + # eg: /files/file/view/file-1/ + path( + "file/view//", + views.FileDetailView.as_view(), + name="file_detail" + ), + # eg: /files/file/create/ + path( + "file/create/", + views.FileCreateView.as_view(), + name="file_create" + ), + # eg: /files/file/update/file-1/ + path( + "file/update//", + views.FileUpdateView.as_view(), + name="file_update" ), ] diff --git a/files/views.py b/files/views.py index da725b1..b5fb061 100644 --- a/files/views.py +++ b/files/views.py @@ -3,17 +3,53 @@ from django_tables2 import SingleTableMixin from django_filters.views import FilterView from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.messages.views import SuccessMessageMixin +from django.urls import reverse_lazy +from django.views.generic import ( + DetailView, + CreateView, + UpdateView, +) from files.tables import FileTable from files.filters import FileFilter from files.models import ( File, ) +from files.forms import ( + FileForm, +) class FileList(LoginRequiredMixin, SingleTableMixin, FilterView): """View for the file list page.""" - template_name = "files/index.html" + template_name = "files/file_list.html" model = File table_class = FileTable filterset_class = FileFilter + + +class FileDetailView(LoginRequiredMixin, DetailView): + """View for a file.""" + + model = File + + +class FileCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): + """View for creating a glossary definition.""" + + model = File + form_class = FileForm + template_name = "files/file_form_create.html" + success_message = "File created!" + success_url = reverse_lazy("files:file_list") + + +class FileUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): + """View for updating a glossary definition.""" + + model = File + form_class = FileForm + template_name = "files/file_form_update.html" + success_message = "File updated!" + success_url = reverse_lazy("files:file_list") diff --git a/pikau/mixins.py b/pikau/mixins.py index f237d85..8d7a367 100644 --- a/pikau/mixins.py +++ b/pikau/mixins.py @@ -13,5 +13,6 @@ def delete(self, request, *args, **kwargs): class TopicActionMixin(object): + """Topic mixin.""" fields = ("name", "slug") diff --git a/pikau/models.py b/pikau/models.py index 1c51797..49da107 100644 --- a/pikau/models.py +++ b/pikau/models.py @@ -53,6 +53,14 @@ class GlossaryTerm(models.Model): term = models.CharField(max_length=200, unique=True) description = models.TextField() + def get_absolute_url(self): + """Return the canonical URL for a glossary term. + + Returns: + URL as string. + """ + return reverse("pikau:glossaryterm_detail", args=[self.slug]) + def __str__(self): """Text representation of GlossaryTerm object. diff --git a/pikau/tables.py b/pikau/tables.py index 7c84e14..2ea73bc 100644 --- a/pikau/tables.py +++ b/pikau/tables.py @@ -1,6 +1,5 @@ """Tables for the pikau application.""" -from django_tables2 import A import django_tables2 as tables from pikau.models import ( GlossaryTerm, @@ -10,7 +9,7 @@ class GlossaryTermTable(tables.Table): """Table to display all glossary terms.""" - term = tables.LinkColumn("pikau:glossaryterm_detail", args=[A("slug")]) + term = tables.LinkColumn() slug = tables.TemplateColumn(template_code="{{ record.slug }}") class Meta: diff --git a/pikau/urls.py b/pikau/urls.py index c3df88d..e7eff59 100644 --- a/pikau/urls.py +++ b/pikau/urls.py @@ -40,7 +40,7 @@ ), # eg: /pikau/glossary/view/slug-1/ path( - "glossary/view/", + "glossary/view//", views.GlossaryDetailView.as_view(), name="glossaryterm_detail" ), diff --git a/templates/base.html b/templates/base.html index 85e5c04..8ff9dc9 100644 --- a/templates/base.html +++ b/templates/base.html @@ -36,7 +36,7 @@ {% trans "Pīkau" %} - + {% trans "Files" %}
      @@ -112,7 +112,7 @@

      {% trans "Pīkau" %}
    • - + {% trans "Files" %}
    • diff --git a/templates/files/file_detail.html b/templates/files/file_detail.html new file mode 100644 index 0000000..0370639 --- /dev/null +++ b/templates/files/file_detail.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Files" "files:file_list" %} + {% breadcrumb file.filename "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + File: {{ file.filename }} +
      + {% with text='Update file' %} + {% url 'files:file_update' file.slug as update_url %} + {% include "snippets/update_button.html" %} + {% endwith %} +
      +{% endblock page_heading %} + +{% block content %} +

      {{ filename.location }}

      + +

      {{ file.licence.name }}

      + +

      {{ file.description }}

      + +

      Slug: {{ file.slug }}

      +{% endblock content %} diff --git a/templates/files/file_form_create.html b/templates/files/file_form_create.html new file mode 100644 index 0000000..551839e --- /dev/null +++ b/templates/files/file_form_create.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} +{% load crispy_forms_tags %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Files" "files:file_list" %} + {% breadcrumb "Create file" "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + Create new file +{% endblock page_heading %} + +{% block content %} + {% crispy form %} +{% endblock content %} + +{% block scripts %} + {% include 'snippets/slugify.html' %} +{% endblock scripts %} diff --git a/templates/files/file_form_update.html b/templates/files/file_form_update.html new file mode 100644 index 0000000..2683952 --- /dev/null +++ b/templates/files/file_form_update.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% load django_bootstrap_breadcrumbs %} +{% load crispy_forms_tags %} + +{% block breadcrumbs %} + {% breadcrumb "Home" "/" %} + {% breadcrumb "Files" "files:file_list" %} + {% breadcrumb "Update file" "" %} +{% endblock breadcrumbs %} + +{% block page_heading %} + Update file +{% endblock page_heading %} + +{% block content %} + {% crispy form %} +{% endblock content %} + +{% block scripts %} + {% include 'snippets/slugify.html' %} +{% endblock scripts %} diff --git a/templates/files/index.html b/templates/files/file_list.html similarity index 81% rename from templates/files/index.html rename to templates/files/file_list.html index 177b7e7..11c31ff 100644 --- a/templates/files/index.html +++ b/templates/files/file_list.html @@ -13,6 +13,12 @@ {% block page_heading %} Files +
      + {% with text='Add file' %} + {% url 'files:file_create' as add_url %} + {% include "snippets/add_button.html" %} + {% endwith %} +
      {% endblock page_heading %} {% block content %} diff --git a/templates/index.html b/templates/index.html index e7a128b..2da83f6 100644 --- a/templates/index.html +++ b/templates/index.html @@ -64,7 +64,7 @@

      Pīkau

      Files

      Lists files used within this project with their licences.

      - + View files
      diff --git a/tests/files/views/test_index_view.py b/tests/files/views/test_index_view.py index 6052186..3f17b59 100644 --- a/tests/files/views/test_index_view.py +++ b/tests/files/views/test_index_view.py @@ -12,13 +12,13 @@ def __init__(self, *args, **kwargs): self.test_data = FileTestDataGenerator() def test_index_with_no_files(self): - url = reverse("files:index") + url = reverse("files:file_list") response = self.client.get(url) self.assertEqual(HTTPStatus.OK, response.status_code) def test_index_with_one_file(self): obj = self.test_data.create_file(1) - url = reverse("files:index") + url = reverse("files:file_list") response = self.client.get(url) self.assertEqual(HTTPStatus.OK, response.status_code) self.assertQuerysetEqual( @@ -30,7 +30,7 @@ def test_index_with_multiple_files(self): obj_1 = self.test_data.create_file(1) obj_3 = self.test_data.create_file(3) obj_2 = self.test_data.create_file(2) - url = reverse("files:index") + url = reverse("files:file_list") response = self.client.get(url) self.assertEqual(HTTPStatus.OK, response.status_code) self.assertQuerysetEqual( From 46d9d7952d1e6066ae937c015e589102db7beea7 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Wed, 23 May 2018 07:03:20 +1200 Subject: [PATCH 31/35] Display slug input with monospace font --- files/forms.py | 2 +- pikau/forms.py | 2 +- static/css/website.css | 3 +++ templates/snippets/slugify.html | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/files/forms.py b/files/forms.py index da50294..7c9add9 100644 --- a/files/forms.py +++ b/files/forms.py @@ -20,7 +20,7 @@ def __init__(self, *args, **kwargs): "description", "location", "licence", - "slug", + Field("slug", css_class="slug-input"), Submit("submit", "Submit"), ) diff --git a/pikau/forms.py b/pikau/forms.py index 20ab2d2..58481e5 100644 --- a/pikau/forms.py +++ b/pikau/forms.py @@ -18,7 +18,7 @@ def __init__(self, *args, **kwargs): self.helper.layout = Layout( Field("term", css_class="slug-source"), "description", - "slug", + Field("slug", css_class="slug-input"), Submit("submit", "Submit"), ) diff --git a/static/css/website.css b/static/css/website.css index 96166b0..f995c8e 100644 --- a/static/css/website.css +++ b/static/css/website.css @@ -125,3 +125,6 @@ footer > .container { .asteriskField { color: #dc3545; } +.slug-input { + font-family: monospace; +} diff --git a/templates/snippets/slugify.html b/templates/snippets/slugify.html index 8f7a9d7..36ae18b 100644 --- a/templates/snippets/slugify.html +++ b/templates/snippets/slugify.html @@ -4,7 +4,7 @@ From c25706abc8653c9bc707a4052ad001e2f396ce8c Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Wed, 23 May 2018 07:05:57 +1200 Subject: [PATCH 32/35] Fix link to file location --- templates/files/file_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/files/file_detail.html b/templates/files/file_detail.html index 0370639..801c986 100644 --- a/templates/files/file_detail.html +++ b/templates/files/file_detail.html @@ -19,7 +19,7 @@ {% endblock page_heading %} {% block content %} -

      {{ filename.location }}

      +

      {{ file.location }}

      {{ file.licence.name }}

      From 91539513928f1f2a7844b092b3d4c544b38ebc1f Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Wed, 23 May 2018 08:09:38 +1200 Subject: [PATCH 33/35] Display warning if unknown licences (#36) --- files/views.py | 10 ++++++++++ static/css/website.css | 3 +++ templates/files/file_list.html | 9 ++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/files/views.py b/files/views.py index b5fb061..939f81e 100644 --- a/files/views.py +++ b/files/views.py @@ -28,6 +28,16 @@ class FileList(LoginRequiredMixin, SingleTableMixin, FilterView): table_class = FileTable filterset_class = FileFilter + def get_context_data(self, **kwargs): + """Provide the context data for the view. + + Returns: + Dictionary of context data. + """ + context = super(FileList, self).get_context_data(**kwargs) + context["unknown_licences"] = File.objects.filter(licence__name="Unknown").count() + return context + class FileDetailView(LoginRequiredMixin, DetailView): """View for a file.""" diff --git a/static/css/website.css b/static/css/website.css index f995c8e..a6e06fd 100644 --- a/static/css/website.css +++ b/static/css/website.css @@ -128,3 +128,6 @@ footer > .container { .slug-input { font-family: monospace; } +.form-inline label { + margin-right: 0.5rem; +} diff --git a/templates/files/file_list.html b/templates/files/file_list.html index 11c31ff..344fb53 100644 --- a/templates/files/file_list.html +++ b/templates/files/file_list.html @@ -28,10 +28,17 @@ {% endblocktrans %}

      + {% if unknown_licences %} + + {% endif %} + {% if filter %}
      {{ filter.form|crispy }} - +
      {% endif %} From f5fa2234b0293bc6ae304a0d4b43c9097e8402e5 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Wed, 23 May 2018 08:54:06 +1200 Subject: [PATCH 34/35] Update CHANGELOG for 0.4.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 423a444..0e92862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 0.4.0 (Pre-release) + +- Add files application for tracking files and their licences. (fixes #34) + - Display warning if any files have unknown licence. (fixes #36) + - Allow filtering of files by licence type. + - Allow users to add and update files. +- Allow users to add and update pīkau glossary entries. +- Begin process of replacing manual HTML tables with tables from django-tables2. +- Dependency updates: + - Add django-tables2 2.0.0a2. + - Add django-filter 1.1.0. + - Update django from 2.0.4 to 2.0.5. + - Update django-allauth from 0.35.0 to 0.36.0. + - Update django-anymail from 2.0 to 2.2. + - Update django-debug-toolbar from 1.8 to 1.9.1. + - Update psycopg2 from 2.7.3.1 to 2.7.4. + - Update gunicorn from 19.7.1 to 19.8.1. + - Update python-markdown-math from 0.3 to 0.5. + ## 0.3.0 (Pre-release) - Add milestone list and detail pages, with table showing milestone statuses. (fixes #9) From f19087f6355cf90b415ef7a2164d312ff840c875 Mon Sep 17 00:00:00 2001 From: Jack Morgan Date: Wed, 23 May 2018 08:54:20 +1200 Subject: [PATCH 35/35] Increment version number to 0.4.0 --- config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/__init__.py b/config/__init__.py index 9535603..31e00d6 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -1,3 +1,3 @@ """Module for Django system configuration.""" -__version__ = "0.3.0" +__version__ = "0.4.0"