Add phase update page

This commit is contained in:
Yohann D'ANELLO 2020-10-20 13:14:02 +02:00
parent 168bfc83c9
commit 2d467ef3af
6 changed files with 41 additions and 328 deletions

View File

@ -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(),
}

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load crispy_forms_filters i18n %}
{% block content %}
<form method="post">
<div id="form-content">
{% csrf_token %}
{{ form|crispy }}
</div>
<button class="btn btn-primary" type="submit">{% trans "Update" %}</button>
</form>
{% endblock content %}

View File

@ -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/<int:pk>/", ParticipationDetailView.as_view(), name="participation_detail"),
path("detail/upload-video/<int:pk>/", UploadVideoView.as_view(), name="upload_video"),
path("calendar/", CalendarView.as_view(), name="calendar"),
path("calendar/<int:pk>/", PhaseUpdateView.as_view(), name="update_phase"),
]

View File

@ -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

View File

@ -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

View File

@ -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