diff --git a/commentaries/factories.py b/commentaries/factories.py new file mode 100644 index 0000000000000000000000000000000000000000..f0bae1a0912e9a18ceb0c6f079918c595534948b --- /dev/null +++ b/commentaries/factories.py @@ -0,0 +1,33 @@ +import factory + +from .models import Commentary, COMMENTARY_TYPES + +from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS +from scipost.factories import ContributorFactory +from journals.models import SCIPOST_JOURNALS_DOMAINS + + +class CommentaryFactory(factory.django.DjangoModelFactory): + class Meta: + model = Commentary + abstract = True + + requested_by = factory.SubFactory(ContributorFactory) + vetted_by = factory.SubFactory(ContributorFactory) + type = COMMENTARY_TYPES[0][0] + discipline = SCIPOST_DISCIPLINES[0][0] + domain = SCIPOST_JOURNALS_DOMAINS[0][0] + subject_area = SCIPOST_SUBJECT_AREAS[0][1][0][0] + pub_title = factory.Sequence(lambda n: "Commentary %d" % n) + pub_DOI = '10.1103/PhysRevB.92.214427' + arxiv_identifier = '1610.06911v1' + author_list = factory.Faker('name') + pub_abstract = factory.Faker('text') + + +class EmptyCommentaryFactory(CommentaryFactory): + pub_DOI = None + arxiv_identifier = None + +class VettedCommentaryFactory(CommentaryFactory): + vetted = True diff --git a/commentaries/forms.py b/commentaries/forms.py index 14cd5852633ca67586f10bb5cf3e39f7fcefd1ea..f0dcdf3a30c8497a8d19650abc9fda2c291bda45 100644 --- a/commentaries/forms.py +++ b/commentaries/forms.py @@ -1,7 +1,9 @@ from django import forms +from django.shortcuts import get_object_or_404 from .models import Commentary +from scipost.models import Contributor COMMENTARY_ACTION_CHOICES = ( (0, 'modify'), @@ -17,10 +19,12 @@ COMMENTARY_REFUSAL_CHOICES = ( ) commentary_refusal_dict = dict(COMMENTARY_REFUSAL_CHOICES) + class DOIToQueryForm(forms.Form): doi = forms.CharField(widget=forms.TextInput( {'label': 'DOI', 'placeholder': 'ex.: 10.21468/00.000.000000'})) + class IdentifierToQueryForm(forms.Form): identifier = forms.CharField(widget=forms.TextInput( {'label': 'arXiv identifier', @@ -28,6 +32,8 @@ class IdentifierToQueryForm(forms.Form): class RequestCommentaryForm(forms.ModelForm): + existing_commentary = None + class Meta: model = Commentary fields = ['type', 'discipline', 'domain', 'subject_area', @@ -38,6 +44,7 @@ class RequestCommentaryForm(forms.ModelForm): 'pub_DOI', 'pub_abstract'] def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user', None) super(RequestCommentaryForm, self).__init__(*args, **kwargs) self.fields['metadata'].widget = forms.HiddenInput() self.fields['pub_date'].widget.attrs.update({'placeholder': 'Format: YYYY-MM-DD'}) @@ -46,6 +53,44 @@ class RequestCommentaryForm(forms.ModelForm): self.fields['pub_DOI'].widget.attrs.update({'placeholder': 'ex.: 10.21468/00.000.000000'}) self.fields['pub_abstract'].widget.attrs.update({'cols': 100}) + def clean(self, *args, **kwargs): + cleaned_data = super(RequestCommentaryForm, self).clean(*args, **kwargs) + + # Either Arxiv-ID or DOI is given + if not cleaned_data['arxiv_identifier'] and not cleaned_data['pub_DOI']: + msg = ('You must provide either a DOI (for a published paper) ' + 'or an arXiv identifier (for a preprint).') + self.add_error('arxiv_identifier', msg) + self.add_error('pub_DOI', msg) + elif (cleaned_data['arxiv_identifier'] and + (Commentary.objects + .filter(arxiv_identifier=cleaned_data['arxiv_identifier']).exists())): + msg = 'There already exists a Commentary Page on this preprint, see' + self.existing_commentary = get_object_or_404( + Commentary, + arxiv_identifier=cleaned_data['arxiv_identifier']) + self.add_error('arxiv_identifier', msg) + elif (cleaned_data['pub_DOI'] and + Commentary.objects.filter(pub_DOI=cleaned_data['pub_DOI']).exists()): + msg = 'There already exists a Commentary Page on this publication, see' + self.existing_commentary = get_object_or_404(Commentary, pub_DOI=cleaned_data['pub_DOI']) + self.add_error('pub_DOI', msg) + + # Current user is not known + if not self.user or not Contributor.objects.filter(user=self.user).exists(): + self.add_error(None, 'Sorry, current user is not known to SciPost.') + + + def save(self, *args, **kwargs): + """Prefill instance before save""" + self.requested_by = Contributor.objects.get(user=self.user) + return super(RequestCommentaryForm, self).save(*args, **kwargs) + + def get_existing_commentary(self): + """Get Commentary if found after validation""" + return self.existing_commentary + + class VetCommentaryForm(forms.Form): action_option = forms.ChoiceField(widget=forms.RadioSelect, choices=COMMENTARY_ACTION_CHOICES, @@ -54,7 +99,16 @@ class VetCommentaryForm(forms.Form): email_response_field = forms.CharField(widget=forms.Textarea( attrs={'rows': 5, 'cols': 40}), label='Justification (optional)', required=False) + class CommentarySearchForm(forms.Form): + """Search for Commentary specified by user""" pub_author = forms.CharField(max_length=100, required=False, label="Author(s)") - pub_title_keyword = forms.CharField(max_length=100, label="Title", required=False) + pub_title_keyword = forms.CharField(max_length=100, required=False, label="Title") pub_abstract_keyword = forms.CharField(max_length=1000, required=False, label="Abstract") + + def search_results(self): + """Return all Commentary objects according to search""" + return Commentary.objects.vetted( + pub_title__icontains=self.cleaned_data['pub_title_keyword'], + pub_abstract__icontains=self.cleaned_data['pub_abstract_keyword'], + author_list__icontains=self.cleaned_data['pub_author']).order_by('-pub_date') diff --git a/commentaries/migrations/0013_auto_20161213_2328.py b/commentaries/migrations/0013_auto_20161213_2328.py new file mode 100644 index 0000000000000000000000000000000000000000..ec27da0367e4f87026cfc64b8131232f62bfb3cc --- /dev/null +++ b/commentaries/migrations/0013_auto_20161213_2328.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-12-13 22:28 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('commentaries', '0012_remove_commentary_specialization'), + ] + + operations = [ + migrations.AddField( + model_name='commentary', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AlterField( + model_name='commentary', + name='latest_activity', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/commentaries/models.py b/commentaries/models.py index c216ce3599e806f4ec35c2925dcb8d49c7b3caf2..a42005b4dae74ac4621960f27e9bfc11dcd4bbb0 100644 --- a/commentaries/models.py +++ b/commentaries/models.py @@ -1,70 +1,86 @@ from django.utils import timezone from django.db import models -from django.contrib.auth.models import User from django.contrib.postgres.fields import JSONField from django.template import Template, Context from journals.models import SCIPOST_JOURNALS_DOMAINS, SCIPOST_JOURNALS_SPECIALIZATIONS -from scipost.models import Contributor -from scipost.models import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS - - +from scipost.models import TimeStampedModel, Contributor +from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS COMMENTARY_TYPES = ( ('published', 'published paper'), ('preprint', 'arXiv preprint'), ) -class Commentary(models.Model): + +class CommentaryManager(models.Manager): + def vetted(self, **kwargs): + return self.filter(vetted=True, **kwargs) + + def awaiting_vetting(self, **kwargs): + return self.filter(vetted=False, **kwargs) + + +class Commentary(TimeStampedModel): """ A Commentary contains all the contents of a SciPost Commentary page for a given publication. """ - requested_by = models.ForeignKey (Contributor, blank=True, null=True, - on_delete=models.CASCADE, related_name='requested_by') + requested_by = models.ForeignKey( + Contributor, blank=True, null=True, + on_delete=models.CASCADE, related_name='requested_by') vetted = models.BooleanField(default=False) - vetted_by = models.ForeignKey (Contributor, blank=True, null=True, on_delete=models.CASCADE) - type = models.CharField(max_length=9, choices=COMMENTARY_TYPES) # published paper or arxiv preprint + vetted_by = models.ForeignKey(Contributor, blank=True, null=True, on_delete=models.CASCADE) + type = models.CharField(max_length=9, choices=COMMENTARY_TYPES) discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES, default='physics') domain = models.CharField(max_length=3, choices=SCIPOST_JOURNALS_DOMAINS) -# specialization = models.CharField(max_length=1, choices=SCIPOST_JOURNALS_SPECIALIZATIONS) - subject_area = models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS, default='Phys:QP') + subject_area = models.CharField( + max_length=10, choices=SCIPOST_SUBJECT_AREAS, + default='Phys:QP') open_for_commenting = models.BooleanField(default=True) pub_title = models.CharField(max_length=300, verbose_name='title') - arxiv_identifier = models.CharField(max_length=100, - verbose_name="arXiv identifier (including version nr)", - blank=True, null=True) + arxiv_identifier = models.CharField( + max_length=100, verbose_name="arXiv identifier (including version nr)", + blank=True, null=True) arxiv_link = models.URLField(verbose_name='arXiv link (including version nr)', blank=True) - pub_DOI = models.CharField(max_length=200, verbose_name='DOI of the original publication', - blank=True, null=True) - pub_DOI_link = models.URLField(verbose_name='DOI link to the original publication', blank=True) + pub_DOI = models.CharField( + max_length=200, verbose_name='DOI of the original publication', + blank=True, null=True) + pub_DOI_link = models.URLField( + verbose_name='DOI link to the original publication', + blank=True) metadata = JSONField(default={}, blank=True, null=True) arxiv_or_DOI_string = models.CharField( max_length=100, verbose_name='string form of arxiv nr or DOI for commentary url', default='') author_list = models.CharField(max_length=1000) + # Authors which have been mapped to contributors: - authors = models.ManyToManyField (Contributor, blank=True, - related_name='authors_com') - authors_claims = models.ManyToManyField (Contributor, blank=True, - related_name='authors_com_claims') - authors_false_claims = models.ManyToManyField (Contributor, blank=True, - related_name='authors_com_false_claims') + authors = models.ManyToManyField( + Contributor, blank=True, + related_name='authors_com') + authors_claims = models.ManyToManyField( + Contributor, blank=True, + related_name='authors_com_claims') + authors_false_claims = models.ManyToManyField( + Contributor, blank=True, + related_name='authors_com_false_claims') journal = models.CharField(max_length=300, blank=True, null=True) volume = models.CharField(max_length=50, blank=True, null=True) pages = models.CharField(max_length=50, blank=True, null=True) - pub_date = models.DateField(verbose_name='date of original publication', blank=True, null=True) + pub_date = models.DateField( + verbose_name='date of original publication', + blank=True, null=True) pub_abstract = models.TextField(verbose_name='abstract') - latest_activity = models.DateTimeField(default=timezone.now) + + objects = CommentaryManager() class Meta: verbose_name_plural = 'Commentaries' - def __str__(self): return self.pub_title - def header_as_table(self): # for display in Commentary page itself header = ('<table>' @@ -93,8 +109,8 @@ class Commentary(models.Model): header += '</table>' template = Template(header) context = Context({ - 'pub_title': self.pub_title, 'author_list': self.author_list, - }) + 'pub_title': self.pub_title, 'author_list': self.author_list, + }) if self.type == 'published': context['journal'] = self.journal context['volume'] = self.volume @@ -105,7 +121,6 @@ class Commentary(models.Model): context['arxiv_link'] = self.arxiv_link return template.render(context) - def header_as_li(self): # for display in search lists context = Context({'scipost_url': self.scipost_url(), 'pub_title': self.pub_title, @@ -136,7 +151,6 @@ class Commentary(models.Model): return template.render(context) - def simple_header_as_li(self): # for display in Lists context = Context({'scipost_url': self.scipost_url(), 'pub_title': self.pub_title, @@ -158,7 +172,6 @@ class Commentary(models.Model): template = Template(header) return template.render(context) - def parse_links_into_urls(self): """ Takes the arXiv nr or DOI and turns it into the urls """ if self.pub_DOI: diff --git a/commentaries/test_forms.py b/commentaries/test_forms.py new file mode 100644 index 0000000000000000000000000000000000000000..694ce19ec56c93503b879a51038f068b06947f7c --- /dev/null +++ b/commentaries/test_forms.py @@ -0,0 +1,42 @@ +import factory + +from django.test import TestCase + +from scipost.factories import UserFactory + +from .factories import VettedCommentaryFactory +from .forms import RequestCommentaryForm +from common.helpers import model_form_data + + +class TestRequestCommentaryForm(TestCase): + fixtures = ['permissions', 'groups'] + + def setUp(self): + factory_instance = VettedCommentaryFactory.build() + self.user = UserFactory() + self.valid_form_data = model_form_data(factory_instance, RequestCommentaryForm) + + def test_valid_data_is_valid_for_arxiv(self): + """Test valid form for Arxiv identifier""" + form_data = self.valid_form_data + form_data['pub_DOI'] = '' + form = RequestCommentaryForm(form_data, user=self.user) + self.assertTrue(form.is_valid()) + + def test_valid_data_is_valid_for_DOI(self): + """Test valid form for DOI""" + form_data = self.valid_form_data + form_data['arxiv_identifier'] = '' + form = RequestCommentaryForm(form_data, user=self.user) + self.assertTrue(form.is_valid()) + + # def test_form_has_no_identifiers(self): + # """Test invalid form has no DOI nor Arxiv ID""" + # form_data = self.valid_form_data + # form_data['pub_DOI'] = '' + # form_data['arxiv_identifier'] = '' + # form = RequestCommentaryForm(form_data, user=self.user) + # form_response = form.is_valid() + # print(form_response) + # self.assertFormError(form_response, form, 'arxiv_identifier', None) diff --git a/commentaries/test_models.py b/commentaries/test_models.py new file mode 100644 index 0000000000000000000000000000000000000000..2e9cb5f6ba351402af656aec1be5d9ac257bc5c0 --- /dev/null +++ b/commentaries/test_models.py @@ -0,0 +1 @@ +from django.test import TestCase diff --git a/commentaries/test_views.py b/commentaries/test_views.py new file mode 100644 index 0000000000000000000000000000000000000000..e6cc591f82f92345fe466d9dad9a23fe0d24063f --- /dev/null +++ b/commentaries/test_views.py @@ -0,0 +1,29 @@ +from django.contrib.auth.models import Group +from django.core.urlresolvers import reverse +from django.test import TestCase + +class RequestCommentaryTest(TestCase): + """Test cases for `request_commentary` view method""" + fixtures = ['permissions', 'groups', 'contributors'] + + def setUp(self): + self.view_url = reverse('commentaries:request_commentary') + self.login_url = reverse('scipost:login') + self.redirected_login_url = '%s?next=%s' % (self.login_url, self.view_url) + + def test_get_requests(self): + """Test different GET requests on view""" + # Anoymous user should redirect to login page + request = self.client.get(self.view_url) + self.assertRedirects(request, self.redirected_login_url) + + # Registered Contributor should get 200 + self.client.login(username="Test", password="testpw") + request = self.client.get(self.view_url) + self.assertEquals(request.status_code, 200) + + def test_post_invalid_forms(self): + """Test different kind of invalid RequestCommentaryForm submits""" + self.client.login(username="Test", password="testpw") + request = self.client.post(self.view_url) + self.assertEquals(request.status_code, 200) diff --git a/commentaries/views.py b/commentaries/views.py index 88f4d7c69b97e0c58870415078bd703ea2afa578..506fd838fc126d015f7c7144b29e4f73a4340b0f 100644 --- a/commentaries/views.py +++ b/commentaries/views.py @@ -6,15 +6,12 @@ import requests from django.db.models import Q from django.utils import timezone from django.shortcuts import get_object_or_404, render -from django.contrib.auth import authenticate, login, logout +from django.contrib.auth import login, logout from django.contrib.auth.decorators import login_required, permission_required -from django.contrib.auth.models import User from django.core.mail import EmailMessage from django.core.urlresolvers import reverse -from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponse from django.shortcuts import redirect -from django.views.decorators.csrf import csrf_protect -from django.db.models import Avg from .models import Commentary from .forms import RequestCommentaryForm, DOIToQueryForm, IdentifierToQueryForm @@ -35,64 +32,29 @@ from scipost.forms import AuthenticationForm @login_required @permission_required('scipost.can_request_commentary_pages', raise_exception=True) def request_commentary(request): + form = RequestCommentaryForm(request.POST or None, user=request.user) if request.method == 'POST': - form = RequestCommentaryForm(request.POST) if form.is_valid(): - errormessage = '' - existing_commentary = None - if not form.cleaned_data['arxiv_identifier'] and not form.cleaned_data['pub_DOI']: - errormessage = ('You must provide either a DOI (for a published paper) ' - 'or an arXiv identifier (for a preprint).') - elif (form.cleaned_data['arxiv_identifier'] and - (Commentary.objects - .filter(arxiv_identifier=form.cleaned_data['arxiv_identifier']).exists())): - errormessage = 'There already exists a Commentary Page on this preprint, see' - existing_commentary = get_object_or_404( - Commentary, - arxiv_identifier=form.cleaned_data['arxiv_identifier']) - elif (form.cleaned_data['pub_DOI'] and - Commentary.objects.filter(pub_DOI=form.cleaned_data['pub_DOI']).exists()): - errormessage = 'There already exists a Commentary Page on this publication, see' - existing_commentary = get_object_or_404(Commentary, pub_DOI=form.cleaned_data['pub_DOI']) - if errormessage: - doiform = DOIToQueryForm() - identifierform = IdentifierToQueryForm() - context = {'form': form, 'doiform': doiform, 'identifierform': identifierform, - 'errormessage': errormessage, - 'existing_commentary': existing_commentary} - return render(request, 'commentaries/request_commentary.html', context) - - # Otherwise we can create the Commentary - contributor = Contributor.objects.get(user=request.user) - commentary = Commentary ( - requested_by = contributor, - type = form.cleaned_data['type'], - discipline = form.cleaned_data['discipline'], - domain = form.cleaned_data['domain'], - subject_area = form.cleaned_data['subject_area'], - pub_title = form.cleaned_data['pub_title'], - arxiv_identifier = form.cleaned_data['arxiv_identifier'], - pub_DOI = form.cleaned_data['pub_DOI'], - metadata = form.cleaned_data['metadata'], - author_list = form.cleaned_data['author_list'], - journal = form.cleaned_data['journal'], - volume = form.cleaned_data['volume'], - pages = form.cleaned_data['pages'], - pub_date = form.cleaned_data['pub_date'], - pub_abstract = form.cleaned_data['pub_abstract'], - latest_activity = timezone.now(), - ) + commentary = form.save(commit=False) commentary.parse_links_into_urls() commentary.save() - + context = {'ack_header': 'Thank you for your request for a Commentary Page', 'ack_message': 'Your request will soon be handled by an Editor. ', 'followup_message': 'Return to your ', 'followup_link': reverse('scipost:personal_page'), 'followup_link_label': 'personal page'} return render(request, 'scipost/acknowledgement.html', context) - else: - form = RequestCommentaryForm() + + else: + doiform = DOIToQueryForm() + existing_commentary = form.get_existing_commentary() + identifierform = IdentifierToQueryForm() + context = {'form': form, 'doiform': doiform, 'identifierform': identifierform, + 'errormessage': form.errors, + 'existing_commentary': existing_commentary} + return render(request, 'commentaries/request_commentary.html', context) + doiform = DOIToQueryForm() identifierform = IdentifierToQueryForm() context = {'form': form, 'doiform': doiform, 'identifierform': identifierform} @@ -120,7 +82,7 @@ def prefill_using_DOI(request): 'errormessage': errormessage, 'existing_commentary': existing_commentary} return render(request, 'commentaries/request_commentary.html', context) - + # Otherwise we query Crossref for the information: try: queryurl = 'http://api.crossref.org/works/%s' % doiform.cleaned_data['doi'] @@ -133,12 +95,12 @@ def prefill_using_DOI(request): for author in doiqueryJSON['message']['author'][1:]: authorlist += ', ' + author['given'] + ' ' + author['family'] journal = doiqueryJSON['message']['container-title'][0] - + try: volume = doiqueryJSON['message']['volume'] except KeyError: volume = '' - + pages = '' try: pages = doiqueryJSON['message']['article-number'] # for Phys Rev @@ -148,7 +110,7 @@ def prefill_using_DOI(request): pages = doiqueryJSON['message']['page'] except KeyError: pass - + pub_date = '' try: pub_date = (str(doiqueryJSON['message']['issued']['date-parts'][0][0]) + '-' + @@ -209,7 +171,7 @@ def prefill_using_identifier(request): queryurl = ('http://export.arxiv.org/api/query?id_list=%s' % identifierform.cleaned_data['identifier']) arxivquery = feedparser.parse(queryurl) - + # If paper has been published, should comment on published version try: arxiv_journal_ref = arxivquery['entries'][0]['arxiv_journal_ref'] @@ -223,7 +185,7 @@ def prefill_using_identifier(request): + '. Please comment on the published version.') except (IndexError, KeyError): pass - + if errormessage: form = RequestCommentaryForm() doiform = DOIToQueryForm() @@ -231,7 +193,7 @@ def prefill_using_identifier(request): 'errormessage': errormessage, 'existing_commentary': existing_commentary} return render(request, 'commentaries/request_commentary.html', context) - + # otherwise prefill the form: metadata = arxivquery pub_title = arxivquery['entries'][0]['title'] @@ -265,7 +227,7 @@ def prefill_using_identifier(request): @permission_required('scipost.can_vet_commentary_requests', raise_exception=True) def vet_commentary_requests(request): contributor = Contributor.objects.get(user=request.user) - commentary_to_vet = Commentary.objects.filter(vetted=False).first() # only handle one at a time + commentary_to_vet = Commentary.objects.awaiting_vetting().first() # only handle one at a time form = VetCommentaryForm() context = {'contributor': contributor, 'commentary_to_vet': commentary_to_vet, 'form': form } return render(request, 'commentaries/vet_commentary_requests.html', context) @@ -348,62 +310,33 @@ def vet_commentary_request_ack(request, commentary_id): 'followup_link_label': 'Commentary requests page'} return render(request, 'scipost/acknowledgement.html', context) - def commentaries(request): - if request.method == 'POST': - form = CommentarySearchForm(request.POST) - if form.is_valid() and form.has_changed(): - commentary_search_list = Commentary.objects.filter( - pub_title__icontains=form.cleaned_data['pub_title_keyword'], - author_list__icontains=form.cleaned_data['pub_author'], - pub_abstract__icontains=form.cleaned_data['pub_abstract_keyword'], - vetted=True, - ) - commentary_search_list.order_by('-pub_date') - else: - commentary_search_list = [] - + """List and search all commentaries""" + form = CommentarySearchForm(request.POST or None) + if form.is_valid() and form.has_changed(): + commentary_search_list = form.search_results() else: - form = CommentarySearchForm() commentary_search_list = [] - comment_recent_list = (Comment.objects.filter(status='1') - .order_by('-date_submitted')[:10]) - - commentary_recent_list = (Commentary.objects.filter(vetted=True) - .order_by('-latest_activity')[:10]) - context = {'form': form, 'commentary_search_list': commentary_search_list, - 'comment_recent_list': comment_recent_list, - 'commentary_recent_list': commentary_recent_list } + comment_recent_list = Comment.objects.filter(status='1').order_by('-date_submitted')[:10] + commentary_recent_list = Commentary.objects.vetted().order_by('-latest_activity')[:10] + context = { + 'form': form, 'commentary_search_list': commentary_search_list, + 'comment_recent_list': comment_recent_list, + 'commentary_recent_list': commentary_recent_list} return render(request, 'commentaries/commentaries.html', context) - def browse(request, discipline, nrweeksback): - if request.method == 'POST': - form = CommentarySearchForm(request.POST) - if form.is_valid() and form.has_changed(): - commentary_search_list = Commentary.objects.filter( - pub_title__icontains=form.cleaned_data['pub_title_keyword'], - author_list__icontains=form.cleaned_data['pub_author'], - pub_abstract__icontains=form.cleaned_data['pub_abstract_keyword'], - vetted=True, - ) - commentary_search_list.order_by('-pub_date') - else: - commentary_search_list = [] - context = {'form': form, 'commentary_search_list': commentary_search_list} - return HttpResponseRedirect(request, 'commentaries/commentaries.html', context) - else: - form = CommentarySearchForm() - commentary_browse_list = Commentary.objects.filter( - vetted=True, discipline=discipline, - latest_activity__gte=timezone.now() + datetime.timedelta(weeks=-int(nrweeksback)) - ) - context = {'form': form, 'discipline': discipline, 'nrweeksback': nrweeksback, - 'commentary_browse_list': commentary_browse_list } + """List all commentaries for discipline and period""" + commentary_browse_list = Commentary.objects.vetted( + discipline=discipline, + latest_activity__gte=timezone.now() + datetime.timedelta(weeks=-int(nrweeksback))) + context = { + 'form': CommentarySearchForm(), + 'discipline': discipline, 'nrweeksback': nrweeksback, + 'commentary_browse_list': commentary_browse_list} return render(request, 'commentaries/commentaries.html', context) - def commentary_detail(request, arxiv_or_DOI_string): commentary = get_object_or_404(Commentary, arxiv_or_DOI_string=arxiv_or_DOI_string) comments = commentary.comment_set.all() diff --git a/journals/models.py b/journals/models.py index 7afb01756af51cc4ac083b00d0fbff342e9d2b27..80464164d6915e51439452e8d41f5739657fa91a 100644 --- a/journals/models.py +++ b/journals/models.py @@ -3,8 +3,8 @@ from django.db import models from django.template import Template, Context from django.utils import timezone -from scipost.models import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS, subject_areas_dict, TITLE_CHOICES -from scipost.models import ChoiceArrayField, Contributor +from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS, subject_areas_dict +from scipost.models import ChoiceArrayField, Contributor, TITLE_CHOICES class UnregisteredAuthor(models.Model): diff --git a/scipost/constants.py b/scipost/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..7c7933e18e787769f830f999423730a6af836d82 --- /dev/null +++ b/scipost/constants.py @@ -0,0 +1,120 @@ +SCIPOST_DISCIPLINES = ( + ('physics', 'Physics'), + ('astrophysics', 'Astrophysics'), + ('mathematics', 'Mathematics'), + ('computerscience', 'Computer Science'), + ) +disciplines_dict = dict(SCIPOST_DISCIPLINES) + +SCIPOST_SUBJECT_AREAS = ( + ('Physics', ( + ('Phys:AE', 'Atomic, Molecular and Optical Physics - Experiment'), + ('Phys:AT', 'Atomic, Molecular and Optical Physics - Theory'), + ('Phys:BI', 'Biophysics'), + ('Phys:CE', 'Condensed Matter Physics - Experiment'), + ('Phys:CT', 'Condensed Matter Physics - Theory'), + ('Phys:FD', 'Fluid Dynamics'), + ('Phys:GR', 'Gravitation, Cosmology and Astroparticle Physics'), + ('Phys:HE', 'High-Energy Physics - Experiment'), + ('Phys:HT', 'High-Energy Physics- Theory'), + ('Phys:HP', 'High-Energy Physics - Phenomenology'), + ('Phys:MP', 'Mathematical Physics'), + ('Phys:NE', 'Nuclear Physics - Experiment'), + ('Phys:NT', 'Nuclear Physics - Theory'), + ('Phys:QP', 'Quantum Physics'), + ('Phys:SM', 'Statistical and Soft Matter Physics'), + ) + ), + ('Astrophysics', ( + ('Astro:GA', 'Astrophysics of Galaxies'), + ('Astro:CO', 'Cosmology and Nongalactic Astrophysics'), + ('Astro:EP', 'Earth and Planetary Astrophysics'), + ('Astro:HE', 'High Energy Astrophysical Phenomena'), + ('Astro:IM', 'Instrumentation and Methods for Astrophysics'), + ('Astro:SR', 'Solar and Stellar Astrophysics'), + ) + ), + ('Mathematics', ( + ('Math:AG', 'Algebraic Geometry'), + ('Math:AT', 'Algebraic Topology'), + ('Math:AP', 'Analysis of PDEs'), + ('Math:CT', 'Category Theory'), + ('Math:CA', 'Classical Analysis and ODEs'), + ('Math:CO', 'Combinatorics'), + ('Math:AC', 'Commutative Algebra'), + ('Math:CV', 'Complex Variables'), + ('Math:DG', 'Differential Geometry'), + ('Math:DS', 'Dynamical Systems'), + ('Math:FA', 'Functional Analysis'), + ('Math:GM', 'General Mathematics'), + ('Math:GN', 'General Topology'), + ('Math:GT', 'Geometric Topology'), + ('Math:GR', 'Group Theory'), + ('Math:HO', 'History and Overview'), + ('Math:IT', 'Information Theory'), + ('Math:KT', 'K-Theory and Homology'), + ('Math:LO', 'Logic'), + ('Math:MP', 'Mathematical Physics'), + ('Math:MG', 'Metric Geometry'), + ('Math:NT', 'Number Theory'), + ('Math:NA', 'Numerical Analysis'), + ('Math:OA', 'Operator Algebras'), + ('Math:OC', 'Optimization and Control'), + ('Math:PR', 'Probability'), + ('Math:QA', 'Quantum Algebra'), + ('Math:RT', 'Representation Theory'), + ('Math:RA', 'Rings and Algebras'), + ('Math:SP', 'Spectral Theory'), + ('Math:ST', 'Statistics Theory'), + ('Math:SG', 'Symplectic Geometry'), + ) + ), + ('Computer Science', ( + ('Comp:AI', 'Artificial Intelligence'), + ('Comp:CC', 'Computational Complexity'), + ('Comp:CE', 'Computational Engineering, Finance, and Science'), + ('Comp:CG', 'Computational Geometry'), + ('Comp:GT', 'Computer Science and Game Theory'), + ('Comp:CV', 'Computer Vision and Pattern Recognition'), + ('Comp:CY', 'Computers and Society'), + ('Comp:CR', 'Cryptography and Security'), + ('Comp:DS', 'Data Structures and Algorithms'), + ('Comp:DB', 'Databases'), + ('Comp:DL', 'Digital Libraries'), + ('Comp:DM', 'Discrete Mathematics'), + ('Comp:DC', 'Distributed, Parallel, and Cluster Computing'), + ('Comp:ET', 'Emerging Technologies'), + ('Comp:FL', 'Formal Languages and Automata Theory'), + ('Comp:GL', 'General Literature'), + ('Comp:GR', 'Graphics'), + ('Comp:AR', 'Hardware Architecture'), + ('Comp:HC', 'Human-Computer Interaction'), + ('Comp:IR', 'Information Retrieval'), + ('Comp:IT', 'Information Theory'), + ('Comp:LG', 'Learning'), + ('Comp:LO', 'Logic in Computer Science'), + ('Comp:MS', 'Mathematical Software'), + ('Comp:MA', 'Multiagent Systems'), + ('Comp:MM', 'Multimedia'), + ('Comp:NI', 'Networking and Internet Architecture'), + ('Comp:NE', 'Neural and Evolutionary Computing'), + ('Comp:NA', 'Numerical Analysis'), + ('Comp:OS', 'Operating Systems'), + ('Comp:OH', 'Other Computer Science'), + ('Comp:PF', 'Performance'), + ('Comp:PL', 'Programming Languages'), + ('Comp:RO', 'Robotics'), + ('Comp:SI', 'Social and Information Networks'), + ('Comp:SE', 'Software Engineering'), + ('Comp:SD', 'Sound'), + ('Comp:SC', 'Symbolic Computation'), + ('Comp:SY', 'Systems and Control'), + ) + ), +) +subject_areas_raw_dict = dict(SCIPOST_SUBJECT_AREAS) + +# Make dict of the form {'Phys:AT': 'Atomic...', ...} +subject_areas_dict = {} +for k in subject_areas_raw_dict.keys(): + subject_areas_dict.update(dict(subject_areas_raw_dict[k])) diff --git a/scipost/forms.py b/scipost/forms.py index 3716ad8d83f4800b3c1e0eed0756279746f16bda..f8d0629bd7684d0ae9d2ee559d420167aadaf496 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -11,6 +11,7 @@ from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Div, Field, Fieldset, HTML, Submit from .models import * +from .constants import SCIPOST_DISCIPLINES from journals.models import Publication from submissions.models import SUBMISSION_STATUS_PUBLICLY_UNLISTED diff --git a/scipost/models.py b/scipost/models.py index 31e27a84a3986d012b3c6836443ac290619f855b..033c08ec50202e4cc88e200a4db4f8ee523a5be2 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -10,129 +10,10 @@ from django.utils.safestring import mark_safe from django_countries.fields import CountryField -from scipost.models import * - - -SCIPOST_DISCIPLINES = ( - ('physics', 'Physics'), - ('astrophysics', 'Astrophysics'), - ('mathematics', 'Mathematics'), - ('computerscience', 'Computer Science'), - ) -disciplines_dict = dict(SCIPOST_DISCIPLINES) - -SCIPOST_SUBJECT_AREAS = ( - ('Physics', ( - ('Phys:AE', 'Atomic, Molecular and Optical Physics - Experiment'), - ('Phys:AT', 'Atomic, Molecular and Optical Physics - Theory'), - ('Phys:BI', 'Biophysics'), - ('Phys:CE', 'Condensed Matter Physics - Experiment'), - ('Phys:CT', 'Condensed Matter Physics - Theory'), - ('Phys:FD', 'Fluid Dynamics'), - ('Phys:GR', 'Gravitation, Cosmology and Astroparticle Physics'), - ('Phys:HE', 'High-Energy Physics - Experiment'), - ('Phys:HT', 'High-Energy Physics- Theory'), - ('Phys:HP', 'High-Energy Physics - Phenomenology'), - ('Phys:MP', 'Mathematical Physics'), - ('Phys:NE', 'Nuclear Physics - Experiment'), - ('Phys:NT', 'Nuclear Physics - Theory'), - ('Phys:QP', 'Quantum Physics'), - ('Phys:SM', 'Statistical and Soft Matter Physics'), - ) - ), - ('Astrophysics', ( - ('Astro:GA', 'Astrophysics of Galaxies'), - ('Astro:CO', 'Cosmology and Nongalactic Astrophysics'), - ('Astro:EP', 'Earth and Planetary Astrophysics'), - ('Astro:HE', 'High Energy Astrophysical Phenomena'), - ('Astro:IM', 'Instrumentation and Methods for Astrophysics'), - ('Astro:SR', 'Solar and Stellar Astrophysics'), - ) - ), - ('Mathematics', ( - ('Math:AG', 'Algebraic Geometry'), - ('Math:AT', 'Algebraic Topology'), - ('Math:AP', 'Analysis of PDEs'), - ('Math:CT', 'Category Theory'), - ('Math:CA', 'Classical Analysis and ODEs'), - ('Math:CO', 'Combinatorics'), - ('Math:AC', 'Commutative Algebra'), - ('Math:CV', 'Complex Variables'), - ('Math:DG', 'Differential Geometry'), - ('Math:DS', 'Dynamical Systems'), - ('Math:FA', 'Functional Analysis'), - ('Math:GM', 'General Mathematics'), - ('Math:GN', 'General Topology'), - ('Math:GT', 'Geometric Topology'), - ('Math:GR', 'Group Theory'), - ('Math:HO', 'History and Overview'), - ('Math:IT', 'Information Theory'), - ('Math:KT', 'K-Theory and Homology'), - ('Math:LO', 'Logic'), - ('Math:MP', 'Mathematical Physics'), - ('Math:MG', 'Metric Geometry'), - ('Math:NT', 'Number Theory'), - ('Math:NA', 'Numerical Analysis'), - ('Math:OA', 'Operator Algebras'), - ('Math:OC', 'Optimization and Control'), - ('Math:PR', 'Probability'), - ('Math:QA', 'Quantum Algebra'), - ('Math:RT', 'Representation Theory'), - ('Math:RA', 'Rings and Algebras'), - ('Math:SP', 'Spectral Theory'), - ('Math:ST', 'Statistics Theory'), - ('Math:SG', 'Symplectic Geometry'), - ) - ), - ('Computer Science', ( - ('Comp:AI', 'Artificial Intelligence'), - ('Comp:CC', 'Computational Complexity'), - ('Comp:CE', 'Computational Engineering, Finance, and Science'), - ('Comp:CG', 'Computational Geometry'), - ('Comp:GT', 'Computer Science and Game Theory'), - ('Comp:CV', 'Computer Vision and Pattern Recognition'), - ('Comp:CY', 'Computers and Society'), - ('Comp:CR', 'Cryptography and Security'), - ('Comp:DS', 'Data Structures and Algorithms'), - ('Comp:DB', 'Databases'), - ('Comp:DL', 'Digital Libraries'), - ('Comp:DM', 'Discrete Mathematics'), - ('Comp:DC', 'Distributed, Parallel, and Cluster Computing'), - ('Comp:ET', 'Emerging Technologies'), - ('Comp:FL', 'Formal Languages and Automata Theory'), - ('Comp:GL', 'General Literature'), - ('Comp:GR', 'Graphics'), - ('Comp:AR', 'Hardware Architecture'), - ('Comp:HC', 'Human-Computer Interaction'), - ('Comp:IR', 'Information Retrieval'), - ('Comp:IT', 'Information Theory'), - ('Comp:LG', 'Learning'), - ('Comp:LO', 'Logic in Computer Science'), - ('Comp:MS', 'Mathematical Software'), - ('Comp:MA', 'Multiagent Systems'), - ('Comp:MM', 'Multimedia'), - ('Comp:NI', 'Networking and Internet Architecture'), - ('Comp:NE', 'Neural and Evolutionary Computing'), - ('Comp:NA', 'Numerical Analysis'), - ('Comp:OS', 'Operating Systems'), - ('Comp:OH', 'Other Computer Science'), - ('Comp:PF', 'Performance'), - ('Comp:PL', 'Programming Languages'), - ('Comp:RO', 'Robotics'), - ('Comp:SI', 'Social and Information Networks'), - ('Comp:SE', 'Software Engineering'), - ('Comp:SD', 'Sound'), - ('Comp:SC', 'Symbolic Computation'), - ('Comp:SY', 'Systems and Control'), - ) - ), -) -subject_areas_raw_dict = dict(SCIPOST_SUBJECT_AREAS) +from .constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS,\ + disciplines_dict, subject_areas_dict -# Make dict of the form {'Phys:AT': 'Atomic...', ...} -subject_areas_dict = {} -for k in subject_areas_raw_dict.keys(): - subject_areas_dict.update(dict(subject_areas_raw_dict[k])) +from scipost.models import * class ChoiceArrayField(ArrayField): @@ -179,6 +60,19 @@ TITLE_CHOICES = ( title_dict = dict(TITLE_CHOICES) +class TimeStampedModel(models.Model): + """ + All objects should inherit from this abstract model. + This will ensure the creation of created and modified + timestamps in the objects. + """ + created = models.DateTimeField(auto_now_add=True) + latest_activity = models.DateTimeField(auto_now=True) + + class Meta: + abstract = True + + class Contributor(models.Model): """ All users of SciPost are Contributors. diff --git a/submissions/models.py b/submissions/models.py index 68a3d1e867b2720f97fd109dee8cd1d8a18d681a..2ea5a6c80cecdb9cdbc796f30b55e1ec87b17d8a 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -8,8 +8,8 @@ from django.template import Template, Context from .models import * from scipost.models import ChoiceArrayField, Contributor, title_dict, Remark -from scipost.models import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS -from scipost.models import subject_areas_dict, TITLE_CHOICES +from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS, subject_areas_dict +from scipost.models import TITLE_CHOICES from journals.models import SCIPOST_JOURNALS_SUBMIT, SCIPOST_JOURNALS_DOMAINS from journals.models import SCIPOST_JOURNALS_SPECIALIZATIONS from journals.models import journals_submit_dict, journals_domains_dict, journals_spec_dict diff --git a/theses/migrations/0006_auto_20161219_2012.py b/theses/migrations/0006_auto_20161219_2012.py new file mode 100644 index 0000000000000000000000000000000000000000..935f906aacc44850699e1d60558ba9c3fd845f31 --- /dev/null +++ b/theses/migrations/0006_auto_20161219_2012.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-12-19 19:12 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('theses', '0005_remove_thesislink_specialization'), + ] + + operations = [ + migrations.AlterField( + model_name='thesislink', + name='domain', + field=models.CharField(choices=[('E', 'Experimental'), ('T', 'Theoretical'), ('C', 'Computational'), ('ET', 'Exp. & Theor.'), ('EC', 'Exp. & Comp.'), ('TC', 'Theor. & Comp.'), ('ETC', 'Exp., Theor. & Comp.')], max_length=3), + ), + ] diff --git a/theses/models.py b/theses/models.py index 139948b9175deb6bc62fdc098bef6d3fc4a4ef86..50ec6b1f47548e9f43d6f53d31d091883f4ed091 100644 --- a/theses/models.py +++ b/theses/models.py @@ -6,6 +6,7 @@ from django.template import Template, Context from .models import * from journals.models import * +from scipost.constants import SCIPOST_DISCIPLINES, subject_areas_dict, disciplines_dict from scipost.models import *