From d33eddbc11e9dbf3253cb7fbd5098a6d6f8138b7 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Sat, 27 Oct 2018 17:50:15 +0200
Subject: [PATCH] Start work on Ontology

---
 SciPost_v1/settings/base.py                   |  1 +
 SciPost_v1/urls.py                            |  1 +
 ontology/__init__.py                          |  0
 ontology/admin.py                             | 19 +++++++++
 ontology/migrations/0001_initial.py           | 38 +++++++++++++++++
 .../migrations/0002_auto_20181027_1748.py     | 27 ++++++++++++
 .../migrations/0003_auto_20181027_1748.py     | 20 +++++++++
 ontology/migrations/__init__.py               |  0
 ontology/models.py                            | 28 +++++++++++++
 ontology/templates/ontology/base.html         | 13 ++++++
 ontology/templates/ontology/topic_form.html   | 21 ++++++++++
 ontology/templates/ontology/topic_list.html   | 34 +++++++++++++++
 ontology/urls.py                              | 25 +++++++++++
 ontology/views.py                             | 42 +++++++++++++++++++
 organizations/views.py                        |  1 -
 .../commands/add_groups_and_permissions.py    | 10 +++++
 16 files changed, 279 insertions(+), 1 deletion(-)
 create mode 100644 ontology/__init__.py
 create mode 100644 ontology/admin.py
 create mode 100644 ontology/migrations/0001_initial.py
 create mode 100644 ontology/migrations/0002_auto_20181027_1748.py
 create mode 100644 ontology/migrations/0003_auto_20181027_1748.py
 create mode 100644 ontology/migrations/__init__.py
 create mode 100644 ontology/models.py
 create mode 100644 ontology/templates/ontology/base.html
 create mode 100644 ontology/templates/ontology/topic_form.html
 create mode 100644 ontology/templates/ontology/topic_list.html
 create mode 100644 ontology/urls.py
 create mode 100644 ontology/views.py

diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py
index 966b7dd41..1f517a0c5 100644
--- a/SciPost_v1/settings/base.py
+++ b/SciPost_v1/settings/base.py
@@ -106,6 +106,7 @@ INSTALLED_APPS = (
     'submissions',
     'theses',
     'virtualmeetings',
+    'ontology',
     'organizations',
     'proceedings',
     'production',
diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py
index d8fe905e9..9f9948655 100644
--- a/SciPost_v1/urls.py
+++ b/SciPost_v1/urls.py
@@ -57,6 +57,7 @@ urlpatterns = [
     url(r'^meetings/', include('virtualmeetings.urls', namespace="virtualmeetings")),
     url(r'^news/', include('news.urls', namespace="news")),
     url(r'^notifications/', include('notifications.urls', namespace="notifications")),
+    url(r'^ontology/', include('ontology.urls', namespace="ontology")),
     url(r'^organizations/', include('organizations.urls', namespace="organizations")),
     url(r'^petitions/', include('petitions.urls', namespace="petitions")),
     url(r'^preprints/', include('preprints.urls', namespace="preprints")),
diff --git a/ontology/__init__.py b/ontology/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ontology/admin.py b/ontology/admin.py
new file mode 100644
index 000000000..d02a46aa6
--- /dev/null
+++ b/ontology/admin.py
@@ -0,0 +1,19 @@
+__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.contrib import admin
+
+from .models import Tag, Topic
+
+
+class TagAdmin(admin.ModelAdmin):
+    pass
+
+admin.site.register(Tag, TagAdmin)
+
+
+class TopicAdmin(admin.ModelAdmin):
+    pass
+
+admin.site.register(Topic, TopicAdmin)
diff --git a/ontology/migrations/0001_initial.py b/ontology/migrations/0001_initial.py
new file mode 100644
index 000000000..56b3e3058
--- /dev/null
+++ b/ontology/migrations/0001_initial.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-10-27 15:31
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Relation',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Tag',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=32, unique=True)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Topic',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=256, unique=True)),
+                ('slug', models.SlugField(allow_unicode=True, unique=True)),
+                ('tags', models.ManyToManyField(to='ontology.Tag')),
+            ],
+        ),
+    ]
diff --git a/ontology/migrations/0002_auto_20181027_1748.py b/ontology/migrations/0002_auto_20181027_1748.py
new file mode 100644
index 000000000..6618082b2
--- /dev/null
+++ b/ontology/migrations/0002_auto_20181027_1748.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-10-27 15:48
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ontology', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.DeleteModel(
+            name='Relation',
+        ),
+        migrations.AlterModelOptions(
+            name='tag',
+            options={'ordering': ['name']},
+        ),
+        migrations.AlterField(
+            model_name='topic',
+            name='tags',
+            field=models.ManyToManyField(blank=True, null=True, to='ontology.Tag'),
+        ),
+    ]
diff --git a/ontology/migrations/0003_auto_20181027_1748.py b/ontology/migrations/0003_auto_20181027_1748.py
new file mode 100644
index 000000000..4d3c2cfca
--- /dev/null
+++ b/ontology/migrations/0003_auto_20181027_1748.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-10-27 15:48
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ontology', '0002_auto_20181027_1748'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='topic',
+            name='tags',
+            field=models.ManyToManyField(blank=True, to='ontology.Tag'),
+        ),
+    ]
diff --git a/ontology/migrations/__init__.py b/ontology/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ontology/models.py b/ontology/models.py
new file mode 100644
index 000000000..fe750e8c2
--- /dev/null
+++ b/ontology/models.py
@@ -0,0 +1,28 @@
+__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.db import models
+
+
+class Tag(models.Model):
+    """
+    Tags can be attached to a Topic to specify which category it fits.
+    Examples: Concept, Device, Model, Theory, ...
+    """
+    name = models.CharField(max_length=32, unique=True)
+
+    class Meta:
+        ordering = ['name']
+
+
+class Topic(models.Model):
+    """
+    A Topic represents one of the nodes in the ontology.
+    """
+    name = models.CharField(max_length=256, unique=True)
+    slug = models.SlugField(unique=True, allow_unicode=True)
+    tags = models.ManyToManyField(Tag, blank=True)
+
+    def __str__(self):
+        return self.name
diff --git a/ontology/templates/ontology/base.html b/ontology/templates/ontology/base.html
new file mode 100644
index 000000000..2c038d8d6
--- /dev/null
+++ b/ontology/templates/ontology/base.html
@@ -0,0 +1,13 @@
+{% extends 'scipost/base.html' %}
+
+{% block breadcrumb %}
+    <div class="container-outside header">
+        <div class="container">
+            <nav class="breadcrumb hidden-sm-down">
+                {% block breadcrumb_items %}
+                    <a href="{% url 'ontology:topics' %}" class="breadcrumb-item">Topics</a>
+                {% endblock %}
+            </nav>
+        </div>
+    </div>
+{% endblock %}
diff --git a/ontology/templates/ontology/topic_form.html b/ontology/templates/ontology/topic_form.html
new file mode 100644
index 000000000..e9636848b
--- /dev/null
+++ b/ontology/templates/ontology/topic_form.html
@@ -0,0 +1,21 @@
+{% extends 'ontology/base.html' %}
+
+{% load bootstrap %}
+
+{% block breadcrumb_items %}
+    {{ block.super }}
+    <span class="breadcrumb-item">{% if form.instance.id %}Update {{ form.instance }}{% else %}Add new Topic{% endif %}</span>
+{% endblock %}
+
+{% block pagetitle %}: Topics{% endblock pagetitle %}
+
+{% block content %}
+<div class="row">
+  <div class="col-12">
+    <form action="" method="post">
+      {% csrf_token %}
+      {{ form|bootstrap }}
+      <input type="submit" value="Submit" class="btn btn-primary">
+  </div>
+</div>
+{% endblock content %}
diff --git a/ontology/templates/ontology/topic_list.html b/ontology/templates/ontology/topic_list.html
new file mode 100644
index 000000000..66c3e0c85
--- /dev/null
+++ b/ontology/templates/ontology/topic_list.html
@@ -0,0 +1,34 @@
+{% extends 'ontology/base.html' %}
+
+{% block pagetitle %}: Topics{% endblock pagetitle %}
+
+{% block breadcrumb_items %}
+{{ block.super }}
+<span class="breadcrumb-item">Topics</span>
+{% endblock %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-12">
+    <h3 class="highlight">Topics</h3>
+    {% if perms.scipost.can_manage_ontology %}
+    <ul>
+      <li><a href="{% url 'ontology:topic_create' %}">Add a Topic</a></li>
+    </ul>
+    {% endif %}
+  </div>
+</div>
+
+
+<div class="row">
+  <div class="col-12">
+    <ul>
+      {% for topic in object_list %}
+      <li>{{ topic }}</li>
+      {% endfor %}
+    </ul>
+  </div>
+</div>
+
+{% endblock content %}
diff --git a/ontology/urls.py b/ontology/urls.py
new file mode 100644
index 000000000..e65bd07d9
--- /dev/null
+++ b/ontology/urls.py
@@ -0,0 +1,25 @@
+__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.conf.urls import url
+
+from . import views
+
+urlpatterns = [
+    url(
+        r'^topic/add/$',
+        views.TopicCreateView.as_view(),
+        name='topic_create'
+        ),
+    url(
+        r'^topic/(?P<slug>[-\w]+)/update/$',
+        views.TopicCreateView.as_view(),
+        name='topic_update'
+        ),
+    url(
+        r'^topics/$',
+        views.TopicListView.as_view(),
+        name='topics'
+        ),
+]
diff --git a/ontology/views.py b/ontology/views.py
new file mode 100644
index 000000000..a3164e028
--- /dev/null
+++ b/ontology/views.py
@@ -0,0 +1,42 @@
+__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.core.urlresolvers import reverse_lazy
+from django.views.generic.detail import DetailView
+from django.views.generic.edit import CreateView, UpdateView
+from django.views.generic.list import ListView
+
+from .models import Topic
+
+from scipost.mixins import PermissionsMixin
+
+
+class TopicCreateView(PermissionsMixin, CreateView):
+    """
+    Create a new Topic for an Ontology.
+    """
+    permission_required = 'scipost.can_manage_ontology'
+    model = Topic
+    fields = '__all__'
+    template_name = 'ontology/topic_form.html'
+    success_url = reverse_lazy('ontology:topics')
+
+
+class TopicUpdateView(PermissionsMixin, UpdateView):
+    """
+    Update a Topic for an Ontology.
+    """
+    permission_required = 'scipost.can_manage_ontology'
+    model = Topic
+    fields = '__all__'
+    template_name = 'ontology/topic_form.html'
+    success_url = reverse_lazy('ontology:topics')
+
+
+class TopicListView(ListView):
+    model = Topic
+
+
+class TopicDetailView(DetailView):
+    model = Topic
diff --git a/organizations/views.py b/organizations/views.py
index d11f618d3..d10c41317 100644
--- a/organizations/views.py
+++ b/organizations/views.py
@@ -3,7 +3,6 @@ __license__ = "AGPL v3"
 
 
 from django.core.urlresolvers import reverse_lazy
-from django.shortcuts import render
 from django.utils import timezone
 from django.views.generic.detail import DetailView
 from django.views.generic.edit import CreateView, UpdateView, DeleteView
diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py
index f701e296f..a6be4d4a0 100644
--- a/scipost/management/commands/add_groups_and_permissions.py
+++ b/scipost/management/commands/add_groups_and_permissions.py
@@ -318,6 +318,13 @@ class Command(BaseCommand):
             name='Can manage Mailchimp settings',
             content_type=content_type)
 
+        # Ontology
+        can_manage_ontology, created = Permission.objects.get_or_create(
+            codename='can_manage_ontology',
+            name='Can manage ontology',
+            content_type=content_type)
+
+
         # Assign permissions to groups
         SciPostAdmin.permissions.set([
             can_read_all_privacy_sensitive_data,
@@ -347,6 +354,7 @@ class Command(BaseCommand):
             can_view_statistics,
             can_create_profiles,
             can_view_profiles,
+            can_manage_ontology,
         ])
 
         FinancialAdmin.permissions.set([
@@ -388,6 +396,7 @@ class Command(BaseCommand):
             can_view_statistics,
             can_create_profiles,
             can_view_profiles,
+            can_manage_ontology,
         ])
 
         EditorialCollege.permissions.set([
@@ -395,6 +404,7 @@ class Command(BaseCommand):
             can_take_charge_of_submissions,
             can_attend_VGMs,
             can_view_statistics,
+            can_manage_ontology,
         ])
 
         VettingEditors.permissions.set([
-- 
GitLab