diff --git a/notes/actions.py b/notes/actions.py
index 7d99369..1d44e4c 100644
--- a/notes/actions.py
+++ b/notes/actions.py
@@ -1,5 +1,4 @@
-from .models import Sujet
-from statistiques.models import FicheStatistique, NSP
+from statistiques.models import NSP
def merge_stats(main, merged):
@@ -21,17 +20,18 @@ def merge_stats(main, merged):
for field in ('prob_psychiatrie', 'prob_somatique',
'prob_administratif', 'prob_addiction',
'connu_siao', 'lien_familial'):
- if not getattr(main.statistiques, field): # Ignore if already filled
+ if not getattr(main.statistiques, field): # Ignore if already filled
setattr(main.statistiques, field, getattr(merged.statistiques, field, None))
# Choice fields, None is NSP
for field in ('habitation', 'ressources', 'parcours_de_vie'):
- if getattr(main.statistiques, field) == NSP: # Ignore if already filled
+ if getattr(main.statistiques, field) == NSP: # Ignore if already filled
setattr(main.statistiques, field, getattr(merged.statistiques, field, NSP))
+
def merge_two(main, merged):
""" Merge 'merged' sujet into 'main' one """
- merge_stats(main, merged) # Merge statistics and informations
- for note in merged.notes.all(): # Move all notes
+ merge_stats(main, merged) # Merge statistics and informations
+ for note in merged.notes.all(): # Move all notes
note.sujet = main
note.save()
main.save()
diff --git a/notes/apps.py b/notes/apps.py
index a92cd8b..1941e64 100644
--- a/notes/apps.py
+++ b/notes/apps.py
@@ -1,9 +1,10 @@
from django.apps import AppConfig
from watson import search as watson
+
class NotesConfig(AppConfig):
name = 'notes'
def ready(self):
- Sujet = self.get_model("Sujet")
- watson.register(Sujet, fields=('nom', 'prenom', 'surnom'))
+ sujet_model = self.get_model("Sujet")
+ watson.register(sujet_model, fields=('nom', 'prenom', 'surnom'))
diff --git a/notes/forms.py b/notes/forms.py
index 494da59..f9b9890 100644
--- a/notes/forms.py
+++ b/notes/forms.py
@@ -6,8 +6,8 @@ from utilisateurs.models import Professionnel
from django import forms
from django_select2.forms import Select2Widget
-### NOTES
+# NOTES
class NoteForm(forms.ModelForm):
""" Generic Note form """
class Meta:
@@ -15,11 +15,12 @@ class NoteForm(forms.ModelForm):
fields = ['sujet', 'text', 'created_by', 'created_date', 'created_time']
widgets = {
'sujet': Select2Widget(),
- 'text': forms.Textarea(attrs={'rows':4}),
+ 'text': forms.Textarea(
+ attrs={'rows': 4}
+ ),
}
-
class SimpleNoteForm(forms.ModelForm):
""" Simple note with only 'sujet' and 'text' fields.
@@ -30,7 +31,6 @@ class SimpleNoteForm(forms.ModelForm):
fields = ['sujet', 'text']
-
class UserNoteForm(NoteForm):
""" Form that sets 'created_by' with current user id.
@@ -56,7 +56,6 @@ class UserNoteForm(NoteForm):
return instance
-
class AutoNoteForm(UserNoteForm):
class Meta(UserNoteForm.Meta):
fields = ['text']
@@ -73,21 +72,21 @@ class AutoNoteForm(UserNoteForm):
return inst
-
-### SUJETS
-
+# SUJETS
current_year = datetime.date.today().year
YEAR_CHOICE = tuple(year - 2 for year in range(current_year, current_year + 10))
+
class SujetCreateForm(forms.ModelForm):
class Meta:
model = Sujet
fields = ['nom', 'surnom', 'prenom', 'genre', 'premiere_rencontre']
- widgets = {
- 'premiere_rencontre': forms.SelectDateWidget( empty_label=("Année", "Mois", "Jour"),
- years = YEAR_CHOICE,
- ),
- }
+ widgets = {'premiere_rencontre': forms.SelectDateWidget(
+ empty_label=("Année", "Mois", "Jour"),
+ years=YEAR_CHOICE,
+ ),
+ }
+
class SelectSujetForm(forms.Form):
diff --git a/notes/managers.py b/notes/managers.py
index db76b63..02e512a 100644
--- a/notes/managers.py
+++ b/notes/managers.py
@@ -5,10 +5,7 @@ from django.db.models.query import QuerySet
class NoteQuerySet(QuerySet):
def _ordered_by_field(self, field, reverse=False):
- return self.order_by( '%s%s' % ( "-" if reverse else "",
- field
- )
- )
+ return self.order_by('%s%s' % ("-" if reverse else "", field))
def by_date(self, reverse=False):
return self._ordered_by_field('created_date', reverse=reverse)
@@ -16,6 +13,7 @@ class NoteQuerySet(QuerySet):
def by_time(self, reverse=False):
return self._ordered_by_field('created_time', reverse=reverse)
+
class NoteManager(Manager):
def get_queryset(self):
@@ -28,5 +26,3 @@ class NoteManager(Manager):
def by_time(self, **kwargs):
return self.get_queryset().by_time(**kwargs)
-
-
diff --git a/notes/mixins.py b/notes/mixins.py
index 678cf2d..81b83f5 100644
--- a/notes/mixins.py
+++ b/notes/mixins.py
@@ -1,34 +1,39 @@
from django.views.generic.edit import FormMixin
from django.contrib import messages
-from .forms import *
class NoteFormMixin(FormMixin):
+ """ A mixin that allows to easily embed multiple distinct forms on a view.
+ Only one form can be processed by request !
+ """
+ # 'forms' shall be a dict of form classes, indexed by a prefix
forms = None
- def get_form(self, prefix):
- kwargs = self.get_form_kwargs()
- kwargs['prefix'] = prefix
+ def get_form_kwargs(self):
+ kwargs = super().get_form_kwargs()
kwargs['request'] = self.request
- form_class = self.forms[prefix]
- return form_class(**kwargs)
+ return kwargs
+
+ def get_form_by_prefix(self, prefix):
+ try:
+ return self.get_form(form_class=self.forms[prefix])
+ except KeyError:
+ raise ValueError("The form with prefix %s is not declared in %s" % (prefix, self.forms))
def post(self, request, **kwargs):
+ """ Save the first valid form found or reload page displaying a warning """
for prefix in self.forms.keys():
- form = self.get_form(prefix)
+ form = self.get_form_by_prefix(prefix)
if form.is_valid():
form.save()
+ messages.success(self.request, "Un nouveau %s a été enregistré" % prefix)
return self.form_valid(form)
- return self.form_invalid(form)
-
- def form_valid(self, form):
- messages.success(self.request, "Un nouveau %s a été enregistré" % form.prefix)
- return super().form_valid(form)
+ messages.warning(request, "Il y a eu une erreur lors du traitement du formulaire")
+ return self.get(request, **kwargs)
def get_context_data(self, **kwargs):
context = super(FormMixin, self).get_context_data(**kwargs)
for prefix in self.forms.keys():
- context['%s_form' % prefix] = self.get_form(prefix)
+ context['%s_form' % prefix] = self.get_form_by_prefix(prefix)
return context
-
diff --git a/notes/models.py b/notes/models.py
index 4035a61..01044e1 100644
--- a/notes/models.py
+++ b/notes/models.py
@@ -16,6 +16,7 @@ GENRE_CHOICES = (
(FEMME, 'Femme'),
)
+
class Sujet(models.Model):
""" Personne faisant l'objet d'un suivi par la maraude
"""
@@ -38,9 +39,12 @@ class Sujet(models.Model):
def __str__(self):
string = '%s ' % self.genre
- if self.nom: string += '%s ' % self.nom
- if self.surnom: string += '"%s" ' % self.surnom
- if self.prenom: string += '%s' % self.prenom
+ if self.nom:
+ string += '%s ' % self.nom
+ if self.surnom:
+ string += '"%s" ' % self.surnom
+ if self.prenom:
+ string += '%s' % self.prenom
return string
def clean(self):
@@ -65,6 +69,27 @@ class Sujet(models.Model):
return reverse("notes:details-sujet", kwargs={"pk": self.pk})
+# Attributes used by 'notes' template tags
+# bg_color : background color of header
+# labels : list of strings to put in labels
+def cached_attr(name):
+ """ Cached property set on return value of 'note_ATTR' method on
+ child instance.
+ """
+ private_name = '_%s' % name
+
+ def getter(self):
+ if not hasattr(self, private_name):
+ setattr(self,
+ private_name,
+ # Call *child instance* method
+ getattr(self.cast(), 'note_%s' % name)()
+ )
+ return getattr(self, private_name)
+
+ return getter
+
+
class Note(models.Model):
""" Note relative à un sujet.
@@ -121,7 +146,7 @@ class Note(models.Model):
""" Returns (header background color, labels color).
Values must be valid bootstrap color name
"""
- return ("default", "info")
+ return "default", "info"
def note_labels(self):
""" Returns list of objects that are printed as bootstrap labels """
@@ -144,7 +169,6 @@ class Note(models.Model):
self._child_class = self._child_instance.__class__
return
-
@property
def type_name(self):
return self.child_class.__qualname__
@@ -160,25 +184,9 @@ class Note(models.Model):
self._get_child_class_and_instance()
return self._child_instance
- ## Attributes used by 'notes' template tags
- # bg_color : background color of header
- # labels : list of strings to put in labels
- def cached_attr(name):
- """ Cached property set on return value of 'note_ATTR' method on
- child instance.
- """
- private_name = '_%s' % name
- def getter(self):
- if not hasattr(self, private_name):
- setattr(self,
- private_name,
- # Call *child instance* method
- getattr(self.cast(), 'note_%s' % name)()
- )
- return getattr(self, private_name)
- return getter
+ # Define specials properties
bg_colors = property(cached_attr('bg_colors'), doc="background color of header")
labels = property(cached_attr('labels'), doc="list of string to display as labels")
def get_absolute_url(self):
- return reverse("notes:details-sujet", kwargs={"pk": self.sujet.pk})
\ No newline at end of file
+ return reverse("notes:details-sujet", kwargs={"pk": self.sujet.pk})
diff --git a/notes/templates/notes/table_inline.html b/notes/templates/notes/table_inline.html
index e0d9711..2e9af99 100644
--- a/notes/templates/notes/table_inline.html
+++ b/notes/templates/notes/table_inline.html
@@ -1,16 +1,14 @@
- |
+ |
{% if link %}
{{header}}
{% else %}
{{header}}
{% endif %} {{small}}
-
+
{% for label in labels %}
- {{label}}
- {% endfor %}
+ {{label}}
+ {% endfor %}
|
-
-
{{text | linebreaks }} |
diff --git a/notes/templatetags/notes.py b/notes/templatetags/notes.py
index c8c55d2..faece84 100644
--- a/notes/templatetags/notes.py
+++ b/notes/templatetags/notes.py
@@ -1,8 +1,8 @@
from django import template
-from django.urls import reverse
register = template.Library()
+
@register.inclusion_tag("notes/table_inline.html")
def inline_table(note, header=None):
from maraudes.models import Maraude
@@ -10,7 +10,7 @@ def inline_table(note, header=None):
if not header:
header = "date"
- if not header in ['sujet', 'date']:
+ if header not in ['sujet', 'date']:
raise ValueError('header must be "sujet" or "date"')
if header == "date":
@@ -24,10 +24,8 @@ def inline_table(note, header=None):
header_field = "sujet"
link = note.sujet.get_absolute_url()
- header = getattr(note, header_field)
-
return {
- 'header': header,
+ 'header': getattr(note, header_field),
'link': link,
'small': note.child_class.__qualname__,
'bg_color': bg_color or "default",
diff --git a/notes/tests.py b/notes/tests.py
index 55ea0c2..dc8499e 100644
--- a/notes/tests.py
+++ b/notes/tests.py
@@ -24,6 +24,7 @@ class SujetModelTestCase(TestCase):
with self.assertRaises(ValidationError):
Sujet.objects.create(age=25)
+
class NoteManagerTestCase(TestCase):
""" managers.NoteManager Test Case """
@@ -32,8 +33,11 @@ class NoteManagerTestCase(TestCase):
self.note_manager = NoteManager
def test_note_default_manager_is_NoteManager(self):
- self.assertIsInstance(Note.objects, self.note_manager,
- msg="%s is not Note default manager" % self.note_manager)
+ self.assertIsInstance(
+ Note.objects,
+ self.note_manager,
+ msg="%s is not Note default manager" % self.note_manager
+ )
def test_by_date(self):
prev_note = None
@@ -41,8 +45,11 @@ class NoteManagerTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertLessEqual(prev_note.created_date, note.created_date,
- msg="%s is not same date or prior to %s" % (prev_note, note))
+ self.assertLessEqual(
+ prev_note.created_date,
+ note.created_date,
+ msg="%s is not same date or prior to %s" % (prev_note, note)
+ )
prev_note = note
def test_by_date_reversed(self):
@@ -51,8 +58,11 @@ class NoteManagerTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertGreaterEqual(prev_note.created_date, note.created_date,
- msg="%s is not same date or later to %s" % (prev_note, note))
+ self.assertGreaterEqual(
+ prev_note.created_date,
+ note.created_date,
+ msg="%s is not same date or later to %s" % (prev_note, note)
+ )
prev_note = note
def test_by_time(self):
@@ -61,8 +71,11 @@ class NoteManagerTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertLessEqual(prev_note.created_time, note.created_time,
- msg="%s is not same time or prior to %s" % (prev_note, note))
+ self.assertLessEqual(
+ prev_note.created_time,
+ note.created_time,
+ msg="%s is not same time or prior to %s" % (prev_note, note)
+ )
prev_note = note
def test_by_time_reversed(self):
@@ -71,10 +84,13 @@ class NoteManagerTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertLessEqual(prev_note.created_time, note.created_time,
- msg="%s is not same time or later to %s" % (prev_note, note))
+ self.assertLessEqual(
+ prev_note.created_time,
+ note.created_time,
+ msg="%s is not same time or later to %s" % (prev_note, note))
prev_note = note
+
class NoteQuerySetTestCase(TestCase):
def setUp(self):
@@ -86,7 +102,9 @@ class NoteQuerySetTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertLessEqual(prev_note.created_date, note.created_date,
+ self.assertLessEqual(
+ prev_note.created_date,
+ note.created_date,
msg="%s is not same date or prior to %s" % (prev_note, note))
prev_note = note
@@ -96,8 +114,11 @@ class NoteQuerySetTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertGreaterEqual(prev_note.created_date, note.created_date,
- msg="%s is not same date or later to %s" % (prev_note, note))
+ self.assertGreaterEqual(
+ prev_note.created_date,
+ note.created_date,
+ msg="%s is not same date or later to %s" % (prev_note, note)
+ )
prev_note = note
def test_by_time(self):
@@ -106,8 +127,11 @@ class NoteQuerySetTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertLessEqual(prev_note.created_time, note.created_time,
- msg="%s is not same time or prior to %s" % (prev_note, note))
+ self.assertLessEqual(
+ prev_note.created_time,
+ note.created_time,
+ msg="%s is not same time or prior to %s" % (prev_note, note)
+ )
prev_note = note
def test_by_time_reversed(self):
@@ -116,6 +140,9 @@ class NoteQuerySetTestCase(TestCase):
if not prev_note:
prev_note = note
else:
- self.assertLessEqual(prev_note.created_time, note.created_time,
- msg="%s is not same time or later to %s" % (prev_note, note))
+ self.assertLessEqual(
+ prev_note.created_time,
+ note.created_time,
+ msg="%s is not same time or later to %s" % (prev_note, note)
+ )
prev_note = note
diff --git a/notes/views.py b/notes/views.py
index 7eb7350..bcce6ca 100644
--- a/notes/views.py
+++ b/notes/views.py
@@ -1,13 +1,12 @@
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 watson import search as watson
+from website.mixins import AjaxTemplateMixin
from utilisateurs.mixins import MaraudeurMixin
from maraudes.models import Maraude, CompteRendu
from maraudes.notes import Observation, Signalement
@@ -20,13 +19,13 @@ 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
@@ -38,7 +37,6 @@ class Filter:
return self._filter_func(qs)
-
class ListView(MaraudeurMixin, generic.ListView):
""" Base ListView for Maraude and Sujet lists """
paginate_by = 30
@@ -47,8 +45,8 @@ class ListView(MaraudeurMixin, generic.ListView):
filters = []
active_filter = None
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
self._filters = {}
if self.filters:
@@ -95,38 +93,37 @@ class MaraudeListView(ListView):
class SujetListView(ListView):
- #ListView
+ # ListView
model = Sujet
template_name = "notes/liste_sujets.html"
cell_template = "notes/table_cell_sujets.html"
table_header = "Liste des sujets"
-
+ @staticmethod
def info_completed_filter(qs):
- COMPLETED_RATIO = 70 # % of total fields completed
+ completed_ratio = 70 # % of total fields completed
excluded_set = set()
for sujet in qs:
- if sujet.statistiques.info_completed >= COMPLETED_RATIO:
+ if sujet.statistiques.info_completed >= completed_ratio:
excluded_set.add(sujet.pk)
return qs.exclude(pk__in=excluded_set)
+ @staticmethod
def rencontre_dans_le_mois(qs):
""" Renvoie les sujets du queryset pour lesquelles une observation a été enregistrée
au cours des 30 derniers jours """
- DAYS_NUMBER = 30
- LIMIT_DATE = timezone.now().date() - datetime.timedelta(DAYS_NUMBER)
-
+ days_number = 30
+ limit_date = timezone.now().date() - datetime.timedelta(days_number)
included_set = set()
for sujet in qs:
# Try to find an observation in the range
most_recent_obs = Observation.objects.filter(sujet=sujet).order_by("-created_date").first()
- if most_recent_obs and most_recent_obs.created_date >= LIMIT_DATE:
+ if most_recent_obs and most_recent_obs.created_date >= limit_date:
included_set.add(sujet.pk)
return qs.filter(pk__in=included_set)
-
filters = [
("Connu(e)s cette année",
lambda qs: qs.filter(
@@ -138,10 +135,9 @@ class SujetListView(ListView):
]
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))
+ # logger.warning("SEARCH for %s : %s" % (search_text, results))
if results.count() == 1:
return redirect(results[0].get_absolute_url())
self.queryset = results
@@ -156,6 +152,7 @@ class SujetListView(ListView):
class DetailView(MaraudeurMixin, generic.DetailView):
template_name = "notes/details.html"
+
class CompteRenduDetailsView(DetailView):
""" Vue détaillé d'un compte-rendu de maraude """
@@ -165,32 +162,38 @@ class CompteRenduDetailsView(DetailView):
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['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()
+ 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()
+ date=self.object.date
+ ).filter(
+ heure_fin__isnull=False
+ ).last()
return context
class SuiviSujetView(NoteFormMixin, DetailView):
- #NoteFormMixin
+ # 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
+
+ # DetailView
model = Sujet
template_name = "notes/details_sujet.html"
context_object_name = "sujet"
@@ -205,8 +208,8 @@ class SuiviSujetView(NoteFormMixin, DetailView):
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)
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
try:
notes = self.paginator.page(self.page)
except PageNotAnInteger:
@@ -217,10 +220,9 @@ class SuiviSujetView(NoteFormMixin, DetailView):
return context
-### Sujet Management Views
-
+# Sujet Management Views
class SujetAjaxDetailsView(generic.DetailView):
- #DetailView
+ # DetailView
template_name = "notes/details_sujet_inner.html"
model = Sujet
@@ -232,8 +234,9 @@ class SujetAjaxDetailsView(generic.DetailView):
return redirect("notes:details-sujet", pk=self.get_object().pk)
return super().get(*args, **kwargs)
+
class SujetAjaxUpdateView(generic.edit.UpdateView):
- #UpdateView
+ """ View for 'sujet' updates, can be retrieved by ajax requests. """
template_name = "notes/details_sujet_update.html"
model = Sujet
fields = '__all__'
@@ -241,24 +244,28 @@ class SujetAjaxUpdateView(generic.edit.UpdateView):
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
+ """ View for 'sujet' creation, can be retrieved by ajax requests. """
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
+ 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 """
+ """ Merge two 'sujet' objects by implementing actions.merge_two as a view """
template_name = "notes/sujet_merge.html"
model = Sujet