diff --git a/apps/participation/forms.py b/apps/participation/forms.py index 45839d9..3d0d829 100644 --- a/apps/participation/forms.py +++ b/apps/participation/forms.py @@ -1,10 +1,11 @@ import re +from bootstrap_datepicker_plus import DateTimePickerInput from django import forms from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ -from .models import Participation, Team, Video +from .models import Participation, Phase, Team, Video class TeamForm(forms.ModelForm): @@ -72,3 +73,13 @@ class UploadVideoForm(forms.ModelForm): class Meta: model = Video fields = ('link',) + + +class PhaseForm(forms.ModelForm): + class Meta: + model = Phase + fields = ('start', 'end',) + widgets = { + 'start': DateTimePickerInput(), + 'end': DateTimePickerInput(), + } diff --git a/apps/participation/templates/participation/phase_form.html b/apps/participation/templates/participation/phase_form.html new file mode 100644 index 0000000..a5bb901 --- /dev/null +++ b/apps/participation/templates/participation/phase_form.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% load crispy_forms_filters i18n %} + +{% block content %} +
+
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+{% endblock content %} + diff --git a/apps/participation/urls.py b/apps/participation/urls.py index 121dc75..54efc74 100644 --- a/apps/participation/urls.py +++ b/apps/participation/urls.py @@ -1,7 +1,7 @@ from django.urls import path from .views import CalendarView, CreateTeamView, JoinTeamView, MyParticipationDetailView, MyTeamDetailView, \ - ParticipationDetailView,TeamAuthorizationsView, TeamDetailView, TeamUpdateView, UploadVideoView + ParticipationDetailView, PhaseUpdateView, TeamAuthorizationsView, TeamDetailView, TeamUpdateView, UploadVideoView app_name = "participation" @@ -17,4 +17,5 @@ urlpatterns = [ path("detail//", ParticipationDetailView.as_view(), name="participation_detail"), path("detail/upload-video//", UploadVideoView.as_view(), name="upload_video"), path("calendar/", CalendarView.as_view(), name="calendar"), + path("calendar//", PhaseUpdateView.as_view(), name="update_phase"), ] diff --git a/apps/participation/views.py b/apps/participation/views.py index d777916..97b18ef 100644 --- a/apps/participation/views.py +++ b/apps/participation/views.py @@ -3,6 +3,7 @@ import os from zipfile import ZipFile from corres2math.lists import get_sympa_client +from corres2math.views import AdminMixin from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied from django.core.mail import send_mail @@ -17,7 +18,7 @@ from django_tables2 import SingleTableView from magic import Magic from registration.models import AdminRegistration -from .forms import JoinTeamForm, ParticipationForm, RequestValidationForm, TeamForm, UploadVideoForm,\ +from .forms import JoinTeamForm, ParticipationForm, PhaseForm, RequestValidationForm, TeamForm, UploadVideoForm,\ ValidateParticipationForm from .models import Participation, Phase, Team, Video from .tables import CalendarTable @@ -281,3 +282,8 @@ class UploadVideoView(LoginRequiredMixin, UpdateView): class CalendarView(SingleTableView): table_class = CalendarTable model = Phase + + +class PhaseUpdateView(AdminMixin, UpdateView): + model = Phase + form_class = PhaseForm diff --git a/corres2math/inputs.py b/corres2math/inputs.py deleted file mode 100644 index 67838fc..0000000 --- a/corres2math/inputs.py +++ /dev/null @@ -1,322 +0,0 @@ -from json import dumps as json_dumps - -from django.forms.widgets import DateTimeBaseInput, NumberInput, TextInput, Widget - - -class AmountInput(NumberInput): - """ - This input type lets the user type amounts in euros, but forms receive data in cents - """ - template_name = "amount_input.html" - - def format_value(self, value): - return None if value is None or value == "" else "{:.02f}".format(int(value) / 100, ) - - def value_from_datadict(self, data, files, name): - val = super().value_from_datadict(data, files, name) - return str(int(100 * float(val))) if val else val - - -class Autocomplete(TextInput): - template_name = "autocomplete_model.html" - - def __init__(self, model, attrs=None): - super().__init__(attrs) - - self.model = model - self.model_pk = None - - class Media: - """JS/CSS resources needed to render the date-picker calendar.""" - - js = ('js/autocomplete_model.js', ) - - def format_value(self, value): - if value: - self.attrs["model_pk"] = int(value) - return str(self.model.objects.get(pk=int(value))) - return "" - - -class ColorWidget(Widget): - """ - Pulled from django-colorfield. - Select a color. - """ - template_name = 'colorfield/color.html' - - class Media: - js = [ - 'colorfield/jscolor/jscolor.min.js', - 'colorfield/colorfield.js', - ] - - def format_value(self, value): - if value is None: - value = 0xFFFFFF - return "#{:06X}".format(value) - - def value_from_datadict(self, data, files, name): - val = super().value_from_datadict(data, files, name) - return int(val[1:], 16) - - -""" -The remaining of this file comes from the project `django-bootstrap-datepicker-plus` available on Github: -https://github.com/monim67/django-bootstrap-datepicker-plus -This is distributed under Apache License 2.0. - -This adds datetime pickers with bootstrap. -""" - -"""Contains Base Date-Picker input class for widgets of this package.""" - - -class DatePickerDictionary: - """Keeps track of all date-picker input classes.""" - - _i = 0 - items = dict() - - @classmethod - def generate_id(cls): - """Return a unique ID for each date-picker input class.""" - cls._i += 1 - return 'dp_%s' % cls._i - - -class BasePickerInput(DateTimeBaseInput): - """Base Date-Picker input class for widgets of this package.""" - - template_name = 'bootstrap_datepicker_plus/date_picker.html' - picker_type = 'DATE' - format = '%Y-%m-%d' - config = {} - _default_config = { - 'id': None, - 'picker_type': None, - 'linked_to': None, - 'options': {} # final merged options - } - options = {} # options extended by user - options_param = {} # options passed as parameter - _default_options = { - 'showClose': True, - 'showClear': True, - 'showTodayButton': True, - "locale": "fr", - } - - # source: https://github.com/tutorcruncher/django-bootstrap3-datetimepicker - # file: /blob/31fbb09/bootstrap3_datetime/widgets.py#L33 - format_map = ( - ('DDD', r'%j'), - ('DD', r'%d'), - ('MMMM', r'%B'), - ('MMM', r'%b'), - ('MM', r'%m'), - ('YYYY', r'%Y'), - ('YY', r'%y'), - ('HH', r'%H'), - ('hh', r'%I'), - ('mm', r'%M'), - ('ss', r'%S'), - ('a', r'%p'), - ('ZZ', r'%z'), - ) - - class Media: - """JS/CSS resources needed to render the date-picker calendar.""" - - js = ( - 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/' - 'moment-with-locales.min.js', - 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/' - '4.17.47/js/bootstrap-datetimepicker.min.js', - 'bootstrap_datepicker_plus/js/datepicker-widget.js' - ) - css = {'all': ( - 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/' - '4.17.47/css/bootstrap-datetimepicker.css', - 'bootstrap_datepicker_plus/css/datepicker-widget.css' - ), } - - @classmethod - def format_py2js(cls, datetime_format): - """Convert python datetime format to moment datetime format.""" - for js_format, py_format in cls.format_map: - datetime_format = datetime_format.replace(py_format, js_format) - return datetime_format - - @classmethod - def format_js2py(cls, datetime_format): - """Convert moment datetime format to python datetime format.""" - for js_format, py_format in cls.format_map: - datetime_format = datetime_format.replace(js_format, py_format) - return datetime_format - - def __init__(self, attrs=None, format=None, options=None): - """Initialize the Date-picker widget.""" - self.format_param = format - self.options_param = options if options else {} - self.config = self._default_config.copy() - self.config['id'] = DatePickerDictionary.generate_id() - self.config['picker_type'] = self.picker_type - self.config['options'] = self._calculate_options() - attrs = attrs if attrs else {} - if 'class' not in attrs: - attrs['class'] = 'form-control' - super().__init__(attrs, self._calculate_format()) - - def _calculate_options(self): - """Calculate and Return the options.""" - _options = self._default_options.copy() - _options.update(self.options) - if self.options_param: - _options.update(self.options_param) - return _options - - def _calculate_format(self): - """Calculate and Return the datetime format.""" - _format = self.format_param if self.format_param else self.format - if self.config['options'].get('format'): - _format = self.format_js2py(self.config['options'].get('format')) - else: - self.config['options']['format'] = self.format_py2js(_format) - return _format - - def get_context(self, name, value, attrs): - """Return widget context dictionary.""" - context = super().get_context( - name, value, attrs) - context['widget']['attrs']['dp_config'] = json_dumps(self.config) - return context - - def start_of(self, event_id): - """ - Set Date-Picker as the start-date of a date-range. - - Args: - - event_id (string): User-defined unique id for linking two fields - """ - DatePickerDictionary.items[str(event_id)] = self - return self - - def end_of(self, event_id, import_options=True): - """ - Set Date-Picker as the end-date of a date-range. - - Args: - - event_id (string): User-defined unique id for linking two fields - - import_options (bool): inherit options from start-date input, - default: TRUE - """ - event_id = str(event_id) - if event_id in DatePickerDictionary.items: - linked_picker = DatePickerDictionary.items[event_id] - self.config['linked_to'] = linked_picker.config['id'] - if import_options: - backup_moment_format = self.config['options']['format'] - self.config['options'].update(linked_picker.config['options']) - self.config['options'].update(self.options_param) - if self.format_param or 'format' in self.options_param: - self.config['options']['format'] = backup_moment_format - else: - self.format = linked_picker.format - # Setting useCurrent is necessary, see following issue - # https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1075 - self.config['options']['useCurrent'] = False - self._link_to(linked_picker) - else: - raise KeyError( - 'start-date not specified for event_id "%s"' % event_id) - return self - - def _link_to(self, linked_picker): - """ - Executed when two date-inputs are linked together. - - This method for sub-classes to override to customize the linking. - """ - pass - - -class DatePickerInput(BasePickerInput): - """ - Widget to display a Date-Picker Calendar on a DateField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'DATE' - format = '%Y-%m-%d' - format_key = 'DATE_INPUT_FORMATS' - - -class TimePickerInput(BasePickerInput): - """ - Widget to display a Time-Picker Calendar on a TimeField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'TIME' - format = '%H:%M' - format_key = 'TIME_INPUT_FORMATS' - template_name = 'bootstrap_datepicker_plus/time_picker.html' - - -class DateTimePickerInput(BasePickerInput): - """ - Widget to display a DateTime-Picker Calendar on a DateTimeField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'DATETIME' - format = '%Y-%m-%d %H:%M' - format_key = 'DATETIME_INPUT_FORMATS' - - -class MonthPickerInput(BasePickerInput): - """ - Widget to display a Month-Picker Calendar on a DateField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'MONTH' - format = '01/%m/%Y' - format_key = 'DATE_INPUT_FORMATS' - - -class YearPickerInput(BasePickerInput): - """ - Widget to display a Year-Picker Calendar on a DateField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'YEAR' - format = '01/01/%Y' - format_key = 'DATE_INPUT_FORMATS' - - def _link_to(self, linked_picker): - """Customize the options when linked with other date-time input""" - yformat = self.config['options']['format'].replace('-01-01', '-12-31') - self.config['options']['format'] = yformat diff --git a/corres2math/views.py b/corres2math/views.py index 7ee3d7c..c777822 100644 --- a/corres2math/views.py +++ b/corres2math/views.py @@ -1,13 +1,16 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied -from django.utils.translation import gettext_lazy as _ from haystack.generic_views import SearchView -class AdminSearchView(LoginRequiredMixin, SearchView): +class AdminMixin(LoginRequiredMixin): def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: return self.handle_no_permission() if not request.user.registration.is_admin: - raise PermissionDenied(_("Only administrators are allowed to perform a full research.")) + raise PermissionDenied return super().dispatch(request, *args, **kwargs) + + +class AdminSearchView(AdminMixin, SearchView): + pass