Commit a6298380 authored by cermak's avatar cermak
Browse files

Add proposal categories

parent 83b3f23e
......@@ -36,6 +36,19 @@ class StatusAdmin(admin.ModelAdmin):, StatusAdmin)
class ProposalCategoryAdminForm(forms.ModelForm):
class Meta:
model = ProposalCategory
fields = '__all__'
class ProposalCategoryAdmin(admin.ModelAdmin):
form = ProposalCategoryAdminForm
list_display = ['slug', 'name']
readonly_fields = ['slug'], ProposalCategoryAdmin)
class ProposalsAdminForm(forms.ModelForm):
class Meta:
model = Proposals
......@@ -44,7 +57,7 @@ class ProposalsAdminForm(forms.ModelForm):
class ProposalsAdmin(admin.ModelAdmin):
form = ProposalsAdminForm
list_display = ['pid', 'name', 'proposer', 'created', 'last_updated', 'grants', 'scientific_bg', 'supervisor', 'review_process']
list_display = ['pid', 'name', 'proposer', 'created', 'last_updated', 'grants', 'scientific_bg', 'supervisor', 'review_process', 'get_categories']
readonly_fields = ['slug', 'created', 'last_updated'], ProposalsAdmin)
......@@ -8,7 +8,7 @@ from django.contrib.auth.forms import AuthenticationForm, UserCreationForm, Pass
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from .models import Proposals, Instruments, Contacts, Affiliations, Countries, Options, SharedOptions
from .models import Samples, SamplePhotos, SampleRemarks, SharedOptionSlot
from .models import Samples, SamplePhotos, SampleRemarks, SharedOptionSlot, ProposalCategory
from .models import Publication, Experiments, Status, Report
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, HTML, Fieldset, Div, ButtonHolder, Field
......@@ -229,6 +229,11 @@ The panel report has two parts - visible and hidden to the user."""
class ProposalsForm(forms.ModelForm):
categories = forms.ModelMultipleChoiceField(
label='Proposal category',
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
......@@ -252,7 +257,7 @@ class ProposalsForm(forms.ModelForm):
self.helper.layout = Layout(
None, 'name', 'abstract', 'scientific_bg', 'proposaltype', 'student', 'thesis_topic', 'supervisor', 'grants', 'local_contacts', 'coproposers'
None, 'name', 'abstract', 'scientific_bg', 'proposaltype', 'student', 'thesis_topic', 'supervisor', 'grants', 'categories', 'local_contacts', 'coproposers'
Submit('submit', 'Save', css_class='button white'),
......@@ -275,6 +280,7 @@ class ProposalsForm(forms.ModelForm):
'data-theme': 'bootstrap4',
self.fields['grants'].help_text = "If the proposal is connected with any funding, add its abbreviation and/or number. In case of more fundings, separate by comma. Example: <i>'GAČR 19-000123S, ERC BoBEK 123456'</i>."
self.fields['categories'].help_text = "Optional proposal categories. Please tick all fields which fits for your proposal."
self.fields['student'].help_text = "Student proposals needs to mention supervisor and thesis topic."
self.fields['student'].widget.attrs['onclick'] = "javascript:toggleDivs();"
if not self.user.groups.filter(name='localcontacts').exists():
......@@ -306,7 +312,7 @@ class ProposalsForm(forms.ModelForm):
class Meta:
model = Proposals
fields = ['name', 'abstract', 'scientific_bg', 'proposaltype', 'student', 'supervisor', 'thesis_topic', 'local_contacts', 'grants', 'coproposers']
fields = ['name', 'abstract', 'scientific_bg', 'proposaltype', 'student', 'supervisor', 'thesis_topic', 'local_contacts', 'grants', 'categories', 'coproposers']
labels = {
"name": "Proposal name",
"proposaltype": "Type of proposal",
# Generated by Django 3.1.1 on 2020-09-11 12:20
from django.db import migrations, models
import django_extensions.db.fields
class Migration(migrations.Migration):
dependencies = [
('app', '0053_auto_20200609_1306'),
operations = [
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, null=True, populate_from='name')),
'verbose_name_plural': 'categories',
'ordering': ('name',),
field=models.ManyToManyField(blank=True, to='app.ProposalCategory'),
......@@ -115,6 +115,19 @@ def validate_pdf_lenth(value):
if pdf.getNumPages() > 5:
raise ValidationError(u'Uploaded file has too many pages. Maximum allowed is 5.')
class ProposalCategory(models.Model):
name = models.CharField(max_length=200)
slug = extension_fields.AutoSlugField(populate_from='name', blank=True, null=True)
class Meta:
ordering = ('name',)
verbose_name_plural = "categories"
def __str__(self):
return f'{}'
class Proposals(models.Model):
......@@ -146,6 +159,7 @@ class Proposals(models.Model):
# Relationship Fields
proposer = models.ForeignKey(User, null=True, blank=True, on_delete=models.PROTECT)
samples = models.ManyToManyField('app.Samples', blank=True)
categories = models.ManyToManyField('app.ProposalCategory', blank=True)
local_contacts = models.ManyToManyField('app.Contacts', related_name='proposal_local_contacts', blank=True)
reporter = models.ForeignKey('app.Contacts', related_name='proposal_reporter', on_delete=models.PROTECT, blank=True, null = True)
supervisor = models.ForeignKey('app.Contacts', related_name='proposal_supervisor', on_delete=models.PROTECT, blank=True, null = True)
......@@ -286,6 +300,9 @@ class Proposals(models.Model):
def __str__(self):
return '%s %s' % (,
def get_categories(self):
return ", ".join([ for c in self.categories.all()])
def default_report_time():
return + timedelta(days=60)
# app/
import django_tables2 as tables
from .models import Proposals, Contacts, User, Status, Instruments, Experiments, Log, Resource
from django_tables2.utils import A # alias for Accessor
from django_tables2.utils import A # alias for Accessor
import django_filters
from django.db.models import Q
from django.db.models import Q, Count, Max
from django.utils.timezone import now
from datetime import timedelta, date
......@@ -18,6 +18,7 @@ class TruncatedTextColumn(tables.Column):
class ProposalTable(tables.Table):
local_contacts_short = TruncatedTextColumn(verbose_name= 'Local contact', orderable = False)
get_categories = tables.Column(verbose_name= 'Categories') #, order_by = A('categories__count')
name = TruncatedTextColumn(linkify=True)
proposaltype = tables.Column(verbose_name='Type')
supervisor = tables.Column(empty_values=[])
......@@ -30,6 +31,13 @@ class ProposalTable(tables.Table):
# self.Meta.exclude.pop("reporter")
super().__init__(*args, **kwargs)
def order_get_categories(self, queryset, is_descending):
queryset = queryset.annotate(
).order_by(("-" if is_descending else "") + "countcat", ("-" if is_descending else "") + "firstcat")
return (queryset, True)
def before_render(self, request):
if request.user.has_perm('app.approve_panel') or request.user.has_perm('app.approve_board'):'reporter')
......@@ -27,6 +27,8 @@
<tr><td>Proposer</td><td><b>{{ }}</b><br />{{ }}</td></tr>
<tr><td>Student</td><td>{% if object.student %}Yes, supervisor {{object.supervisor}}{% else %}No{% endif %}</td></tr>
<tr><td>Local Contacts</td><td>{% for p in object.local_contacts.all %}{{ }}{%if not forloop.last%}, {%endif%}{% endfor %}</td></tr>
<tr><td>Grants</td><td>{% if object.grants %}{{ object.grants }}{% else %}-{% endif %}</td></tr>
<tr><td>Categories</td><td>{{ object.get_categories }}</td></tr>
{% if object.reporter and %}<tr><td>Reporter</td><td>{{ object.reporter }}</td></tr>{% endif %}
<tr><td>Experimental team</td><td>{% for p in object.coproposers.all %}{{ }}{%if not forloop.last%}, {%endif%}{% endfor %}</td></tr>
<tr><td>Experimental reports</td><td>{% for p in object.report_set.all %}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment