From 674a2e4436d03f065a822fdb3a5bc333cc1e3a24 Mon Sep 17 00:00:00 2001 From: Jorran de Wit <jorrandewit@outlook.com> Date: Sun, 19 Mar 2017 21:35:15 +0100 Subject: [PATCH] Complete reconstruction of EditorialFellow depending on Contributor --- scipost/admin.py | 22 ++++-- scipost/factories.py | 19 +++-- scipost/management/commands/populate_db.py | 27 +++++-- scipost/migrations/0045_auto_20170319_2008.py | 38 ++++++++++ scipost/migrations/0046_auto_20170319_2032.py | 25 +++++++ scipost/migrations/0047_auto_20170319_2110.py | 25 +++++++ scipost/models.py | 75 ++++++++++++++++--- .../templates/scipost/_college_member.html | 4 - .../templates/scipost/_contributor_short.html | 4 + scipost/templates/scipost/about.html | 4 +- scipost/test_views.py | 8 +- scipost/views.py | 2 +- 12 files changed, 209 insertions(+), 44 deletions(-) create mode 100644 scipost/migrations/0045_auto_20170319_2008.py create mode 100644 scipost/migrations/0046_auto_20170319_2032.py create mode 100644 scipost/migrations/0047_auto_20170319_2110.py delete mode 100644 scipost/templates/scipost/_college_member.html create mode 100644 scipost/templates/scipost/_contributor_short.html diff --git a/scipost/admin.py b/scipost/admin.py index 047aa36ae..d9684a154 100644 --- a/scipost/admin.py +++ b/scipost/admin.py @@ -1,3 +1,5 @@ +import datetime + from django.contrib import admin from django.contrib.auth.admin import UserAdmin @@ -8,7 +10,7 @@ from scipost.models import Contributor, Remark,\ AffiliationObject,\ SupportingPartner, SPBMembershipAgreement, RegistrationInvitation,\ AuthorshipClaim, PrecookedEmail,\ - EditorialCollege, EditorialCollegeMember + EditorialCollege, EditorialCollegeFellow class ContributorInline(admin.StackedInline): @@ -87,10 +89,18 @@ class EditorialCollegeAdmin(admin.ModelAdmin): admin.site.register(EditorialCollege, EditorialCollegeAdmin) -class EditorialCollegeMemberAdmin(admin.ModelAdmin): - list_display = ('__str__', 'college') - list_filter = ('college', 'subtitle') - search_fields = ['name', 'subtitle', 'college'] +def college_fellow_is_active(fellow): + '''Check if fellow is currently active.''' + return fellow.is_active() + + +class EditorialCollegeFellowAdmin(admin.ModelAdmin): + list_display = ('__str__', 'college', college_fellow_is_active) + list_filter = ('college', 'contributor__user') + search_fields = ['college', 'contributor__user'] + fields = ('contributor', 'college', 'start_date', 'until_date') + + college_fellow_is_active.boolean = True -admin.site.register(EditorialCollegeMember, EditorialCollegeMemberAdmin) +admin.site.register(EditorialCollegeFellow, EditorialCollegeFellowAdmin) diff --git a/scipost/factories.py b/scipost/factories.py index 8a747b60b..41aba74fb 100644 --- a/scipost/factories.py +++ b/scipost/factories.py @@ -4,17 +4,22 @@ import random from django.contrib.auth import get_user_model from django.contrib.auth.models import Group -from .models import Contributor, EditorialCollege, EditorialCollegeMember +from django_countries.data import COUNTRIES + +from .models import Contributor, EditorialCollege, EditorialCollegeFellow, TITLE_CHOICES class ContributorFactory(factory.django.DjangoModelFactory): class Meta: model = Contributor - title = "MR" + title = random.choice(list(dict(TITLE_CHOICES).keys())) user = factory.SubFactory('scipost.factories.UserFactory', contributor=None) status = 1 # normal user vetted_by = factory.SubFactory('scipost.factories.ContributorFactory', vetted_by=None) + personalwebpage = factory.Faker('url') + country_of_employment = factory.Iterator(list(COUNTRIES)) + affiliation = factory.Faker('company') class VettingEditorFactory(ContributorFactory): @@ -59,12 +64,10 @@ class EditorialCollegeFactory(factory.django.DjangoModelFactory): discipline = random.choice(['Physics', 'Chemistry', 'Medicine']) -class EditorialCollegeMemberFactory(factory.django.DjangoModelFactory): +class EditorialCollegeFellowFactory(factory.django.DjangoModelFactory): class Meta: - model = EditorialCollegeMember + model = EditorialCollegeFellow - title = factory.Faker('prefix') - name = factory.Faker('name') - link = factory.Faker('url') - subtitle = factory.Faker('company') college = factory.Iterator(EditorialCollege.objects.all()) + contributor = factory.Iterator(Contributor.objects.exclude( + user__username='deleted').order_by('?')) diff --git a/scipost/management/commands/populate_db.py b/scipost/management/commands/populate_db.py index e1296b85e..e99f98b54 100644 --- a/scipost/management/commands/populate_db.py +++ b/scipost/management/commands/populate_db.py @@ -2,7 +2,7 @@ from django.core.management.base import BaseCommand from news.factories import NewsItemFactory -from ...factories import EditorialCollegeFactory, EditorialCollegeMemberFactory +from ...factories import ContributorFactory, EditorialCollegeFactory, EditorialCollegeFellowFactory class Command(BaseCommand): @@ -15,11 +15,18 @@ class Command(BaseCommand): help='Add NewsItems', ) parser.add_argument( - '--editorial-college', + '--contributor', + action='store_true', + dest='contributor', + default=False, + help='Add Contributors', + ) + parser.add_argument( + '--college', action='store_true', dest='editorial-college', default=False, - help='Add Editorial College and members', + help='Add Editorial College and Fellows (Contributors required)', ) parser.add_argument( '--all', @@ -30,19 +37,25 @@ class Command(BaseCommand): ) def handle(self, *args, **kwargs): + if kwargs['contributor'] or kwargs['all']: + self.create_contributors() if kwargs['editorial-college'] or kwargs['all']: self.create_editorial_college() - self.create_editorial_college_members() + self.create_editorial_college_fellows() if kwargs['news'] or kwargs['all']: self.create_news_items() + def create_contributors(self): + ContributorFactory.create_batch(5) + self.stdout.write(self.style.SUCCESS('Successfully created Contributors.')) + def create_editorial_college(self): EditorialCollegeFactory.create_batch(5) self.stdout.write(self.style.SUCCESS('Successfully created Editorial College\'s.')) - def create_editorial_college_members(self): - EditorialCollegeMemberFactory.create_batch(20) - self.stdout.write(self.style.SUCCESS('Successfully created Editorial College Members.')) + def create_editorial_college_fellows(self): + EditorialCollegeFellowFactory.create_batch(5) + self.stdout.write(self.style.SUCCESS('Successfully created Editorial College Fellows.')) def create_news_items(self): NewsItemFactory.create_batch(5) diff --git a/scipost/migrations/0045_auto_20170319_2008.py b/scipost/migrations/0045_auto_20170319_2008.py new file mode 100644 index 000000000..0f3ef75bf --- /dev/null +++ b/scipost/migrations/0045_auto_20170319_2008.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-03-19 19:08 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import scipost.db.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0044_auto_20170319_0940'), + ] + + operations = [ + migrations.CreateModel( + name='EditorialCollegeFellow', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('latest_activity', scipost.db.fields.AutoDateTimeField(blank=True, default=django.utils.timezone.now, editable=False)), + ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fellows', to='scipost.EditorialCollege')), + ('contributor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='scipost.Contributor')), + ], + options={ + 'abstract': False, + }, + ), + migrations.RemoveField( + model_name='editorialcollegemember', + name='college', + ), + migrations.DeleteModel( + name='EditorialCollegeMember', + ), + ] diff --git a/scipost/migrations/0046_auto_20170319_2032.py b/scipost/migrations/0046_auto_20170319_2032.py new file mode 100644 index 000000000..2e15e2bf9 --- /dev/null +++ b/scipost/migrations/0046_auto_20170319_2032.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-03-19 19:32 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0045_auto_20170319_2008'), + ] + + operations = [ + migrations.AddField( + model_name='editorialcollegefellow', + name='start_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name='editorialcollegefellow', + name='until_date', + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/scipost/migrations/0047_auto_20170319_2110.py b/scipost/migrations/0047_auto_20170319_2110.py new file mode 100644 index 000000000..6f24286a6 --- /dev/null +++ b/scipost/migrations/0047_auto_20170319_2110.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-03-19 20:10 +from __future__ import unicode_literals + +from django.db import migrations, models +import scipost.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0046_auto_20170319_2032'), + ] + + operations = [ + migrations.AlterField( + model_name='contributor', + name='vetted_by', + field=models.ForeignKey(blank=True, null=True, on_delete=models.SET(scipost.models.get_sentinel_user), related_name='contrib_vetted_by', to='scipost.Contributor'), + ), + migrations.AlterUniqueTogether( + name='editorialcollegefellow', + unique_together=set([('contributor', 'college', 'start_date', 'until_date')]), + ), + ] diff --git a/scipost/models.py b/scipost/models.py index fcec39e5f..668f53644 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -4,6 +4,7 @@ from django import forms from django.contrib.auth.models import User from django.contrib.postgres.fields import ArrayField from django.db import models +from django.db.models import Q from django.template import Template, Context from django.utils import timezone from django.utils.safestring import mark_safe @@ -72,6 +73,12 @@ class TimeStampedModel(models.Model): abstract = True +def get_sentinel_user(): + '''Deceased people talk after their death.''' + user, new = User.objects.get_or_create(username='deleted') + return Contributor.objects.get_or_create(status=-4, user=user)[0] + + class Contributor(models.Model): """ All users of SciPost are Contributors. @@ -98,7 +105,7 @@ class Contributor(models.Model): default='', blank=True) personalwebpage = models.URLField(verbose_name='personal web page', blank=True) - vetted_by = models.ForeignKey('self', on_delete=models.CASCADE, + vetted_by = models.ForeignKey('self', on_delete=models.SET(get_sentinel_user), related_name="contrib_vetted_by", blank=True, null=True) accepts_SciPost_emails = models.BooleanField( @@ -517,6 +524,17 @@ class SPBMembershipAgreement(models.Model): # Static info models # ###################### +class FellowManager(models.Manager): + def current_fellows(self, *args, **kwargs): + today = datetime.date.today() + return self.filter( + Q(start_date__lte=today, until_date__isnull=True) | + Q(start_date__isnull=True, until_date__gte=today) | + Q(start_date__lte=today, until_date__gte=today) | + Q(start_date__isnull=True, until_date__isnull=True), + **kwargs) + + class EditorialCollege(models.Model): '''A SciPost Editorial College for a specific discipline.''' discipline = models.CharField(max_length=255, unique=True) @@ -524,17 +542,50 @@ class EditorialCollege(models.Model): def __str__(self): return self.discipline + @property + def active_fellows(self): + return self.fellows.current_fellows() -class EditorialCollegeMember(models.Model): - """ - Editorial College Members for non-functional use! - This model is used for static information purposes only. - """ - name = models.CharField(max_length=255) - title = models.CharField(max_length=10, blank=True) - link = models.URLField(blank=True) - subtitle = models.CharField(max_length=255, blank=True) - college = models.ForeignKey('scipost.EditorialCollege', related_name='member') + +class EditorialCollegeFellow(TimeStampedModel): + '''Editorial College Fellow connecting Editorial College and Contributors.''' + contributor = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE, + related_name='+') + college = models.ForeignKey('scipost.EditorialCollege', on_delete=models.CASCADE, + related_name='fellows') + start_date = models.DateField(null=True, blank=True) + until_date = models.DateField(null=True, blank=True) + + objects = FellowManager() + + class Meta: + unique_together = ('contributor', 'college', 'start_date', 'until_date') def __str__(self): - return ('%s %s' % (self.title, self.name)).strip() + return self.contributor.__str__() + + def is_active(self): + today = datetime.date.today() + if not self.start_date: + if not self.until_date: + return True + elif today <= self.until_date: + return True + return False + elif not self.until_date: + if today >= self.start_date: + return True + elif today >= self.start_date and today <= self.until_date: + return True + return False + # + # + # if not self.start_date and not self.until_date: + # return True + # elif not self.start_date and today <= self.until_date: + # return True + # elif today >= self.start_date and not self.until_date: + # return True + # elif today >= self.start_date and today <= self.until_date: + # return True + # return False diff --git a/scipost/templates/scipost/_college_member.html b/scipost/templates/scipost/_college_member.html deleted file mode 100644 index a6114acac..000000000 --- a/scipost/templates/scipost/_college_member.html +++ /dev/null @@ -1,4 +0,0 @@ -{% if member.link %}<a target="_blank" href="{{ member.link }}">{% endif %}{{ member }}{% if member.link %}</a>{% endif %} -{% if member.subtitle %} -<br/>({{member.subtitle}}) -{% endif %} diff --git a/scipost/templates/scipost/_contributor_short.html b/scipost/templates/scipost/_contributor_short.html new file mode 100644 index 000000000..52c7fb8bb --- /dev/null +++ b/scipost/templates/scipost/_contributor_short.html @@ -0,0 +1,4 @@ +{% if contributor.personalwebpage %}<a target="_blank" href="{{ contributor.personalwebpage }}">{% endif %}{{ contributor.get_title_display }} {{ contributor.user.first_name }} {{ contributor.user.last_name }}{% if contributor.personalwebpage %}</a>{% endif %} +{% if contributor.affiliation %} +<br/>({{contributor.affiliation}}) +{% endif %} diff --git a/scipost/templates/scipost/about.html b/scipost/templates/scipost/about.html index 725dc4092..36028f137 100644 --- a/scipost/templates/scipost/about.html +++ b/scipost/templates/scipost/about.html @@ -155,9 +155,9 @@ </div> <div class="row"> - {% for member in college.member.all %} + {% for fellow in college.active_fellows %} <div class="col-md-4 py-1"> - {% include 'scipost/_college_member.html' with member=member %} + {% include 'scipost/_contributor_short.html' with contributor=fellow.contributor %} </div> {% endfor %} </div> diff --git a/scipost/test_views.py b/scipost/test_views.py index d6b766630..5b764927f 100644 --- a/scipost/test_views.py +++ b/scipost/test_views.py @@ -8,8 +8,8 @@ from commentaries.forms import CommentarySearchForm from commentaries.models import Commentary from .factories import ContributorFactory,\ - EditorialCollegeMemberFactory, EditorialCollegeFactory -from .models import EditorialCollege, EditorialCollegeMember + EditorialCollegeFellowFactory, EditorialCollegeFactory +from .models import EditorialCollege, EditorialCollegeFellow class RequestCommentaryTest(TestCase): @@ -137,7 +137,7 @@ class AboutViewTest(TestCase): # Create College with 10 members self.college = EditorialCollegeFactory() - EditorialCollegeMemberFactory.create_batch(10) + EditorialCollegeFellowFactory.create_batch(10) def test_status_code_200_including_members(self): response = self.client.get(self.target) @@ -151,4 +151,4 @@ class AboutViewTest(TestCase): # Members exist in college self.assertTrue(college.member.count() >= 10) last_member = college.member.last() - self.assertTrue(isinstance(last_member, EditorialCollegeMember)) + self.assertTrue(isinstance(last_member, EditorialCollegeFellow)) diff --git a/scipost/views.py b/scipost/views.py index aad9aaf68..c491d98e5 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -1756,4 +1756,4 @@ def SPB_membership_request(request): class AboutView(ListView): model = EditorialCollege template_name = 'scipost/about.html' - queryset = EditorialCollege.objects.prefetch_related('member') + queryset = EditorialCollege.objects.prefetch_related('fellows__contributor') -- GitLab