Remaster (#38)
* setup new 'statistiques' module * added 'graphos' package and created first test graph * put graphos in requirements, deleted local folder * added "load_csv" management command ! * added update of premiere_rencontre field in 'load_csv' management command * added missing urls.py file * added 'merge' action and view * added 'info_completed' ratio * linked sujets:merge views inside suivi:details * added link to maraudes:details in notes table headers, if any * Major reorganisation, moved 'suivi' and 'sujets' to 'notes', cleanup in 'maraudes', dropping 'website' mixins (mostly useless) * small cleanup * worked on Maraude and Sujet lists * corrected missing line in notes.__init__ * restored 'details' view for maraudes and sujets insie 'notes' module * worked on 'notes': added navigation between maraude's compte-rendu, right content in details, header to list tables * changed queryset for CompteRenduDetailsView to all notes of same date, minor layout changes * added right content to 'details-sujet', created 'statistiques' view and update templates * restored 'statistiques' ajax view in 'details-sujet', fixed 'merge_two' util function * added auto-creation of FicheStatistique (plus some tests), pagination for notes in 'details-sujet' * added error-prone cases in paginator * fixed non-working modals, added titles * added UpdateStatistiques capacity in CompteRenduCreate view * fixed missing AjaxTemplateMixin for CreateSujetView, worked on compte-rendu creation scripts * fixed MaraudeManager.all_of() for common Maraudeurs, added color hints in planning * re-instated statistiques module link and first test page * added FinalizeView to send a mail before finalizing compte-rendu * Added PieChart view for FicheStatistique fields * small style updates, added 'age' and 'genre' fields from sujets in statistiques.PieChartView * worked on statistiques, fixed small issues in 'notes' list views * small theme change * removed some dead code * fixed notes.tests, fixed statistiques.info_completed display, added filter in SujetLisView * added some tests * added customised admin templates * added authenticate in CustomAuthenticatationBackend, more verbose login thanks to messages * added django-nose for test coverage * Corrected raising exception on first migration On first migration, qs.exists() would previously be called and raising an Exception, sot he migrations would fail. * Better try block * cleaned up custom settings.py, added some overrides of django base_settings * corrected bad dictionnary key
This commit is contained in:
254
notes/views.py
Normal file
254
notes/views.py
Normal file
@@ -0,0 +1,254 @@
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
from django.shortcuts import redirect, reverse
|
||||
from django.views import generic
|
||||
from django.utils import timezone
|
||||
from django.contrib import messages
|
||||
from django.http.response import HttpResponseNotAllowed
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
|
||||
from utilisateurs.mixins import MaraudeurMixin
|
||||
from maraudes.models import Maraude, CompteRendu
|
||||
from .models import Sujet, Note
|
||||
from .forms import SujetCreateForm, AutoNoteForm, SelectSujetForm
|
||||
from .mixins import NoteFormMixin
|
||||
from .actions import merge_two
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
# Create your views here.
|
||||
|
||||
|
||||
|
||||
class IndexView(MaraudeurMixin, generic.TemplateView):
|
||||
template_name = "notes/index.html"
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return redirect("notes:liste-sujet")
|
||||
|
||||
class Filter:
|
||||
def __init__(self, title, name, filter_func):
|
||||
self.title = title
|
||||
self.parameter_name = name
|
||||
self.active = False
|
||||
self._filter_func = filter_func
|
||||
|
||||
def filter(self, qs):
|
||||
return self._filter_func(qs)
|
||||
|
||||
|
||||
|
||||
class ListView(MaraudeurMixin, generic.ListView):
|
||||
""" Base ListView for Maraude and Sujet lists """
|
||||
paginate_by = 30
|
||||
cell_template = None
|
||||
|
||||
filters = []
|
||||
active_filter = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._filters = {}
|
||||
|
||||
if self.filters:
|
||||
for i, (title, func) in enumerate(self.filters):
|
||||
_id = "filter_%i" % i
|
||||
self._filters[_id] = Filter(title, _id, func)
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
filter_name = self.request.GET.get('filter', None)
|
||||
if filter_name:
|
||||
self.active_filter = self._filters.get(filter_name, None)
|
||||
if self.active_filter:
|
||||
self.active_filter.active = True
|
||||
|
||||
return super().get(request, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
if self.active_filter:
|
||||
qs = self.active_filter.filter(qs)
|
||||
return qs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["filters"] = self._filters.values()
|
||||
context["table_cell_template"] = getattr(self, 'cell_template', None)
|
||||
context["table_header"] = getattr(self, 'table_header', None)
|
||||
return context
|
||||
|
||||
|
||||
class MaraudeListView(ListView):
|
||||
""" Vue de la liste des compte-rendus de maraude """
|
||||
|
||||
model = CompteRendu
|
||||
template_name = "notes/liste_maraudes.html"
|
||||
cell_template = "notes/table_cell_maraudes.html"
|
||||
table_header = "Liste des maraudes"
|
||||
|
||||
queryset = Maraude.objects.get_past().order_by("-date")
|
||||
|
||||
filters = [
|
||||
("Ce mois-ci", lambda qs: qs.filter(date__month=timezone.now().date().month)),
|
||||
]
|
||||
|
||||
|
||||
class SujetListView(ListView):
|
||||
#ListView
|
||||
model = Sujet
|
||||
template_name = "notes/liste_sujets.html"
|
||||
cell_template = "notes/table_cell_sujets.html"
|
||||
table_header = "Liste des sujets"
|
||||
|
||||
|
||||
def info_completed_filter(qs):
|
||||
excluded_set = set()
|
||||
for sujet in qs:
|
||||
if sujet.statistiques.info_completed >= 50:
|
||||
excluded_set.add(sujet.pk)
|
||||
|
||||
return qs.exclude(pk__in=excluded_set)
|
||||
|
||||
filters = [
|
||||
("Rencontré(e)s cette année", lambda qs: qs.filter(premiere_rencontre__year=timezone.now().date().year)),
|
||||
("Statistiques incomplètes", info_completed_filter),
|
||||
]
|
||||
|
||||
def post(self, request, **kwargs):
|
||||
from watson import search as watson
|
||||
search_text = request.POST.get('q')
|
||||
results = watson.filter(Sujet, search_text)
|
||||
#logger.warning("SEARCH for %s : %s" % (search_text, results))
|
||||
if results.count() == 1:
|
||||
return redirect(results[0].get_absolute_url())
|
||||
self.queryset = results
|
||||
return self.get(request, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['query_text'] = self.request.POST.get('q', None)
|
||||
return context
|
||||
|
||||
|
||||
class DetailView(MaraudeurMixin, generic.DetailView):
|
||||
template_name = "notes/details.html"
|
||||
|
||||
class CompteRenduDetailsView(DetailView):
|
||||
""" Vue détaillé d'un compte-rendu de maraude """
|
||||
|
||||
model = CompteRendu
|
||||
context_object_name = "maraude"
|
||||
template_name = "notes/details_maraude.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['notes'] = sorted(Note.objects.get_queryset().filter(created_date=self.object.date), key=lambda n: n.created_time)
|
||||
context['next_maraude'] = Maraude.objects.get_future(
|
||||
date=self.object.date + datetime.timedelta(1)
|
||||
).filter(
|
||||
heure_fin__isnull=False
|
||||
).first()
|
||||
context['prev_maraude'] = Maraude.objects.get_past(
|
||||
date=self.object.date
|
||||
).filter(
|
||||
heure_fin__isnull=False
|
||||
).last()
|
||||
return context
|
||||
|
||||
|
||||
class SuiviSujetView(NoteFormMixin, DetailView):
|
||||
#NoteFormMixin
|
||||
forms = {
|
||||
'note': AutoNoteForm,
|
||||
}
|
||||
def get_success_url(self):
|
||||
return reverse('notes:details-sujet', kwargs={'pk': self.get_object().pk})
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['sujet'] = self.get_object()
|
||||
return kwargs
|
||||
#DetailView
|
||||
model = Sujet
|
||||
template_name = "notes/details_sujet.html"
|
||||
context_object_name = "sujet"
|
||||
# Paginator
|
||||
per_page = 5
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
self.paginator = Paginator(
|
||||
self.get_object().notes.by_date(reverse=True),
|
||||
self.per_page
|
||||
)
|
||||
self.page = self.request.GET.get('page', 1)
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
try:
|
||||
notes = self.paginator.page(self.page)
|
||||
except PageNotAnInteger:
|
||||
notes = self.paginator.page(1)
|
||||
except EmptyPage:
|
||||
notes = self.paginator.page(self.paginator.num_pages)
|
||||
context['notes'] = notes
|
||||
return context
|
||||
|
||||
|
||||
### Sujet Management Views
|
||||
|
||||
class SujetAjaxDetailsView(generic.DetailView):
|
||||
#DetailView
|
||||
template_name = "notes/details_sujet_inner.html"
|
||||
model = Sujet
|
||||
|
||||
http_method_names = ["get"]
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
""" Redirect to complete details view if request is not ajax """
|
||||
if not self.request.is_ajax():
|
||||
return redirect("notes:details-sujet", pk=self.get_object().pk)
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
class SujetAjaxUpdateView(generic.edit.UpdateView):
|
||||
#UpdateView
|
||||
template_name = "notes/details_sujet_update.html"
|
||||
model = Sujet
|
||||
fields = '__all__'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("notes:details-sujet", kwargs={'pk': self.object.pk})
|
||||
|
||||
from website.mixins import AjaxTemplateMixin
|
||||
|
||||
class SujetCreateView(AjaxTemplateMixin, generic.edit.CreateView):
|
||||
#CreateView
|
||||
template_name = "notes/sujet_create.html"
|
||||
form_class = SujetCreateForm
|
||||
def post(self, request, *args, **kwargs):
|
||||
if 'next' in self.request.POST:
|
||||
self.success_url = self.request.POST["next"]
|
||||
return super().post(self, request, *args, **kwargs)
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
try: context['next'] = self.request.GET['next']
|
||||
except:context['next'] = None
|
||||
return context
|
||||
|
||||
class MergeView(generic.DetailView, generic.FormView):
|
||||
""" Implement actions.merge_two as a view """
|
||||
|
||||
template_name = "notes/sujet_merge.html"
|
||||
model = Sujet
|
||||
form_class = SelectSujetForm
|
||||
|
||||
def form_valid(self, form):
|
||||
slave = self.get_object()
|
||||
master = form.cleaned_data['sujet']
|
||||
try:
|
||||
merge_two(master, slave)
|
||||
except Exception as e:
|
||||
logger.error("Merge: ", e)
|
||||
messages.error(self.request, "La fusion vers %s a échoué !" % master)
|
||||
return redirect(slave)
|
||||
messages.success(self.request, "%s vient d'être fusionné" % slave)
|
||||
return redirect(master)
|
||||
Reference in New Issue
Block a user