diff --git a/.bootstraprc b/.bootstraprc index 1c2fbdb5c01a0db833debec771dabc78c2620d36..ab4a0cccb6882f155316d46dff87435d40a1502a 100644 --- a/.bootstraprc +++ b/.bootstraprc @@ -26,11 +26,14 @@ "reboot": true, "responsive-embed": true, "transitions": true, + "type": true, + "tooltip": true, "utilities": true, }, "scripts": { "alert": true, "collapse": true, + "tooltip": true, "util": true, } } diff --git a/docs/conf.py b/docs/conf.py index 3e9e0968c2fdc609d4300ce70ec393ce11dc75e6..e504443637f1e3f1027b3f8b89b69c106ff24243 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,9 @@ import django from django.conf import settings sys.path.insert(0, os.path.abspath('..')) -os.environ['DJANGO_SETTINGS_MODULE'] = 'SciPost_v1.settings' + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "SciPost_v1.settings") + #settings.configure() django.setup() diff --git a/journals/models.py b/journals/models.py index 9646853c69880f42447ac2dfa0bb927b0abc451e..d858d6bcc51394a7f299831ae2f5011494c52e87 100644 --- a/journals/models.py +++ b/journals/models.py @@ -169,6 +169,10 @@ class IssueManager(models.Manager): until_date__gte=timezone.now(), **kwargs).order_by('-until_date').first() + def get_last_filled_issue(self, *args, **kwargs): + return self.published(publication__isnull=False, + **kwargs).order_by('-until_date').first() + class Issue(models.Model): in_volume = models.ForeignKey(Volume, on_delete=models.CASCADE) @@ -199,6 +203,10 @@ class Issue(models.Model): return (' (' + self.start_date.strftime('%B') + '-' + self.until_date.strftime('%B') + ' ' + self.until_date.strftime('%Y') + ')') + def is_current(self): + return self.start_date <= timezone.now().date() and\ + self.until_date >= timezone.now().date() + def period(self): text = 'up to {{ until_month }} {{ year }}' template = Template(text) diff --git a/package.json b/package.json index 370a2c2c8e412e6a843cbefe6548b41e7bfdfe2e..903c1b0cbca65a7153672f3895dd591d30c1ca3d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "homepage": "https://www.scipost.org", "devDependencies": { "bootstrap": "^4.0.0-alpha.6", - "bootstrap-loader": "^2.0.0-beta.19", + "bootstrap-loader": "^2.0.0", "clean-webpack-plugin": "^0.1.15", "css-loader": "^0.26.1", "exports-loader": "^0.6.3", @@ -35,7 +35,7 @@ "style-loader": "^0.13.1", "tether": "^1.4.0", "url-loader": "^0.5.7", - "webpack": "^2.1.0-beta.19", + "webpack": "^2.2.1", "webpack-bundle-tracker": "^0.2.0", "webpack-glob-entry": "^2.1.1", "extract-text-webpack-plugin": "2.0.0-beta.5" diff --git a/scipost/admin.py b/scipost/admin.py index 047aa36ae7c5241e3562d2a31d86479c2694a4d4..87df8ce71cbb0db9e7a7289f731f4639c396f6a8 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, EditorialCollegeFellowship class ContributorInline(admin.StackedInline): @@ -87,10 +89,19 @@ 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 EditorialCollegeFellowshipAdmin(admin.ModelAdmin): + list_display = ('__str__', 'college', college_fellow_is_active) + list_filter = ('college', 'contributor__user') + search_fields = ['college__discipline', + 'contributor__user__first_name', 'contributor__user__last_name'] + fields = ('contributor', 'college', 'start_date', 'until_date') + + college_fellow_is_active.boolean = True -admin.site.register(EditorialCollegeMember, EditorialCollegeMemberAdmin) +admin.site.register(EditorialCollegeFellowship, EditorialCollegeFellowshipAdmin) diff --git a/scipost/factories.py b/scipost/factories.py index 8a747b60bc4112ba7df013156173ed3d36285e60..62eb21c81e74b9fa81c6d0c7892f06ef578bbc8c 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, EditorialCollegeFellowship, 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 EditorialCollegeFellowshipFactory(factory.django.DjangoModelFactory): class Meta: - model = EditorialCollegeMember + model = EditorialCollegeFellowship - 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 e1296b85e406604dfc2072597bd7b230f99cc803..4880a671c3f06ff63e924fa0ffdb591a89638e89 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, EditorialCollegeFellowshipFactory 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): + EditorialCollegeFellowshipFactory.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 0000000000000000000000000000000000000000..0f3ef75bf489d60a9f2d01268f96ee1be59d963a --- /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 0000000000000000000000000000000000000000..2e15e2bf9227f7829767346cb859b22f916819d2 --- /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 0000000000000000000000000000000000000000..6f24286a6a95caf53970bf1af944d02067f2a526 --- /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/migrations/0048_auto_20170320_1934.py b/scipost/migrations/0048_auto_20170320_1934.py new file mode 100644 index 0000000000000000000000000000000000000000..59d4ca6352d2cdd5d71dd37c3f90064da8755c15 --- /dev/null +++ b/scipost/migrations/0048_auto_20170320_1934.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-03-20 18:34 +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', '0047_auto_20170319_2110'), + ] + + operations = [ + migrations.CreateModel( + name='EditorialCollegeFellowship', + 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)), + ('start_date', models.DateField(blank=True, null=True)), + ('until_date', models.DateField(blank=True, null=True)), + ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fellowships', to='scipost.EditorialCollege')), + ('contributor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='scipost.Contributor')), + ], + ), + migrations.AlterUniqueTogether( + name='editorialcollegefellow', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='editorialcollegefellow', + name='college', + ), + migrations.RemoveField( + model_name='editorialcollegefellow', + name='contributor', + ), + migrations.DeleteModel( + name='EditorialCollegeFellow', + ), + migrations.AlterUniqueTogether( + name='editorialcollegefellowship', + unique_together=set([('contributor', 'college', 'start_date', 'until_date')]), + ), + ] diff --git a/scipost/models.py b/scipost/models.py index fcec39e5f0185cabdfaa401d04f9baca13809ead..2bb4c15cfb9b6ca2c262301852f55f68dbdad5c1 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -4,8 +4,10 @@ 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.encoding import force_text from django.utils.safestring import mark_safe from django_countries.fields import CountryField @@ -72,6 +74,12 @@ class TimeStampedModel(models.Model): abstract = True +def get_sentinel_user(): + '''Fallback user for models relying on Contributor that is being deleted.''' + 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 +106,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( @@ -190,11 +198,9 @@ class Contributor(models.Model): return mark_safe(output) def expertises_as_string(self): - output = '' if self.expertises: - for exp in self.expertises: - output += subject_areas_dict[exp] + ', ' - return output + return ', '.join([subject_areas_dict[exp].lower() for exp in self.expertises]) + return '' def assignments_summary_as_td(self): assignments = self.editorialassignment_set.all() @@ -517,6 +523,17 @@ class SPBMembershipAgreement(models.Model): # Static info models # ###################### +class FellowManager(models.Manager): + def active(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).order_by('contributor__user__last_name') + + class EditorialCollege(models.Model): '''A SciPost Editorial College for a specific discipline.''' discipline = models.CharField(max_length=255, unique=True) @@ -524,17 +541,36 @@ class EditorialCollege(models.Model): def __str__(self): return self.discipline + def active_fellowships(self): + return self.fellowships.current_fellowships() -class EditorialCollegeMember(models.Model): + +class EditorialCollegeFellowship(TimeStampedModel): """ - Editorial College Members for non-functional use! - This model is used for static information purposes only. + Editorial College Fellowship connecting Editorial College and Contributors, + maybe with a limiting start/until date. """ - 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') + contributor = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE, + related_name='+') + college = models.ForeignKey('scipost.EditorialCollege', on_delete=models.CASCADE, + related_name='fellowships') + 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 + return today <= self.until_date + elif not self.until_date: + return today >= self.start_date + return today >= self.start_date and today <= self.until_date diff --git a/scipost/static/scipost/about.js b/scipost/static/scipost/about.js new file mode 100644 index 0000000000000000000000000000000000000000..748aaa14a672c770beaba534a7f9d1306956bd8c --- /dev/null +++ b/scipost/static/scipost/about.js @@ -0,0 +1,51 @@ + +$(function() { + // Toggle Specialization codes block + $('[data-toggle="toggle-show"]').on('click', function(){ + var el = $($(this).attr('data-target')); + el.toggle(); + + // Reset active search after closing the box + if(!el.is(':visible')) { + $('.all-specializations .specialization') + .removeClass('active-search') + .trigger('search-specialization'); + } + }); + + // Hover/Click class to Contributors on hovering specializations + $('.all-specializations .specialization') + .on('mouseover', function() { + var code = $(this).attr('data-specialization'); + $('.single[data-specialization="'+code+'"]') + .parents('.contributor') + .addClass('hover-active'); + }) + .on('mouseleave', function() { + $('.contributor.hover-active').removeClass('hover-active'); + }) + .on('click', function() { + // Remove hover-class + $(this) + .toggleClass('active-search') + .trigger('search-specialization'); + }) + .on('search-specialization', function() { + // Reset: searching multiple specializations is not supported + $('.search-contributors.active-search').removeClass('active-search'); + $('.contributor-col.active').removeClass('active'); + $('.specialization.active-search').not(this).removeClass('active-search'); + + var el = $(this); + if( el.hasClass('active-search') ) { + // Add general 'click-active' class + $('.search-contributors').addClass('active-search'); + + // Add class to specialized Contributors + var code = el.attr('data-specialization'); + $('.single[data-specialization="' + code + '"]') + .parents('.contributor-col') + .addClass('active'); + } + }); +}); diff --git a/scipost/static/scipost/assets/config/preconfig.scss b/scipost/static/scipost/assets/config/preconfig.scss index 80c80697b39c5a9045ef5c5aeba84e3901c34df2..0ca0434f571a37fc3d31d816d25957730b6091f2 100644 --- a/scipost/static/scipost/assets/config/preconfig.scss +++ b/scipost/static/scipost/assets/config/preconfig.scss @@ -21,8 +21,6 @@ $container-max-widths: ( $card-spacer-x: 0.75rem; $card-border-radius: 0.15rem; $card-border-color: rgba(238, 238, 238, 0.5); -// $card-border-radius-inner: calc(#{$card-border-radius} - #{$card-border-width}); -$card-bg: #f4f4f4 !default; // Colors // @@ -58,11 +56,11 @@ $font-family-base: $font-family-sans-serif !default; $font-size-base: 0.8rem !default; $font-size-sm: 0.75rem; -$font-size-h1: 2.0rem; -$font-size-h2: 1.75rem; -$font-size-h3: 1.5rem; -$font-size-h4: 1.25rem; -$font-size-h5: 1.0rem; +$font-size-h1: 2.0em; +$font-size-h2: 1.5em; +$font-size-h3: 1.17em; +$font-size-h4: 0.8rem; +$font-size-h5: 0.8rem; $font-size-h6: 0.8rem; diff --git a/scipost/static/scipost/assets/css/_about.scss b/scipost/static/scipost/assets/css/_about.scss new file mode 100644 index 0000000000000000000000000000000000000000..c7c0b58d2f159bb6de4ea6e180f7aaf6a8598f86 --- /dev/null +++ b/scipost/static/scipost/assets/css/_about.scss @@ -0,0 +1,34 @@ +.search-contributors.active-search .contributor-col { + display: none; + + &.active { + display: block; + } +} + +.contributor { + margin: 0.25rem 0; + padding: 0.05rem 0.75rem; + border-radius: 0.15rem; + -webkit-transition: all 0.05s ease-in-out; + -o-transition: all 0.05s ease-in-out; + transition: all 0.05s ease-in-out; + + &.hover-active { + background-color: rgba(104, 132, 194, 0.3); + } +} + +.all-specializations { + border: 1px solid $scipost-darkblue; + border-radius: 0.15rem; + padding: 1rem; +} + +.specialization { + &.active-search, + &:hover { + cursor: pointer; + color: $scipost-lightblue; + } +} diff --git a/scipost/static/scipost/assets/css/_cards.scss b/scipost/static/scipost/assets/css/_cards.scss index 9feb44b8c155b366e21838a71bf0c757c3696a7a..2fe5f997e617af4967c022c0c15eb03bb448fd0a 100644 --- a/scipost/static/scipost/assets/css/_cards.scss +++ b/scipost/static/scipost/assets/css/_cards.scss @@ -1,7 +1,16 @@ .card { margin-bottom: 10px; + + &.card-grey { + background-color: #F4F4F4; + } } .list-group-item > .card-block { padding: 0.5rem; } + +.card-title, +.card-text { + margin-bottom: 0.25rem; +} diff --git a/scipost/static/scipost/assets/css/_grid.scss b/scipost/static/scipost/assets/css/_grid.scss index 9f8d9574581db91ece9e89b40e2d8252fe74c1b9..4ae928dd18265da1c82a7325a7131c59a3fb46eb 100644 --- a/scipost/static/scipost/assets/css/_grid.scss +++ b/scipost/static/scipost/assets/css/_grid.scss @@ -1,16 +1,19 @@ +.min-height-190 { + // Safari hack! + min-height: 190px; +} .panel { padding: 0.75rem; height: 100%; background-color: #f4f4f4; border-bottom: 10px solid #fff; - - &.page-header-panel { - // Safari hack! - min-height: 190px; - } } img { max-width: 100%; } + +.col-md-0 { + display: none; +} diff --git a/scipost/static/scipost/assets/css/_modal.scss b/scipost/static/scipost/assets/css/_modal.scss new file mode 100644 index 0000000000000000000000000000000000000000..f992b94c05ff09133c42327fc9bfd0f40cbc6141 --- /dev/null +++ b/scipost/static/scipost/assets/css/_modal.scss @@ -0,0 +1,6 @@ +.modal-content { + border-radius: 0.15rem; +} +.modal-backdrop { + opacity: 0.5 !important; +} diff --git a/scipost/static/scipost/assets/css/_submissions.scss b/scipost/static/scipost/assets/css/_submissions.scss index db0039ca92b28e01e449b07ebfd1158911441d89..a2c299931cd9ce9a2306612835ffb2cc03deee72 100644 --- a/scipost/static/scipost/assets/css/_submissions.scss +++ b/scipost/static/scipost/assets/css/_submissions.scss @@ -21,3 +21,10 @@ table.submission_header { .reportRatings { margin: 0 0 1rem; } + +.required-actions { + padding: 0.5rem; + background-color: $brand-danger; + color: $white; + border-radius: $card-border-radius; +} diff --git a/scipost/static/scipost/assets/css/_tooltip.scss b/scipost/static/scipost/assets/css/_tooltip.scss new file mode 100644 index 0000000000000000000000000000000000000000..dd8445262fc4f7e9b5d1ce039a73a942668e3561 --- /dev/null +++ b/scipost/static/scipost/assets/css/_tooltip.scss @@ -0,0 +1,11 @@ +[data-toggle="tooltip"] { + cursor: pointer; + + &:hover { + color: $scipost-lightblue; + } +} + +.tooltip-inner { + border-radius: 0.15rem; +} diff --git a/scipost/static/scipost/assets/css/_type.scss b/scipost/static/scipost/assets/css/_type.scss index efaabe4337bfd538db0b80e61b4155ab7f818948..f352738152fabc94e77a394155b560e9946840c1 100644 --- a/scipost/static/scipost/assets/css/_type.scss +++ b/scipost/static/scipost/assets/css/_type.scss @@ -45,3 +45,7 @@ hr.hr12 { height: 1px; } } + +.text-blue { + color: $scipost-lightblue; +} diff --git a/scipost/static/scipost/assets/css/style.scss b/scipost/static/scipost/assets/css/style.scss index 628de45f6b53808734312db002be5264de3bde5a..50ebdb189857fbc100f2cd5e0b7f5098941bafa0 100644 --- a/scipost/static/scipost/assets/css/style.scss +++ b/scipost/static/scipost/assets/css/style.scss @@ -24,14 +24,17 @@ @import "grid"; @import "labels"; @import "messages"; +// @import "modal"; @import "navbar"; @import "page_header"; +@import "tooltip"; @import "type"; /** * SciPost Specific * */ +@import "about"; @import "comments"; @import "journals"; @import "personal_page"; diff --git a/scipost/static/scipost/assets/js/modal.js b/scipost/static/scipost/assets/js/modal.js new file mode 100644 index 0000000000000000000000000000000000000000..cdf325ec39455b2bffa1cf60ff4c30a5759ef91b --- /dev/null +++ b/scipost/static/scipost/assets/js/modal.js @@ -0,0 +1,6 @@ +require('bootstrap-loader'); + +jQuery('[data-toggle="modal"]').on('click', function(){ + var target = $(this).attr('data-target'); + $(target).modal('show'); +}); diff --git a/scipost/static/scipost/assets/js/tooltip.js b/scipost/static/scipost/assets/js/tooltip.js new file mode 100644 index 0000000000000000000000000000000000000000..eaba2651c063e2e54a6be9787186e46b0d61c249 --- /dev/null +++ b/scipost/static/scipost/assets/js/tooltip.js @@ -0,0 +1,5 @@ +require('bootstrap-loader'); + +jQuery('[data-toggle="tooltip"]').tooltip({ + animation: false, +}); diff --git a/scipost/templates/scipost/_college_member.html b/scipost/templates/scipost/_college_member.html deleted file mode 100644 index a6114acacd1314b63b3ceba601656172e7c686c8..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..412c56712c22cb699f7b4093889d015ec46bf7e8 --- /dev/null +++ b/scipost/templates/scipost/_contributor_short.html @@ -0,0 +1,22 @@ +{% load scipost_extras %} + +{% load static %} + +<h3 class="mb-0 py-0 d-block"> + {% 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 %} +</h3> + +{% if contributor.affiliation %} + <span class="text-muted">({{contributor.affiliation}})</span> +{% endif %} +<div class="d-block"> + {% for expertise in contributor.expertises %} + <div class="single d-inline" data-specialization="{{expertise|lower}}" data-toggle="tooltip" data-placement="bottom" title="{{expertise|get_specialization_display}}">{{expertise|get_specialization_code}}</div> + {% endfor %} +</div> diff --git a/scipost/templates/scipost/about.html b/scipost/templates/scipost/about.html index 725dc4092f20d677324012a386babf5ea57f6dca..0728f95d21542d31d73333adfaeca93c40f77f22 100644 --- a/scipost/templates/scipost/about.html +++ b/scipost/templates/scipost/about.html @@ -1,9 +1,11 @@ {% extends 'scipost/base.html' %} - +{% load render_bundle from webpack_loader %} {% block pagetitle %}: About{% endblock pagetitle %} {% load staticfiles %} +{% load scipost_extras %} + {% block content %} @@ -142,25 +144,40 @@ </div> </div> - <hr> -{% for college in object_list %} -<div class="row"> - <div class="col-12"> - <div class="panel"> - <h2 id="editorial_college_{{ college|lower }}">Editorial College ({{ college }})</h2> +{% for college, codes in object_list %} + + <div class="row"> + <div class="col-12"> + <div class="panel"> + <h2 id="editorial_college_{{ college|lower }}">Editorial College ({{ college }})</h2> + </div> </div> </div> -</div> -<div class="row"> - {% for member in college.member.all %} - <div class="col-md-4 py-1"> - {% include 'scipost/_college_member.html' with member=member %} + {% if codes %} + <div class="row"> + <div class="col-12"> + <a href="javascript:;" class="d-block mb-1" data-toggle="toggle-show" data-target="#specializations-{{college|lower}}">Show all specialization codes</a> + <div id="specializations-{{college|lower}}" class="all-specializations" style="border: 1px solid; display: none;"> + {% for code in codes %} + <div class="specialization" data-specialization="{{code.0|lower}}">{{code.0}} - {{code.1}}</div> + {% endfor %} + </div> </div> - {% endfor %} -</div> + </div> + {% endif %} + + <div class="row search-contributors" data-contributors="{{ college|lower }}"> + {% for fellowship in college.current_fellows|reorder_list_three %} + <div class="col-md-4 contributor-col"> + <div class="contributor"> + {% include 'scipost/_contributor_short.html' with contributor=fellowship.contributor %} + </div> + </div> + {% endfor %} + </div> {% endfor %} @@ -173,3 +190,9 @@ </div> {% endblock content %} + +{% block footer_script %} +{{block.super}} + {% render_bundle 'tooltip' 'js' %} + <script src="{% static 'scipost/about.js' %}"></script> +{% endblock %} diff --git a/scipost/templates/scipost/index.html b/scipost/templates/scipost/index.html index ff4f0468b5c88f1191e11f5bc3aae86d45a25081..2b03326f8a296976e7827f58156adc9d4c42cc8a 100644 --- a/scipost/templates/scipost/index.html +++ b/scipost/templates/scipost/index.html @@ -8,7 +8,7 @@ <div class="row"> {% if latest_newsitems %} <div class="col-md-6 {% if user.is_authenticated %}col-lg-4{% else %}col-lg-3{% endif %}"> - <div class="card"> + <div class="card card-grey"> <div class="card-block"> <h1 class="card-title mb-0"><a href="{% url 'news:news' %}">News</a><a style="float: right;" href="{% url 'scipost:feeds' %}"><img src="{% static 'scipost/images/feed-icon-14x14.png' %}" alt="Feed logo" width="14"></a></h1> <h4 class="card-subtitle mb-0 pb-0 text-muted">Latest news and announcements.</h4> @@ -27,7 +27,7 @@ {% endif %} <div class="col-md-6 {% if user.is_authenticated %}col-lg-4{% else %}col-lg-3{% endif %}"> - <div class="card"> + <div class="card card-grey"> <div class="card-block"> <h1 class="card-title mb-0"><a href="{% url 'scipost:about' %}">About SciPost</a></h1> <h4 class="card-subtitle mb-2 text-muted">SciPost is a complete scientific publication portal managed by active professional scientists.</h4> @@ -48,7 +48,7 @@ {% if not user.is_authenticated %} <div class="col-md-6 col-lg-3"> - <div class="card"> + <div class="card card-grey"> <div class="card-block"> <h1 class="card-title mb-0"><a href="{% url 'scipost:register' %}">Register</a></h1> <p>Professional scientists (PhD students and above) can become Contributors to SciPost by filling the @@ -67,7 +67,7 @@ {% endif %} <div class="col-md-6 {% if user.is_authenticated %}col-lg-4{% else %}col-lg-3{% endif %}"> - <div class="card"> + <div class="card card-grey"> <div class="card-block"> <h1 class="card-title mb-0"><a href="{% url 'journals:journals' %}">Journals</a></h1> <h4 class="card-subtitle mb-2 text-muted">SciPost publishes a portfolio of high-quality two-way open access scientific journals.</h4> @@ -77,7 +77,7 @@ </h2> </div> {% if issue and publications %} - <h4 class="card-text text-center">A selection from the current issue:</h4> + <h4 class="card-text text-center">A selection from the {% if issue.is_current %}current{% else %}last{% endif %} issue:</h4> {% if issue %} <div class="text-muted text-center">Vol. {{ issue.in_volume.number }} issue {{ issue.number }} {{issue.period_as_string}}</div> {% endif %} @@ -100,7 +100,7 @@ <div class="row"> <div class="col-md-6 {% if user.is_authenticated %}col-lg-4{% else %}col-lg-3{% endif %}"> - <div class="card"> + <div class="card card-grey"> <div class="card-block"> <h1 class="card-title"><a href="{% url 'journals:journals' %}">Journals</a></h1> <p>All SciPost Journals implement the stringent <a href="/FAQ#pwr">peer-witnessed refereeing</a> principle.</p> @@ -115,7 +115,7 @@ </div> </div> <div class="col-md-6 {% if user.is_authenticated %}col-lg-4{% else %}col-lg-3{% endif %}"> - <div class="card"> + <div class="card card-grey"> <div class="card-block"> <h1 class="card-title"><a href="{% url 'commentaries:commentaries' %}">Commentaries</a></h1> <p>SciPost Commentaries allow Contributors to comment and build on all existing literature.</p> @@ -126,7 +126,7 @@ </div> </div> <div class="col-md-6 {% if user.is_authenticated %}col-lg-4{% else %}col-lg-3{% endif %}"> - <div class="card"> + <div class="card card-grey"> <div class="card-block"> <h1 class="card-title"><a href="{% url 'theses:theses' %}">Theses</a></h1> <p>SciPost Theses allow Contributors to find Master's, Ph.D. and Habilitation theses relevant to their work.</p> diff --git a/scipost/templatetags/scipost_extras.py b/scipost/templatetags/scipost_extras.py index 40863a5f2d987024c06caeb3c480f2477293a174..2a7074041510dbb5b0c63c4fe0a4d81804e14f7e 100644 --- a/scipost/templatetags/scipost_extras.py +++ b/scipost/templatetags/scipost_extras.py @@ -1,7 +1,8 @@ from django import template from django.contrib.auth.models import Group -from scipost.models import Contributor +from ..constants import subject_areas_dict +from ..models import Contributor register = template.Library() @@ -28,3 +29,20 @@ def is_in_group(user, group_name): def associated_contributors(draft): return Contributor.objects.filter( user__last_name__icontains=draft.last_name) + + +@register.filter(name='reorder_list_three') +def reorder_list_three(ul): + return ul[::3] + ul[1::3] + ul[2::3] + + +@register.filter(name='get_specialization_code') +def get_specialization_code(code): + # Get the specialization code without the main subject identifier + return code.split(':')[1] + + +@register.filter(name='get_specialization_display') +def get_specialization_display(code): + # Due to the ArrayField construction, one is not able to use get_FOO_display in the template + return subject_areas_dict[code] diff --git a/scipost/test_views.py b/scipost/test_views.py index d6b766630eb0b24ef0fab244bd89681ac24cb90a..5b764927f0baa855389a30b22dafd751cfaa6a2a 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 aad9aaf685596cf7302d53f03b1c29ac65ed7afc..16fe27bf23c0e91f4685aac54438831a17cf3e39 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -22,13 +22,15 @@ from django.template import Context, Template from django.utils.http import is_safe_url from django.views.generic.list import ListView +from django.db.models import Prefetch + from guardian.decorators import permission_required -from .constants import SCIPOST_SUBJECT_AREAS +from .constants import SCIPOST_SUBJECT_AREAS, subject_areas_raw_dict from .models import Contributor, CitationNotification, UnavailabilityPeriod,\ DraftInvitation, RegistrationInvitation,\ title_dict, SciPost_from_addresses_dict,\ - AuthorshipClaim, SupportingPartner, SPBMembershipAgreement, EditorialCollege + AuthorshipClaim, SupportingPartner, SPBMembershipAgreement, EditorialCollege, EditorialCollegeFellowship from .forms import AuthenticationForm, DraftInvitationForm, UnavailabilityPeriodForm,\ RegistrationForm, RegistrationInvitationForm, AuthorshipClaimForm,\ ModifyPersonalMessageForm, SearchForm, VetRegistrationForm, reg_ref_dict,\ @@ -38,18 +40,13 @@ from .forms import AuthenticationForm, DraftInvitationForm, UnavailabilityPeriod from .utils import Utils, EMAIL_FOOTER, SCIPOST_SUMMARY_FOOTER, SCIPOST_SUMMARY_FOOTER_HTML from commentaries.models import Commentary -from commentaries.forms import CommentarySearchForm from comments.models import Comment from journals.models import Publication, Issue from news.models import NewsItem from submissions.models import SUBMISSION_STATUS_PUBLICLY_UNLISTED from submissions.models import Submission, EditorialAssignment from submissions.models import RefereeInvitation, Report, EICRecommendation -from submissions.forms import SubmissionSearchForm from theses.models import ThesisLink -from theses.forms import ThesisLinkSearchForm -# from virtualmeetings.models import VGM, Feedback, Nomination, Motion -# from virtualmeetings.constants import motion_categories_dict ############## @@ -207,7 +204,7 @@ def index(request): """ Main page """ context = {} context['latest_newsitems'] = NewsItem.objects.all().order_by('-date')[:2] - context['issue'] = Issue.objects.get_current_issue(in_volume__in_journal__name='SciPost Physics') + context['issue'] = Issue.objects.get_last_filled_issue(in_volume__in_journal__name='SciPost Physics') if context['issue']: context['publications'] = context['issue'].publication_set.filter(doi_string__isnull=False ).order_by('-publication_date')[:4] @@ -1464,241 +1461,6 @@ def Fellow_activity_overview(request, Fellow_id=None): context['assignments_of_Fellow'] = assignments_of_Fellow return render(request, 'scipost/Fellow_activity_overview.html', context) -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', return_403=True) -# def VGMs(request): -# VGM_list = VGM.objects.all().order_by('start_date') -# context = {'VGM_list': VGM_list} -# return render(request, 'scipost/VGMs.html', context) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', return_403=True) -# def VGM_detail(request, VGM_id): -# VGM_instance = get_object_or_404(VGM, id=VGM_id) -# VGM_information = Template(VGM_instance.information).render(Context({})) -# feedback_received = Feedback.objects.filter(VGM=VGM_instance).order_by('date') -# feedback_form = FeedbackForm() -# current_Fellows = Contributor.objects.filter( -# user__groups__name='Editorial College').order_by('user__last_name') -# sent_inv_Fellows = RegistrationInvitation.objects.filter( -# invitation_type='F', responded=False) -# pending_inv_Fellows = sent_inv_Fellows.filter(declined=False).order_by('last_name') -# declined_inv_Fellows = sent_inv_Fellows.filter(declined=True).order_by('last_name') -# nomination_form = NominationForm() -# nominations = Nomination.objects.filter(accepted=None).order_by('last_name') -# motion_form = MotionForm() -# remark_form = RemarkForm() -# context = { -# 'VGM': VGM_instance, -# 'VGM_information': VGM_information, -# 'feedback_received': feedback_received, -# 'feedback_form': feedback_form, -# 'current_Fellows': current_Fellows, -# 'pending_inv_Fellows': pending_inv_Fellows, -# 'declined_inv_Fellows': declined_inv_Fellows, -# 'nominations': nominations, -# 'nomination_form': nomination_form, -# 'motion_categories_dict': motion_categories_dict, -# 'motion_form': motion_form, -# 'remark_form': remark_form, -# } -# return render(request, 'scipost/VGM_detail.html', context) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', return_403=True) -# def feedback(request, VGM_id=None): -# if request.method == 'POST': -# feedback_form = FeedbackForm(request.POST) -# if feedback_form.is_valid(): -# feedback = Feedback(by=request.user.contributor, -# date=timezone.now().date(), -# feedback=feedback_form.cleaned_data['feedback'],) -# if VGM_id: -# VGM_instance = get_object_or_404(VGM, id=VGM_id) -# feedback.VGM = VGM_instance -# feedback.save() -# ack_message = 'Your feedback has been received.' -# context = {'ack_message': ack_message} -# if VGM_id: -# context['followup_message'] = 'Return to the ' -# context['followup_link'] = reverse('scipost:VGM_detail', -# kwargs={'VGM_id': VGM_id}) -# context['followup_link_label'] = 'VGM page' -# return render(request, 'scipost/acknowledgement.html', context) -# else: -# errormessage = 'The form was not filled properly.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# else: -# errormessage = 'This view can only be posted to.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', raise_exception=True) -# def add_remark_on_feedback(request, VGM_id, feedback_id): -# # contributor = request.user.contributor -# feedback = get_object_or_404(Feedback, pk=feedback_id) -# if request.method == 'POST': -# remark_form = RemarkForm(request.POST) -# if remark_form.is_valid(): -# remark = Remark(contributor=request.user.contributor, -# feedback=feedback, -# date=timezone.now(), -# remark=remark_form.cleaned_data['remark']) -# remark.save() -# return HttpResponseRedirect('/VGM/' + str(VGM_id) + -# '/#feedback_id' + str(feedback.id)) -# else: -# errormessage = 'The form was invalidly filled.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# else: -# errormessage = 'This view can only be posted to.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', return_403=True) -# def nominate_Fellow(request, VGM_id): -# VGM_instance = get_object_or_404(VGM, id=VGM_id) -# if request.method == 'POST': -# nomination_form = NominationForm(request.POST) -# if nomination_form.is_valid(): -# nomination = Nomination( -# VGM=VGM_instance, -# by=request.user.contributor, -# date=timezone.now().date(), -# first_name=nomination_form.cleaned_data['first_name'], -# last_name=nomination_form.cleaned_data['last_name'], -# discipline=nomination_form.cleaned_data['discipline'], -# expertises=nomination_form.cleaned_data['expertises'], -# webpage=nomination_form.cleaned_data['webpage'], -# voting_deadline=VGM_instance.end_date + datetime.timedelta(days=7), -# ) -# nomination.save() -# nomination.update_votes(request.user.contributor.id, 'A') -# ack_message = 'The nomination has been registered.' -# context = {'ack_message': ack_message, -# 'followup_message': 'Return to the ', -# 'followup_link': reverse('scipost:VGM_detail', kwargs={'VGM_id': VGM_id}), -# 'followup_link_label': 'VGM page'} -# return render(request, 'scipost/acknowledgement.html', context) -# else: -# errormessage = 'The form was not filled properly.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# else: -# errormessage = 'This view can only be posted to.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', raise_exception=True) -# def add_remark_on_nomination(request, VGM_id, nomination_id): -# # contributor = request.user.contributor -# nomination = get_object_or_404(Nomination, pk=nomination_id) -# if request.method == 'POST': -# remark_form = RemarkForm(request.POST) -# if remark_form.is_valid(): -# remark = Remark(contributor=request.user.contributor, -# nomination=nomination, -# date=timezone.now(), -# remark=remark_form.cleaned_data['remark']) -# remark.save() -# return HttpResponseRedirect('/VGM/' + str(VGM_id) + -# '/#nomination_id' + str(nomination.id)) -# else: -# errormessage = 'The form was invalidly filled.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# else: -# errormessage = 'This view can only be posted to.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', raise_exception=True) -# def vote_on_nomination(request, nomination_id, vote): -# contributor = request.user.contributor -# nomination = get_object_or_404(Nomination, pk=nomination_id) -# if timezone.now() > nomination.voting_deadline: -# errormessage = 'The voting deadline on this nomination has passed.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# nomination.update_votes(contributor.id, vote) -# return HttpResponseRedirect('/VGM/' + str(nomination.VGM.id) + -# '/#nomination_id' + str(nomination.id)) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', return_403=True) -# def put_motion_forward(request, VGM_id): -# VGM_instance = get_object_or_404(VGM, id=VGM_id) -# if timezone.now().date() > VGM_instance.end_date: -# errormessage = 'This VGM has ended. No new motions can be put forward.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# if request.method == 'POST': -# motion_form = MotionForm(request.POST) -# if motion_form.is_valid(): -# motion = Motion( -# category=motion_form.cleaned_data['category'], -# VGM=VGM_instance, -# background=motion_form.cleaned_data['background'], -# motion=motion_form.cleaned_data['motion'], -# put_forward_by=request.user.contributor, -# date=timezone.now().date(), -# voting_deadline=VGM_instance.end_date + datetime.timedelta(days=7), -# ) -# motion.save() -# motion.update_votes(request.user.contributor.id, 'A') -# ack_message = 'Your motion has been registered.' -# context = {'ack_message': ack_message, -# 'followup_message': 'Return to the ', -# 'followup_link': reverse('scipost:VGM_detail', kwargs={'VGM_id': VGM_id}), -# 'followup_link_label': 'VGM page'} -# return render(request, 'scipost/acknowledgement.html', context) -# else: -# errormessage = 'The form was not filled properly.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# else: -# errormessage = 'This view can only be posted to.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', raise_exception=True) -# def add_remark_on_motion(request, motion_id): -# # contributor = request.user.contributor -# motion = get_object_or_404(Motion, pk=motion_id) -# if request.method == 'POST': -# remark_form = RemarkForm(request.POST) -# if remark_form.is_valid(): -# remark = Remark(contributor=request.user.contributor, -# motion=motion, -# date=timezone.now(), -# remark=remark_form.cleaned_data['remark']) -# remark.save() -# return HttpResponseRedirect('/VGM/' + str(motion.VGM.id) + -# '/#motion_id' + str(motion.id)) -# else: -# errormessage = 'The form was invalidly filled.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# else: -# errormessage = 'This view can only be posted to.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# -# -# @login_required -# @permission_required('scipost.can_attend_VGMs', raise_exception=True) -# def vote_on_motion(request, motion_id, vote): -# contributor = request.user.contributor -# motion = get_object_or_404(Motion, pk=motion_id) -# if timezone.now() > motion.voting_deadline: -# errormessage = 'The voting deadline on this motion has passed.' -# return render(request, 'scipost/error.html', {'errormessage': errormessage}) -# motion.update_votes(contributor.id, vote) -# return HttpResponseRedirect('/VGM/' + str(motion.VGM.id) + -# '/#motion_id' + str(motion.id)) -# ############################# # Supporting Partners Board # @@ -1756,4 +1518,23 @@ 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( + Prefetch('fellowships', + queryset=EditorialCollegeFellowship.objects.active().select_related( + 'contributor__user'), + to_attr='current_fellows')) + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + object_list = [] + for college in context['object_list']: + try: + spec_list = subject_areas_raw_dict[str(college)] + except KeyError: + spec_list = None + object_list.append(( + college, + spec_list, + )) + context['object_list'] = object_list + return context diff --git a/submissions/models.py b/submissions/models.py index 7aea9adab2567057eb7c7753c025fa62f2e26c4b..f720ae1baa5310d8db70a8c3716bfd2768d3111f 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -2,6 +2,7 @@ import datetime from django.utils import timezone from django.db import models, transaction +from django.db.models import Q from django.contrib.postgres.fields import JSONField from django.template import Template, Context @@ -77,19 +78,6 @@ SUBMISSION_STATUS_VOTING_DEPRECATED = [ 'withdrawn', ] - -# SUBMISSION_ACTION_REQUIRED = ( -# ('assign_EIC', 'Editor-in-charge to be assigned'), -# ('Fellow_accepts_or_refuse_assignment', 'Fellow must accept or refuse assignment'), -# ('EIC_runs_refereeing_round', 'Editor-in-charge to run refereeing round (inviting referees)'), -# ('EIC_closes_refereeing_round', 'Editor-in-charge to close refereeing round'), -# ('EIC_invites_author_response', 'Editor-in-charge invites authors to complete their replies'), -# ('EIC_formulates_editorial_recommendation', -# 'Editor-in-charge to formulate editorial recommendation'), -# ('EC_ratification', 'Editorial College ratifies editorial recommendation'), -# ('Decision_to_authors', 'Editor-in-charge forwards decision to authors'), -# ) - SUBMISSION_TYPE = ( ('Letter', 'Letter (broad-interest breakthrough results)'), ('Article', 'Article (in-depth reports on specialized research)'), @@ -98,6 +86,16 @@ SUBMISSION_TYPE = ( submission_type_dict = dict(SUBMISSION_TYPE) +class SubmissionManager(models.Manager): + def get_pool(self, user): + return self.exclude(status__in=SUBMISSION_STATUS_OUT_OF_POOL)\ + .exclude(is_current=False)\ + .exclude(authors=user.contributor)\ + .exclude(Q(author_list__icontains=user.last_name), + ~Q(authors_false_claims=user.contributor))\ + .order_by('-submission_date') + + class Submission(ArxivCallable, models.Model): # Main submission fields author_comments = models.TextField(blank=True, null=True) @@ -147,6 +145,8 @@ class Submission(ArxivCallable, models.Model): submission_date = models.DateField(verbose_name='submission date', default=timezone.now) latest_activity = models.DateTimeField(default=timezone.now) + objects = SubmissionManager() + class Meta: permissions = ( ('can_take_editorial_actions', 'Can take editorial actions'), @@ -321,6 +321,30 @@ class Submission(ArxivCallable, models.Model): template = Template(header) return template.render(context) + def count_accepted_invitations(self): + return self.refereeinvitation_set.filter(accepted=True).count() + + def count_declined_invitations(self): + return self.refereeinvitation_set.filter(accepted=False).count() + + def count_pending_invitations(self): + return self.refereeinvitation_set.filter(accepted=None).count() + + def count_invited_reports(self): + return self.reports.filter(status=1, invited=True).count() + + def count_contrib_reports(self): + return self.reports.filter(status=1, invited=False).count() + + def count_obtained_reports(self): + return self.reports.filter(status=1, invited__isnull=False).count() + + def count_refused_resports(self): + return self.reports.filter(status__lte=-1).count() + + def count_awaiting_vetting(self): + return self.reports.filter(status=0).count() + def refereeing_status_as_p(self): nr_ref_invited = RefereeInvitation.objects.filter(submission=self).count() nr_ref_accepted = RefereeInvitation.objects.filter(submission=self, accepted=True).count() @@ -802,6 +826,13 @@ class EditorialCommunication(models.Model): # Editorial Recommendation # ############################ +class EICRecommendationManager(models.Manager): + def get_for_user_in_pool(self, user): + return self.exclude(submission__authors=user.contributor)\ + .exclude(Q(submission__author_list__icontains=user.last_name), + ~Q(submission__authors_false_claims=user.contributor)) + + # From the Editor-in-charge of a Submission class EICRecommendation(models.Model): submission = models.ForeignKey(Submission, on_delete=models.CASCADE) @@ -820,6 +851,8 @@ class EICRecommendation(models.Model): voted_abstain = models.ManyToManyField(Contributor, blank=True, related_name='voted_abstain') voting_deadline = models.DateTimeField('date submitted', default=timezone.now) + objects = EICRecommendationManager() + def __str__(self): return (self.submission.title[:20] + ' by ' + self.submission.author_list[:30] + ', ' + report_rec_dict[self.recommendation]) diff --git a/submissions/templates/submissions/_recommendation_fellow_content.html b/submissions/templates/submissions/_recommendation_fellow_content.html new file mode 100644 index 0000000000000000000000000000000000000000..3ee1572de1ee79da41f0751b8a84edf9bf8e7e61 --- /dev/null +++ b/submissions/templates/submissions/_recommendation_fellow_content.html @@ -0,0 +1,15 @@ +<div class="card-block"> + <h3 class="card-title text-blue">By {{recommendation.submission.editor_in_charge.get_title_display}} {{recommendation.submission.editor_in_charge.user.first_name}} {{recommendation.submission.editor_in_charge.user.last_name}}, formulated on {{recommendation.date_submitted}}</h3> + + <h3 class="pb-0">Remarks for authors</h3> + <p class="pl-md-3">{{recommendation.remarks_for_authors}}</p> + + <h3 class="pb-0">Requested changes</h3> + <p class="pl-md-3">{{recommendation.requested_changes}}</p> + + <h3 class="pb-0">Remarks for Editorial College</h3> + <p class="pl-md-3">{{recommendation.remarks_for_editorial_college}}</p> + + <h3 class="pb-0">Recommendation</h3> + <p class="pl-md-3 mb-0">{{recommendation.get_recommendation_display}}</p> +</div> diff --git a/submissions/templates/submissions/_submission_card_content.html b/submissions/templates/submissions/_submission_card_content.html new file mode 100644 index 0000000000000000000000000000000000000000..324763720467f6f4fdfb49f023246eb5f6ee4679 --- /dev/null +++ b/submissions/templates/submissions/_submission_card_content.html @@ -0,0 +1,8 @@ +<div class="card-block"> + <h3 class="card-title"> + <a href="{% url 'submissions:submission' submission.arxiv_identifier_w_vn_nr %}">{{submission.title}}</a> + </h3> + <p class="card-text">by {{submission.author_list}}</p> + <p class="card-text text-muted">Version {{submission.arxiv_vn_nr}} ({% if submission.is_current %}current version{% else %}deprecated version {{submission.arxiv_vn_nr}}{% endif %})</p> + <p class="card-text text-muted">Submitted {{submission.submission_date}} to {{submission.get_submitted_to_journal_display}} - latest activity: {{submission.latest_activity}}</p> +</div> diff --git a/submissions/templates/submissions/_submission_card_fellow_content.html b/submissions/templates/submissions/_submission_card_fellow_content.html new file mode 100644 index 0000000000000000000000000000000000000000..e75e6caf6aafae299b180123a9fb87817d074084 --- /dev/null +++ b/submissions/templates/submissions/_submission_card_fellow_content.html @@ -0,0 +1,19 @@ +<div class="card-block"> + <h3 class="card-title"> + <a href="{% url 'submissions:submission' submission.arxiv_identifier_w_vn_nr %}">{{submission.title}}</a> + </h3> + <p class="card-text">by {{submission.author_list}}</p> + <p class="card-text text-muted">Version {{submission.arxiv_vn_nr}} ({% if submission.is_current %}current version{% else %}deprecated version {{submission.arxiv_vn_nr}}{% endif %})</p> + <p class="card-text text-muted">Submitted {{submission.submission_date}} to {{submission.get_submitted_to_journal_display}} - latest activity: {{submission.latest_activity}}</p> + + <!-- Fellow specific info --> + {% if submission.status == 'unassigned' %} + <p class="card-text text-danger">Status: {{ submission.get_status_display }}. You can volunteer to become Editor-in-charge by <a href="{% url 'submissions:volunteer_as_EIC' submission.arxiv_identifier_w_vn_nr %}">clicking here</a>.</p> + {% else %} + <p class="card-text">Editor-in-charge: <em>{{ submission.editor_in_charge }}</em></p> + <p class="card-text">Status: {{ submission.get_status_display }}</p> + {% endif %} + + {% include 'submissions/_submission_refereeing_status.html' with submission=submission %} + {# {{submission.refereeing_status_as_p}}#} +</div> diff --git a/submissions/templates/submissions/_submission_refereeing_status.html b/submissions/templates/submissions/_submission_refereeing_status.html new file mode 100644 index 0000000000000000000000000000000000000000..68736208cf500daa45206848b5e6ae54ae8019e9 --- /dev/null +++ b/submissions/templates/submissions/_submission_refereeing_status.html @@ -0,0 +1,4 @@ +<div class="card-block"> + <p class="card-text">Nr referees invited: {{submission.refereeinvitation_set.count}} <span>[{{submission.count_accepted_invitations}} acccepted / {{submission.count_declined_invitations}} declined / {{submission.count_pending_invitations}} response pending]</span></p> + <p class="card-text">Nr reports obtained: {{submission.count_obtained_reports}} [{{submission.count_invited_reports}} invited / {{submission.count_contrib_reports}} contributed], nr refused: {{submission.count_refused_resports}}, nr awaiting vetting: {{submission.count_awaiting_vetting}}</p> +</div> diff --git a/submissions/templates/submissions/communication.html b/submissions/templates/submissions/communication.html index 60ef4953b439375f877fa8503436fbfbf02e028b..55543c0c7385735c488eec54a203abbd1b28e34f 100644 --- a/submissions/templates/submissions/communication.html +++ b/submissions/templates/submissions/communication.html @@ -2,48 +2,56 @@ {% block pagetitle %}: communication{% endblock pagetitle %} -{% block headsup %} - {% load scipost_extras %} -{% endblock headsup %} - -{% block bodysup %} - -<section> - {% if errormessage %} - <p>{{ errormessage }}</p> - {% else %} - <div class="flex-greybox"> - <h1>Send a Communication</h1> - </div> - {% if comtype == 'EtoA' %} - <p>to the submitting Author of Submission</p> - {% elif comtype == 'AtoE' or comtype == 'RtoE' or comtype == 'StoE' %} - <p>to the Editor-in-charge of Submission</p> - {% elif comtype == 'EtoR' %} - <p>to Referee of Submission</p> - {% elif comtype == 'EtoS' %} - <p>to SciPost Editorial Administrators</p> - {% endif %} - <ul>{{ submission.header_as_li }}</ul> - - <br/> - {% if referee_id %} - <form action="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype=comtype referee_id=referee_id %}" method="post"> - {% csrf_token %} - {{ form }} - <input type="submit" value="Send communication"/> - </form> - {% else %} - <form action="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype=comtype %}" method="post"> - {% csrf_token %} - {{ form }} - <input type="submit" value="Send communication"/> - </form> - {% endif %} - - {% endif %} -</section> - -{% endblock bodysup %} +{% load bootstrap %} + +{% block content %} + +{% if errormessage %} +<div class="row"> + <div class="col-12"> + <p>{{ errormessage }}</p> + </div> +</div> +{% else %} +<div class="row"> + <div class="col-12"> + <h1 class="highlight">Send a Communication</h1> + {% if comtype == 'EtoA' %} + <p>to the submitting Author of Submission</p> + {% elif comtype == 'AtoE' or comtype == 'RtoE' or comtype == 'StoE' %} + <h3>to the Editor-in-charge of Submission</h3> + {% elif comtype == 'EtoR' %} + <p>to Referee of Submission</p> + {% elif comtype == 'EtoS' %} + <p>to SciPost Editorial Administrators</p> + {% endif %} + + <div class="card"> + {% include 'submissions/_submission_card_content.html' with submission=submission %} + </div> + </div> +</div> + +<div class="row"> + <div class="col-12"> + {% if referee_id %} + <form action="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype=comtype referee_id=referee_id %}" method="post"> + {% csrf_token %} + {{ form|bootstrap:'0,12' }} + <input class="btn btn-secondary" type="submit" value="Send communication"/> + </form> + {% else %} + <form action="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype=comtype %}" method="post"> + {% csrf_token %} + {{ form|bootstrap:'0,12' }} + <input class="btn btn-secondary" type="submit" value="Send communication"/> + </form> + {% endif %} + + {% endif %} + </div> +</div> + +{% endblock content %} diff --git a/submissions/templates/submissions/pool.html b/submissions/templates/submissions/pool.html index 6f7db2515e4d59490572adf53ae5429908a88898..b0a5e39d7226ed24706fb783a3480814a559bc27 100644 --- a/submissions/templates/submissions/pool.html +++ b/submissions/templates/submissions/pool.html @@ -2,7 +2,12 @@ {% block pagetitle %}: Submissions Pool{% endblock pagetitle %} -{% block bodysup %} +{% load bootstrap %} +{% load guardian_tags %} +{% load scipost_extras %} +{% load submissions_extras %} + +{% block content %} <script> $(document).ready(function(){ @@ -24,286 +29,326 @@ $(document).ready(function(){ $(this).next("div").toggle(); }); }); - </script> -{% load guardian_tags %} -{% load scipost_extras %} -{% load submissions_extras %} - - {% if request.user|is_in_group:'Editorial Administrators' and recommendations_undergoing_voting %} -<section> - <div class="flex-container"> - <div class="flex-whitebox"> - <h3>Administrative actions on recommendations undergoing voting:</h3> - <ul> - <li>To send an email reminder to each Fellow with at least one voting duty, <a href="{% url 'submissions:remind_Fellows_to_vote' %}">click here</a></li> - </ul> + <div class="row"> + <div class="col-12"> + <h3 class="highlight">Administrative actions on recommendations undergoing voting:</h3> + <ul> + <li>To send an email reminder to each Fellow with at least one voting duty, <a href="{% url 'submissions:remind_Fellows_to_vote' %}">click here</a></li> + </ul> + </div> </div> - </div> - <div class="flex-container"> - <div class="flex-greybox"> - <h1>Recommendations undergoing voting</h1> + + <div class="row"> + <div class="col-12"> + <h1 class="highlight d-block">Recommendations undergoing voting</h1> + </div> </div> - </div> - <ul> + {% for rec in recommendations_undergoing_voting %} - {% if request.user|is_not_author_of_submission:rec.submission.arxiv_identifier_w_vn_nr %} - {{ rec.submission.header_as_li_for_Fellows }} - <div class="flex-greybox"> - <h3>Editorial recommendation:</h3> - <ul> - <li>{{ rec.print_for_Fellows }}</li> - </ul> + {% if not forloop.first %} + <hr> + {% endif %} + + <div class="row"> + <div class="col-12"> + <div class="card card-outline-secondary"> + {% include 'submissions/_submission_card_fellow_content.html' with submission=rec.submission %} + </div> + + <div class="card card-outline-secondary"> + <div class="card-header"> + <h3>Editorial recommendation</h3> + </div> + <div class="card-block"> + {# {{ rec.print_for_Fellows }}#} + <div class="card card-outline-secondary"> + {% include 'submissions/_recommendation_fellow_content.html' with recommendation=rec %} + </div> + + {% if rec.remark_set.all %} + <h3 class="card-title">Remarks by Fellows:</h3> + <ul> + {% for remark in rec.remark_set.all|sort_by:'date' %} + {{ remark.as_li }} + {% endfor %} + </ul> + {% endif %} + + <h3 class="card-title">Fellows eligible to vote:</h3> + <ul> + <li> + {% for eligible in rec.eligible_to_vote.all|sort_by:'user__last_name' %} + {{ eligible.user.last_name }}, + {% endfor %} + </li> + </ul> + + <h3 class="card-title">Voting results up to now:</h3> + <ul> + <li> + Agreed: ({{ rec.voted_for.all.count }}) + {% for agreed in rec.voted_for.all|sort_by:'user__last_name' %} + {{ agreed.user.last_name }}, + {% endfor %} + </li> + <li> + Disagreed: ({{ rec.voted_against.all.count }}) + {% for disagreed in rec.voted_against.all|sort_by:'user__last_name' %} + {{ disagreed.user.last_name }}, + {% endfor %} + </li> + <li> + Abstained: ({{ rec.voted_abstain.all.count }}) + {% for abstained in rec.voted_abstain.all|sort_by:'user__last_name' %} + {{ abstained.user.last_name }}, + {% endfor %} + </li> + </ul> + + {% if rec.remark_set %} + <h4 class="card-title">Remarks:</h4> + <ul> + {% for rem in rec.remark_set.all %} + <li>{{ rem }}</li> + {% endfor %} + </ul> + {% endif %} + </div> + <div class="card-footer"> + <h3 class="card-title">Actions:</h3> + <ul> + <li>To fix the College decision and follow the Editorial Recommendation as is: <a href="{% url 'submissions:fix_College_decision' rec_id=rec.id %}">click here</a></li> + <li>To request a modification of the Recommendation to request for revision: click here</li> + </ul> + </div> + </div> + </div> + {% endfor %} </div> - {% if rec.remark_set.all %} - <h3>Remarks by Fellows:</h3> - <ul> - {% for remark in rec.remark_set.all|sort_by:'date' %} - {{ remark.as_li }} - {% endfor %} - </ul> - {% endif %} - - <h3>Fellows eligible to vote:</h3> - <ul> - <li> - <p> - {% for eligible in rec.eligible_to_vote.all|sort_by:'user__last_name' %} - {{ eligible.user.last_name }}, - {% endfor %} - </p> - </li> - </ul> - <h3>Voting results up to now:</h3> - <ul> - <li> - <p>Agreed: ({{ rec.voted_for.all.count }}) - {% for agreed in rec.voted_for.all|sort_by:'user__last_name' %} - {{ agreed.user.last_name }}, - {% endfor %} - </p> - </li> - <li> - <p>Disagreed: ({{ rec.voted_against.all.count }}) - {% for disagreed in rec.voted_against.all|sort_by:'user__last_name' %} - {{ disagreed.user.last_name }}, - {% endfor %} - </p> - </li> - <li> - <p>Abstained: ({{ rec.voted_abstain.all.count }}) - {% for abstained in rec.voted_abstain.all|sort_by:'user__last_name' %} - {{ abstained.user.last_name }}, - {% endfor %} - </p> - </li> - </ul> - {% if rec.remark_set %} - <h4>Remarks:</h4> - <ul> - {% for rem in rec.remark_set.all %} - <li>{{ rem }}</li> - {% endfor %} - </ul> - {% endif %} - <h4>Actions:</h4> - <ul> - <li>To fix the College decision and follow the Editorial Recommendation as is: <a href="{% url 'submissions:fix_College_decision' rec_id=rec.id %}">click here</a></li> - <li>To request a modification of the Recommendation to request for revision: click here</li> - </ul> - <br/> - <hr class="hr6"/> - {% endif %} - {% endfor %} - </ul> -</section> -<hr class="hr12"/> + <hr> {% endif %} {% if assignments_to_consider %} -<section> - - {% for assignment_to_consider in assignments_to_consider %} - - <div class="flex-greybox"> - <h1>Assignment request: can you act as Editor-in-charge? (see below to accept/decline):</h1> - </div> - <br> - <hr> - - {{ assignment_to_consider.submission.header_as_table }} - <br /> - <h4>Abstract:</h4> - <p>{{ assignment_to_consider.submission.abstract }}</p> - <br/> - - <hr> - <div class="flex-greybox"> - <h1>Accept or Decline this Assignment</h1> - </div> - <h3>By accepting, you will be required to start a refereeing round on the next screen.</h3> - <form action="{% url 'submissions:accept_or_decline_assignment_ack' assignment_id=assignment_to_consider.id %}" method="post"> - {% csrf_token %} - {{ consider_assignment_form.accept }} - <div id="ref_reason"> - <p>Please select a reason for declining this assignment:</p> - {{ consider_assignment_form.refusal_reason }} + <div class="row"> + <div class="col-12"> + <div class="highlight d-block p-3"> + <h1 class="p-0">Assignment request</h1> + <h3 class="p-0 mt-1 d-block text-muted">Can you act as Editor-in-charge? (see below to accept/decline)</h3> + </div> + </div> </div> - <input type="submit" value="Submit" /> - </form> + {% for assignment_to_consider in assignments_to_consider %} + <div class="row"> + <div class="col-12"> + <div class="card"> + <div class="card-block"> + {{ assignment_to_consider.submission.header_as_table }} + <br /> - <hr class="hr6"/> - {% endfor %} + <h4>Abstract:</h4> + <p>{{ assignment_to_consider.submission.abstract }}</p> + </div> + <div class="card-footer"> + <h1>Accept or Decline this Assignment</h1> + <h3 class="mb-2">By accepting, you will be required to start a refereeing round on the next screen.</h3> -</section> -<hr class="hr12"/> + <form action="{% url 'submissions:accept_or_decline_assignment_ack' assignment_id=assignment_to_consider.id %}" method="post"> + {% csrf_token %} + <div class="form-group row"> + <div class="col-12"> + {{ consider_assignment_form.accept }} + </div> + </div> + <div class="row" id="ref_reason"> + <div class="col-12"> + <p>Please select a reason for declining this assignment</p> + {{ consider_assignment_form.refusal_reason|bootstrap:'0,12' }} + </div> + </div> + <input class="btn btn-secondary" type="submit" value="Submit" /> + </form> + </div> + </div> + </div> + </div> + {% endfor %} + <hr> {% endif %} {% if request.user|is_in_group:'Editorial Administrators' and recommendations_to_prepare_for_voting %} -<section> - <div class="flex-container"> - <div class="flex-greybox"> - <h1>Recommendations to prepare for voting</h1> + <div class="row"> + <div class="col-12"> + <h1 class="highlight d-block">Recommendations to prepare for voting</h1> + </div> </div> - </div> - <ul> + {% for rec in recommendations_to_prepare_for_voting %} - {% if request.user|is_not_author_of_submission:rec.submission.arxiv_identifier_w_vn_nr %} - {{ rec.submission.header_as_li_for_Fellows }} - <div class="flex-greybox"> - <h3>Editorial recommendation:</h3> - <ul> - <li>{{ rec.print_for_Fellows }}</li> - </ul> - </div> - <h4>Actions:</h4> - <ul> - <li><a href="{% url 'submissions:prepare_for_voting' rec_id=rec.id %}">Prepare for voting</a></li> - </ul> - {% endif %} + {% if not forloop.first %} + <hr> + {% endif %} + + <div class="row"> + <div class="col-12"> + <div class="card card-outline-secondary"> + {% include 'submissions/_submission_card_fellow_content.html' with submission=rec.submission %} + </div> + + <div class="card card-outline-secondary"> + <div class="card-header"> + <h3>Editorial recommendation</h3> + </div> + <div class="card-block"> + {{ rec.print_for_Fellows }} + </div> + <div class="card-footer"> + <h3>Actions:</h3> + <ul> + <li><a href="{% url 'submissions:prepare_for_voting' rec_id=rec.id %}">Prepare for voting</a></li> + </ul> + </div> + </div> + </div> + </div> {% endfor %} - </ul> -</section> -<hr class="hr12"/> + <hr> {% endif %} {% if recs_to_vote_on %} -<section> - <div class="flex-container"> - <div class="flex-greybox"> - <h1>Recommendations to vote on</h1> + <div class="row"> + <div class="col-12"> + <h1 class="highlight d-block">Recommendations to vote on</h1> + </div> </div> - </div> - <ul> {% for rec in recs_to_vote_on %} - {% if request.user|is_not_author_of_submission:rec.submission.arxiv_identifier_w_vn_nr %} - {{ rec.submission.header_as_li_for_Fellows }} - <div class="flex-greybox"> - <h3>Editorial recommendation:</h3> - <ul> - <li>{{ rec.print_for_Fellows }}</li> - </ul> - </div> - <form action="{% url 'submissions:vote_on_rec' rec_id=rec.id %}" method="post"> - {% csrf_token %} - {% load crispy_forms_tags %} - {% crispy rec_vote_form %} - </form> - <hr class="hr6"/> - <br/> - {% endif %} + {% if not forloop.first %} + <hr> + {% endif %} + + <div class="row"> + <div class="col-12"> + <div class="card card-outline-secondary"> + {% include 'submissions/_submission_card_fellow_content.html' with submission=rec.submission %} + </div> + + <div class="card card-outline-secondary"> + <div class="card-header"> + <h3>Editorial recommendation</h3> + </div> + <div class="card-block"> + {{ rec.print_for_Fellows }} + </div> + <div class="card-footer"> + <h3>Your position on this recommendation</h3> + <form action="{% url 'submissions:vote_on_rec' rec_id=rec.id %}" method="post"> + {% csrf_token %} + {{ rec_vote_form|bootstrap:'0,12' }} + <input type="submit" name="submit" value="Cast your vote" class="btn btn-primary submitButton" id="submit-id-submit"> + </form> + </div> + </div> + </div> + </div> {% endfor %} - </ul> -</section> -<hr class="hr12"/> + <hr> {% endif %} -<section> - <div class="flex-container"> - <div class="flex-greybox"> - <h1>SciPost Submissions Pool</h1> +<div class="row"> + <div class="col-12"> + <h1 class="highlight">SciPost Submissions Pool</h1> </div> - </div> +</div> - <h3>Submissions by status:</h3> - <ul> - {% for key, val in submission_status %} - <li><a href="{% url 'submissions:submissions_by_status' status=key %}">{{ val }}</a></li> - {% endfor %} - </ul> - <hr class="hr6"/> - - - <ul> - {% for sub in submissions_in_pool %} - {% if request.user|is_not_author_of_submission:sub.arxiv_identifier_w_vn_nr %} - <br/> - {{ sub.header_as_li_for_Fellows }} - {% if sub.remark_set.all %} - <h4>Remarks on this submission:</h4> - <ul> - {% for rem in sub.remark_set.all %} - {{ rem.as_li }} - {% endfor %} - </ul> - {% endif %} - <button class="submitRemarkButton" id="remarkButton{{ submission.id }}">Add a remark on this Submission</button> - <div class="submitRemarkForm" id="remarkForm{{ submission.id }}"> - <form action="{% url 'submissions:add_remark' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}" method="post"> - {% csrf_token %} - {{ remark_form.as_p }} - <input type="submit" value="Submit" /> - </form> - </div> - {% get_obj_perms request.user for sub as "sub_perms" %} - {% if "can_take_editorial_actions" in sub_perms or request.user|is_in_group:'Editorial Administrators' %} - <br/> - {% if sub|required_actions %} - <div class="flex-container"> - <div class-"flex-whitebox" style="background-color: #ffaaaa;"> - <h3>Required actions:</h3> - <ul> - {% for todoitem in sub|required_actions %} - <li>{{ todoitem }}</li> - {% endfor %} - </ul> - </div> +<div class="row"> + <div class="col-12"> + <h3>Submissions by status:</h3> + <ul> + {% for key, val in submission_status %} + <li> + <a href="{% url 'submissions:submissions_by_status' status=key %}">{{ val }}</a> + </li> + {% endfor %} + </ul> </div> - {% endif %} - <h4><a href="{% url 'submissions:editorial_page' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}"> - Go to this Submission's Editorial Page</a></h4> - {% endif %} - {% if perms.scipost.can_assign_submissions %} - {% if sub.editorialassignment_set.all %} - <h4>EIC Assignment requests:</h4> - <ul> - {% for assignment in sub.editorialassignment_set.all %} - {{ assignment.info_as_li }} - {% endfor %} - </ul> - {% endif %} - {% if sub.editor_in_charge == None %} - <h4>Actions:</h4> - <ul> - <li><a href="{% url 'submissions:assign_submission' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}">Send a new assignment request</a></li> - <li><a href="{% url 'submissions:assignment_failed' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}">Close pre-screening: failure to find EIC</a></li> - </ul> - {% endif %} - {% endif %} - {% if request.user|is_in_group:'Editorial Administrators' %} - <h4><a href="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr comtype='StoE' %}">Send a communication to the Editor-in-charge</a></h4> - {% if sub.status == 'accepted' %} - <h4>After proofs have been accepted, you can - <a href="{% url 'journals:initiate_publication' %}">initiate the publication process</a> (leads to the validation page)</h4> - {% endif %} - {% endif %} - {% endif %} - {% endfor %} - </ul> +</div> + +<hr> +<div class="row"> + <div class="col-12"> + <!-- Submissions list --> + {% for sub in submissions_in_pool %} + <div class="card card-outline-secondary mt-1"> + {% include 'submissions/_submission_card_fellow_content.html' with submission=sub %} + + <div class="card-block"> + {% if sub.remark_set.all %} + <h4>Remarks on this submission:</h4> + <ul> + {% for rem in sub.remark_set.all %} + {{ rem.as_li }} + {% endfor %} + </ul> + {% endif %} + <button class="btn btn-secondary mb-2 submitRemarkButton" id="remarkButton{{ submission.id }}">Add a remark on this Submission</button> + <div class="submitRemarkForm pb-2" id="remarkForm{{ submission.id }}"> + <form action="{% url 'submissions:add_remark' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}" method="post"> + {% csrf_token %} + {{ remark_form|bootstrap:'0,12' }} + <input class="btn btn-secondary" type="submit" value="Submit" /> + </form> + </div> -</section> + {% get_obj_perms request.user for sub as "sub_perms" %} + {% if "can_take_editorial_actions" in sub_perms or request.user|is_in_group:'Editorial Administrators' %} + {% if sub|required_actions %} + <div class="required-actions"> + <h3 class="pt-0">Required actions:</h3> + <ul> + {% for todoitem in sub|required_actions %} + <li>{{ todoitem }}</li> + {% endfor %} + </ul> + </div> + {% endif %} + + <h4> + <a href="{% url 'submissions:editorial_page' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}">Go to this Submission's Editorial Page</a> + </h4> + {% endif %} + + {% if perms.scipost.can_assign_submissions %} + {% if sub.editorialassignment_set.all %} + <h4>EIC Assignment requests:</h4> + <ul> + {% for assignment in sub.editorialassignment_set.all %} + {{ assignment.info_as_li }} + {% endfor %} + </ul> + {% endif %} + {% if sub.editor_in_charge == None %} + <h4>Actions:</h4> + <ul> + <li><a href="{% url 'submissions:assign_submission' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}">Send a new assignment request</a></li> + <li><a href="{% url 'submissions:assignment_failed' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr %}">Close pre-screening: failure to find EIC</a></li> + </ul> + {% endif %} + {% endif %} + + {% if request.user|is_in_group:'Editorial Administrators' %} + <h4> + <a href="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=sub.arxiv_identifier_w_vn_nr comtype='StoE' %}">Send a communication to the Editor-in-charge</a> + </h4> + {% if sub.status == 'accepted' %} + <h4>After proofs have been accepted, you can <a href="{% url 'journals:initiate_publication' %}">initiate the publication process</a> (leads to the validation page)</h4> + {% endif %} + {% endif %} + </div> + </div> + {% endfor %} + </div> +</div> -{% endblock bodysup %} +{% endblock content %} diff --git a/submissions/templates/submissions/submissions.html b/submissions/templates/submissions/submissions.html index 0307dacacd39c88bd18e39a2e497de1c92868d6e..4abd6ffd43c7d5e22b2ce747a91ecfd61cc48bd5 100644 --- a/submissions/templates/submissions/submissions.html +++ b/submissions/templates/submissions/submissions.html @@ -9,27 +9,33 @@ {% block content %} <div class="row"> <div class="col-md-4"> - <div class="panel page-header-panel"> - <h1>SciPost Submissions</h1> - <h3><a href="{% url 'submissions:sub_and_ref_procedure' %}">Submission and refereeing procedure</a></h3> - <h3><a href="{% url 'submissions:submit_manuscript' %}">Submit a manuscript to SciPost</a></h3> + <div class="card card-grey"> + <div class="card-block min-height-190"> + <h1 class="card-title">SciPost Submissions</h1> + <h3><a href="{% url 'submissions:sub_and_ref_procedure' %}">Submission and refereeing procedure</a></h3> + <h3><a href="{% url 'submissions:submit_manuscript' %}">Submit a manuscript to SciPost</a></h3> + </div> </div> </div> <div class="col-md-4"> - <div class="panel page-header-panel"> - <h2>Search SciPost Submissions:</h2> - <form action="{% url 'submissions:submissions' %}" class="small" method="get"> - {{ form|bootstrap:'4,8,sm' }} - <input class="btn btn-sm btn-secondary" type="submit" name="Submit" /> - </form> + <div class="card card-grey"> + <div class="card-block min-height-190"> + <h2 class="card-title">Search SciPost Submissions:</h2> + <form action="{% url 'submissions:submissions' %}" class="small" method="get"> + {{ form|bootstrap:'4,8,sm' }} + <input class="btn btn-sm btn-secondary" type="submit" name="Submit" /> + </form> + </div> </div> </div> <div class="col-md-4"> - <div class="panel page-header-panel"> - <h2>View SciPost Submissions</h2> - <ul> - <li>Physics: last <a href="{% url 'submissions:browse' discipline='physics' nrweeksback=1 %}">week</a> <a href="{% url 'submissions:browse' discipline='physics' nrweeksback=4 %}">month</a> <a href="{% url 'submissions:browse' discipline='physics' nrweeksback=52 %}">year</a></li> - </ul> + <div class="card card-grey"> + <div class="card-block min-height-190"> + <h2>View SciPost Submissions</h2> + <ul> + <li>Physics: last <a href="{% url 'submissions:browse' discipline='physics' nrweeksback=1 %}">week</a> <a href="{% url 'submissions:browse' discipline='physics' nrweeksback=4 %}">month</a> <a href="{% url 'submissions:browse' discipline='physics' nrweeksback=52 %}">year</a></li> + </ul> + </div> </div> </div> </div> @@ -56,9 +62,11 @@ {% endif %} </p> {% endif %} - <ul> + <ul class="list-group list-group-flush"> {% for submission in object_list %} - {{ submission.header_as_li }} + <li class="list-group-item"> + {% include 'submissions/_submission_card_content.html' with submission=submission %} + </li> {% endfor %} </ul> {% else %} diff --git a/submissions/views.py b/submissions/views.py index e3edda4495e5da2776d546a26cb9940a3e52150b..fda4f7b141a94ddbacfa709fa453769c9bc4f1e6 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -347,25 +347,26 @@ def pool(request): to publication acceptance or rejection. All members of the Editorial College have access. """ - submissions_in_pool = (Submission.objects.all() - .exclude(status__in=SUBMISSION_STATUS_OUT_OF_POOL) - .exclude(is_current=False) - .order_by('-submission_date')) - recommendations_undergoing_voting = (EICRecommendation.objects.filter( - submission__status__in=['put_to_EC_voting'])) - recommendations_to_prepare_for_voting = (EICRecommendation.objects.filter( - submission__status__in=['voting_in_preparation'])) + submissions_in_pool = Submission.objects.get_pool(request.user) + recommendations_undergoing_voting = (EICRecommendation.objects + .get_for_user_in_pool(request.user) + .filter(submission__status__in=['put_to_EC_voting'])) + recommendations_to_prepare_for_voting = (EICRecommendation.objects + .get_for_user_in_pool(request.user) + .filter(submission__status__in= + ['voting_in_preparation'])) contributor = Contributor.objects.get(user=request.user) assignments_to_consider = EditorialAssignment.objects.filter( to=contributor, accepted=None, deprecated=False) consider_assignment_form = ConsiderAssignmentForm() - recs_to_vote_on = EICRecommendation.objects.filter( - eligible_to_vote__in=[contributor]).exclude( - recommendation=-1).exclude(recommendation=-2).exclude( - voted_for__in=[contributor]).exclude( - voted_against__in=[contributor]).exclude( - voted_abstain__in=[contributor]).exclude( - submission__status__in=SUBMISSION_STATUS_VOTING_DEPRECATED) + recs_to_vote_on = (EICRecommendation.objects.get_for_user_in_pool(request.user) + .filter(eligible_to_vote__in=[contributor]) + .exclude(recommendation=-1) + .exclude(recommendation=-2) + .exclude(voted_for__in=[contributor]) + .exclude(voted_against__in=[contributor]) + .exclude(voted_abstain__in=[contributor]) + .exclude(submission__status__in=SUBMISSION_STATUS_VOTING_DEPRECATED)) rec_vote_form = RecommendationVoteForm() remark_form = RemarkForm() context = {'submissions_in_pool': submissions_in_pool, diff --git a/webpack.config.js b/webpack.config.js index 2b29fc5ea351cb1b77f543f2d3f053c5982c49be..f9175f5386badff05540d955f991c81d68778427 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,7 +12,8 @@ module.exports = { "./scipost/static/scipost/assets/js/scripts.js", "./scipost/static/scipost/assets/css/style.scss" ], - bootstrap: 'bootstrap-loader' + bootstrap: 'bootstrap-loader', + tooltip: "./scipost/static/scipost/assets/js/tooltip.js", }, output: { path: path_bundles,