Files
django-maraudes/notes/views.py
artus40 be087464fc 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
2017-06-11 17:16:17 +02:00

255 lines
8.2 KiB
Python

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)