changes display of notes in tables (headers on the side)
corrects code for 'notes' modules according to PEP8 guidelines
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
from .models import Sujet
|
from statistiques.models import NSP
|
||||||
from statistiques.models import FicheStatistique, NSP
|
|
||||||
|
|
||||||
|
|
||||||
def merge_stats(main, merged):
|
def merge_stats(main, merged):
|
||||||
@@ -21,17 +20,18 @@ def merge_stats(main, merged):
|
|||||||
for field in ('prob_psychiatrie', 'prob_somatique',
|
for field in ('prob_psychiatrie', 'prob_somatique',
|
||||||
'prob_administratif', 'prob_addiction',
|
'prob_administratif', 'prob_addiction',
|
||||||
'connu_siao', 'lien_familial'):
|
'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))
|
setattr(main.statistiques, field, getattr(merged.statistiques, field, None))
|
||||||
# Choice fields, None is NSP
|
# Choice fields, None is NSP
|
||||||
for field in ('habitation', 'ressources', 'parcours_de_vie'):
|
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))
|
setattr(main.statistiques, field, getattr(merged.statistiques, field, NSP))
|
||||||
|
|
||||||
|
|
||||||
def merge_two(main, merged):
|
def merge_two(main, merged):
|
||||||
""" Merge 'merged' sujet into 'main' one """
|
""" Merge 'merged' sujet into 'main' one """
|
||||||
merge_stats(main, merged) # Merge statistics and informations
|
merge_stats(main, merged) # Merge statistics and informations
|
||||||
for note in merged.notes.all(): # Move all notes
|
for note in merged.notes.all(): # Move all notes
|
||||||
note.sujet = main
|
note.sujet = main
|
||||||
note.save()
|
note.save()
|
||||||
main.save()
|
main.save()
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from watson import search as watson
|
from watson import search as watson
|
||||||
|
|
||||||
|
|
||||||
class NotesConfig(AppConfig):
|
class NotesConfig(AppConfig):
|
||||||
name = 'notes'
|
name = 'notes'
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
Sujet = self.get_model("Sujet")
|
sujet_model = self.get_model("Sujet")
|
||||||
watson.register(Sujet, fields=('nom', 'prenom', 'surnom'))
|
watson.register(sujet_model, fields=('nom', 'prenom', 'surnom'))
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ from utilisateurs.models import Professionnel
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django_select2.forms import Select2Widget
|
from django_select2.forms import Select2Widget
|
||||||
|
|
||||||
### NOTES
|
|
||||||
|
|
||||||
|
# NOTES
|
||||||
class NoteForm(forms.ModelForm):
|
class NoteForm(forms.ModelForm):
|
||||||
""" Generic Note form """
|
""" Generic Note form """
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -15,11 +15,12 @@ class NoteForm(forms.ModelForm):
|
|||||||
fields = ['sujet', 'text', 'created_by', 'created_date', 'created_time']
|
fields = ['sujet', 'text', 'created_by', 'created_date', 'created_time']
|
||||||
widgets = {
|
widgets = {
|
||||||
'sujet': Select2Widget(),
|
'sujet': Select2Widget(),
|
||||||
'text': forms.Textarea(attrs={'rows':4}),
|
'text': forms.Textarea(
|
||||||
|
attrs={'rows': 4}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleNoteForm(forms.ModelForm):
|
class SimpleNoteForm(forms.ModelForm):
|
||||||
""" Simple note with only 'sujet' and 'text' fields.
|
""" Simple note with only 'sujet' and 'text' fields.
|
||||||
|
|
||||||
@@ -30,7 +31,6 @@ class SimpleNoteForm(forms.ModelForm):
|
|||||||
fields = ['sujet', 'text']
|
fields = ['sujet', 'text']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserNoteForm(NoteForm):
|
class UserNoteForm(NoteForm):
|
||||||
""" Form that sets 'created_by' with current user id.
|
""" Form that sets 'created_by' with current user id.
|
||||||
|
|
||||||
@@ -56,7 +56,6 @@ class UserNoteForm(NoteForm):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AutoNoteForm(UserNoteForm):
|
class AutoNoteForm(UserNoteForm):
|
||||||
class Meta(UserNoteForm.Meta):
|
class Meta(UserNoteForm.Meta):
|
||||||
fields = ['text']
|
fields = ['text']
|
||||||
@@ -73,21 +72,21 @@ class AutoNoteForm(UserNoteForm):
|
|||||||
return inst
|
return inst
|
||||||
|
|
||||||
|
|
||||||
|
# SUJETS
|
||||||
### SUJETS
|
|
||||||
|
|
||||||
current_year = datetime.date.today().year
|
current_year = datetime.date.today().year
|
||||||
YEAR_CHOICE = tuple(year - 2 for year in range(current_year, current_year + 10))
|
YEAR_CHOICE = tuple(year - 2 for year in range(current_year, current_year + 10))
|
||||||
|
|
||||||
|
|
||||||
class SujetCreateForm(forms.ModelForm):
|
class SujetCreateForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Sujet
|
model = Sujet
|
||||||
fields = ['nom', 'surnom', 'prenom', 'genre', 'premiere_rencontre']
|
fields = ['nom', 'surnom', 'prenom', 'genre', 'premiere_rencontre']
|
||||||
widgets = {
|
widgets = {'premiere_rencontre': forms.SelectDateWidget(
|
||||||
'premiere_rencontre': forms.SelectDateWidget( empty_label=("Année", "Mois", "Jour"),
|
empty_label=("Année", "Mois", "Jour"),
|
||||||
years = YEAR_CHOICE,
|
years=YEAR_CHOICE,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class SelectSujetForm(forms.Form):
|
class SelectSujetForm(forms.Form):
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ from django.db.models.query import QuerySet
|
|||||||
class NoteQuerySet(QuerySet):
|
class NoteQuerySet(QuerySet):
|
||||||
|
|
||||||
def _ordered_by_field(self, field, reverse=False):
|
def _ordered_by_field(self, field, reverse=False):
|
||||||
return self.order_by( '%s%s' % ( "-" if reverse else "",
|
return self.order_by('%s%s' % ("-" if reverse else "", field))
|
||||||
field
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def by_date(self, reverse=False):
|
def by_date(self, reverse=False):
|
||||||
return self._ordered_by_field('created_date', reverse=reverse)
|
return self._ordered_by_field('created_date', reverse=reverse)
|
||||||
@@ -16,6 +13,7 @@ class NoteQuerySet(QuerySet):
|
|||||||
def by_time(self, reverse=False):
|
def by_time(self, reverse=False):
|
||||||
return self._ordered_by_field('created_time', reverse=reverse)
|
return self._ordered_by_field('created_time', reverse=reverse)
|
||||||
|
|
||||||
|
|
||||||
class NoteManager(Manager):
|
class NoteManager(Manager):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@@ -28,5 +26,3 @@ class NoteManager(Manager):
|
|||||||
|
|
||||||
def by_time(self, **kwargs):
|
def by_time(self, **kwargs):
|
||||||
return self.get_queryset().by_time(**kwargs)
|
return self.get_queryset().by_time(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,39 @@
|
|||||||
from django.views.generic.edit import FormMixin
|
from django.views.generic.edit import FormMixin
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
|
||||||
from .forms import *
|
|
||||||
|
|
||||||
class NoteFormMixin(FormMixin):
|
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
|
forms = None
|
||||||
|
|
||||||
def get_form(self, prefix):
|
def get_form_kwargs(self):
|
||||||
kwargs = self.get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs['prefix'] = prefix
|
|
||||||
kwargs['request'] = self.request
|
kwargs['request'] = self.request
|
||||||
form_class = self.forms[prefix]
|
return kwargs
|
||||||
return form_class(**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):
|
def post(self, request, **kwargs):
|
||||||
|
""" Save the first valid form found or reload page displaying a warning """
|
||||||
for prefix in self.forms.keys():
|
for prefix in self.forms.keys():
|
||||||
form = self.get_form(prefix)
|
form = self.get_form_by_prefix(prefix)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
|
messages.success(self.request, "Un nouveau %s a été enregistré" % prefix)
|
||||||
return self.form_valid(form)
|
return self.form_valid(form)
|
||||||
return self.form_invalid(form)
|
messages.warning(request, "Il y a eu une erreur lors du traitement du formulaire")
|
||||||
|
return self.get(request, **kwargs)
|
||||||
def form_valid(self, form):
|
|
||||||
messages.success(self.request, "Un nouveau %s a été enregistré" % form.prefix)
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(FormMixin, self).get_context_data(**kwargs)
|
context = super(FormMixin, self).get_context_data(**kwargs)
|
||||||
for prefix in self.forms.keys():
|
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
|
return context
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ GENRE_CHOICES = (
|
|||||||
(FEMME, 'Femme'),
|
(FEMME, 'Femme'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Sujet(models.Model):
|
class Sujet(models.Model):
|
||||||
""" Personne faisant l'objet d'un suivi par la maraude
|
""" Personne faisant l'objet d'un suivi par la maraude
|
||||||
"""
|
"""
|
||||||
@@ -38,9 +39,12 @@ class Sujet(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
string = '%s ' % self.genre
|
string = '%s ' % self.genre
|
||||||
if self.nom: string += '%s ' % self.nom
|
if self.nom:
|
||||||
if self.surnom: string += '"%s" ' % self.surnom
|
string += '%s ' % self.nom
|
||||||
if self.prenom: string += '%s' % self.prenom
|
if self.surnom:
|
||||||
|
string += '"%s" ' % self.surnom
|
||||||
|
if self.prenom:
|
||||||
|
string += '%s' % self.prenom
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
@@ -65,6 +69,27 @@ class Sujet(models.Model):
|
|||||||
return reverse("notes:details-sujet", kwargs={"pk": self.pk})
|
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):
|
class Note(models.Model):
|
||||||
""" Note relative à un sujet.
|
""" Note relative à un sujet.
|
||||||
|
|
||||||
@@ -121,7 +146,7 @@ class Note(models.Model):
|
|||||||
""" Returns (header background color, labels color).
|
""" Returns (header background color, labels color).
|
||||||
Values must be valid bootstrap color name
|
Values must be valid bootstrap color name
|
||||||
"""
|
"""
|
||||||
return ("default", "info")
|
return "default", "info"
|
||||||
|
|
||||||
def note_labels(self):
|
def note_labels(self):
|
||||||
""" Returns list of objects that are printed as bootstrap labels """
|
""" 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__
|
self._child_class = self._child_instance.__class__
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type_name(self):
|
def type_name(self):
|
||||||
return self.child_class.__qualname__
|
return self.child_class.__qualname__
|
||||||
@@ -160,25 +184,9 @@ class Note(models.Model):
|
|||||||
self._get_child_class_and_instance()
|
self._get_child_class_and_instance()
|
||||||
return self._child_instance
|
return self._child_instance
|
||||||
|
|
||||||
## Attributes used by 'notes' template tags
|
# Define specials properties
|
||||||
# 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
|
|
||||||
bg_colors = property(cached_attr('bg_colors'), doc="background color of header")
|
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")
|
labels = property(cached_attr('labels'), doc="list of string to display as labels")
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("notes:details-sujet", kwargs={"pk": self.sujet.pk})
|
return reverse("notes:details-sujet", kwargs={"pk": self.sujet.pk})
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th class="bg-{{bg_color}}" >
|
<th class="bg-{{bg_color}}">
|
||||||
{% if link %}
|
{% if link %}
|
||||||
<a href="{{link}}"><strong>{{header}}</strong></a>
|
<a href="{{link}}"><strong>{{header}}</strong></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<strong>{{header}}</strong>
|
<strong>{{header}}</strong>
|
||||||
{% endif %} <small>{{small}}</small>
|
{% endif %} <small>{{small}}</small>
|
||||||
<span class="pull-right">
|
<br />
|
||||||
{% for label in labels %}
|
{% for label in labels %}
|
||||||
<span class="label label-{{bg_label_color}}">{{label}}</span>
|
<span class="label label-{{bg_label_color}}">{{label}}</span><br />
|
||||||
{% endfor %}</span>
|
{% endfor %}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="background-color:#fff"><p>{{text | linebreaks }}</p></td>
|
<td style="background-color:#fff"><p>{{text | linebreaks }}</p></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from django import template
|
from django import template
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag("notes/table_inline.html")
|
@register.inclusion_tag("notes/table_inline.html")
|
||||||
def inline_table(note, header=None):
|
def inline_table(note, header=None):
|
||||||
from maraudes.models import Maraude
|
from maraudes.models import Maraude
|
||||||
@@ -10,7 +10,7 @@ def inline_table(note, header=None):
|
|||||||
|
|
||||||
if not header:
|
if not header:
|
||||||
header = "date"
|
header = "date"
|
||||||
if not header in ['sujet', 'date']:
|
if header not in ['sujet', 'date']:
|
||||||
raise ValueError('header must be "sujet" or "date"')
|
raise ValueError('header must be "sujet" or "date"')
|
||||||
|
|
||||||
if header == "date":
|
if header == "date":
|
||||||
@@ -24,10 +24,8 @@ def inline_table(note, header=None):
|
|||||||
header_field = "sujet"
|
header_field = "sujet"
|
||||||
link = note.sujet.get_absolute_url()
|
link = note.sujet.get_absolute_url()
|
||||||
|
|
||||||
header = getattr(note, header_field)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header': header,
|
'header': getattr(note, header_field),
|
||||||
'link': link,
|
'link': link,
|
||||||
'small': note.child_class.__qualname__,
|
'small': note.child_class.__qualname__,
|
||||||
'bg_color': bg_color or "default",
|
'bg_color': bg_color or "default",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class SujetModelTestCase(TestCase):
|
|||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
Sujet.objects.create(age=25)
|
Sujet.objects.create(age=25)
|
||||||
|
|
||||||
|
|
||||||
class NoteManagerTestCase(TestCase):
|
class NoteManagerTestCase(TestCase):
|
||||||
""" managers.NoteManager Test Case """
|
""" managers.NoteManager Test Case """
|
||||||
|
|
||||||
@@ -32,8 +33,11 @@ class NoteManagerTestCase(TestCase):
|
|||||||
self.note_manager = NoteManager
|
self.note_manager = NoteManager
|
||||||
|
|
||||||
def test_note_default_manager_is_NoteManager(self):
|
def test_note_default_manager_is_NoteManager(self):
|
||||||
self.assertIsInstance(Note.objects, self.note_manager,
|
self.assertIsInstance(
|
||||||
msg="%s is not Note default manager" % self.note_manager)
|
Note.objects,
|
||||||
|
self.note_manager,
|
||||||
|
msg="%s is not Note default manager" % self.note_manager
|
||||||
|
)
|
||||||
|
|
||||||
def test_by_date(self):
|
def test_by_date(self):
|
||||||
prev_note = None
|
prev_note = None
|
||||||
@@ -41,8 +45,11 @@ class NoteManagerTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
else:
|
||||||
self.assertLessEqual(prev_note.created_date, note.created_date,
|
self.assertLessEqual(
|
||||||
msg="%s is not same date or prior to %s" % (prev_note, note))
|
prev_note.created_date,
|
||||||
|
note.created_date,
|
||||||
|
msg="%s is not same date or prior to %s" % (prev_note, note)
|
||||||
|
)
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|
||||||
def test_by_date_reversed(self):
|
def test_by_date_reversed(self):
|
||||||
@@ -51,8 +58,11 @@ class NoteManagerTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
else:
|
||||||
self.assertGreaterEqual(prev_note.created_date, note.created_date,
|
self.assertGreaterEqual(
|
||||||
msg="%s is not same date or later to %s" % (prev_note, note))
|
prev_note.created_date,
|
||||||
|
note.created_date,
|
||||||
|
msg="%s is not same date or later to %s" % (prev_note, note)
|
||||||
|
)
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|
||||||
def test_by_time(self):
|
def test_by_time(self):
|
||||||
@@ -61,8 +71,11 @@ class NoteManagerTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
else:
|
||||||
self.assertLessEqual(prev_note.created_time, note.created_time,
|
self.assertLessEqual(
|
||||||
msg="%s is not same time or prior to %s" % (prev_note, note))
|
prev_note.created_time,
|
||||||
|
note.created_time,
|
||||||
|
msg="%s is not same time or prior to %s" % (prev_note, note)
|
||||||
|
)
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|
||||||
def test_by_time_reversed(self):
|
def test_by_time_reversed(self):
|
||||||
@@ -71,10 +84,13 @@ class NoteManagerTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
else:
|
||||||
self.assertLessEqual(prev_note.created_time, note.created_time,
|
self.assertLessEqual(
|
||||||
msg="%s is not same time or later to %s" % (prev_note, note))
|
prev_note.created_time,
|
||||||
|
note.created_time,
|
||||||
|
msg="%s is not same time or later to %s" % (prev_note, note))
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|
||||||
|
|
||||||
class NoteQuerySetTestCase(TestCase):
|
class NoteQuerySetTestCase(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -86,7 +102,9 @@ class NoteQuerySetTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
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))
|
msg="%s is not same date or prior to %s" % (prev_note, note))
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|
||||||
@@ -96,8 +114,11 @@ class NoteQuerySetTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
else:
|
||||||
self.assertGreaterEqual(prev_note.created_date, note.created_date,
|
self.assertGreaterEqual(
|
||||||
msg="%s is not same date or later to %s" % (prev_note, note))
|
prev_note.created_date,
|
||||||
|
note.created_date,
|
||||||
|
msg="%s is not same date or later to %s" % (prev_note, note)
|
||||||
|
)
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|
||||||
def test_by_time(self):
|
def test_by_time(self):
|
||||||
@@ -106,8 +127,11 @@ class NoteQuerySetTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
else:
|
||||||
self.assertLessEqual(prev_note.created_time, note.created_time,
|
self.assertLessEqual(
|
||||||
msg="%s is not same time or prior to %s" % (prev_note, note))
|
prev_note.created_time,
|
||||||
|
note.created_time,
|
||||||
|
msg="%s is not same time or prior to %s" % (prev_note, note)
|
||||||
|
)
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|
||||||
def test_by_time_reversed(self):
|
def test_by_time_reversed(self):
|
||||||
@@ -116,6 +140,9 @@ class NoteQuerySetTestCase(TestCase):
|
|||||||
if not prev_note:
|
if not prev_note:
|
||||||
prev_note = note
|
prev_note = note
|
||||||
else:
|
else:
|
||||||
self.assertLessEqual(prev_note.created_time, note.created_time,
|
self.assertLessEqual(
|
||||||
msg="%s is not same time or later to %s" % (prev_note, note))
|
prev_note.created_time,
|
||||||
|
note.created_time,
|
||||||
|
msg="%s is not same time or later to %s" % (prev_note, note)
|
||||||
|
)
|
||||||
prev_note = note
|
prev_note = note
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.shortcuts import redirect, reverse
|
from django.shortcuts import redirect, reverse
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http.response import HttpResponseNotAllowed
|
|
||||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
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 utilisateurs.mixins import MaraudeurMixin
|
||||||
from maraudes.models import Maraude, CompteRendu
|
from maraudes.models import Maraude, CompteRendu
|
||||||
from maraudes.notes import Observation, Signalement
|
from maraudes.notes import Observation, Signalement
|
||||||
@@ -20,13 +19,13 @@ logger = logging.getLogger(__name__)
|
|||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IndexView(MaraudeurMixin, generic.TemplateView):
|
class IndexView(MaraudeurMixin, generic.TemplateView):
|
||||||
template_name = "notes/index.html"
|
template_name = "notes/index.html"
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
return redirect("notes:liste-sujet")
|
return redirect("notes:liste-sujet")
|
||||||
|
|
||||||
|
|
||||||
class Filter:
|
class Filter:
|
||||||
def __init__(self, title, name, filter_func):
|
def __init__(self, title, name, filter_func):
|
||||||
self.title = title
|
self.title = title
|
||||||
@@ -38,7 +37,6 @@ class Filter:
|
|||||||
return self._filter_func(qs)
|
return self._filter_func(qs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ListView(MaraudeurMixin, generic.ListView):
|
class ListView(MaraudeurMixin, generic.ListView):
|
||||||
""" Base ListView for Maraude and Sujet lists """
|
""" Base ListView for Maraude and Sujet lists """
|
||||||
paginate_by = 30
|
paginate_by = 30
|
||||||
@@ -47,8 +45,8 @@ class ListView(MaraudeurMixin, generic.ListView):
|
|||||||
filters = []
|
filters = []
|
||||||
active_filter = None
|
active_filter = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(**kwargs)
|
||||||
self._filters = {}
|
self._filters = {}
|
||||||
|
|
||||||
if self.filters:
|
if self.filters:
|
||||||
@@ -95,38 +93,37 @@ class MaraudeListView(ListView):
|
|||||||
|
|
||||||
|
|
||||||
class SujetListView(ListView):
|
class SujetListView(ListView):
|
||||||
#ListView
|
# ListView
|
||||||
model = Sujet
|
model = Sujet
|
||||||
template_name = "notes/liste_sujets.html"
|
template_name = "notes/liste_sujets.html"
|
||||||
cell_template = "notes/table_cell_sujets.html"
|
cell_template = "notes/table_cell_sujets.html"
|
||||||
table_header = "Liste des sujets"
|
table_header = "Liste des sujets"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def info_completed_filter(qs):
|
def info_completed_filter(qs):
|
||||||
COMPLETED_RATIO = 70 # % of total fields completed
|
completed_ratio = 70 # % of total fields completed
|
||||||
|
|
||||||
excluded_set = set()
|
excluded_set = set()
|
||||||
for sujet in qs:
|
for sujet in qs:
|
||||||
if sujet.statistiques.info_completed >= COMPLETED_RATIO:
|
if sujet.statistiques.info_completed >= completed_ratio:
|
||||||
excluded_set.add(sujet.pk)
|
excluded_set.add(sujet.pk)
|
||||||
|
|
||||||
return qs.exclude(pk__in=excluded_set)
|
return qs.exclude(pk__in=excluded_set)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def rencontre_dans_le_mois(qs):
|
def rencontre_dans_le_mois(qs):
|
||||||
""" Renvoie les sujets du queryset pour lesquelles une observation a été enregistrée
|
""" Renvoie les sujets du queryset pour lesquelles une observation a été enregistrée
|
||||||
au cours des 30 derniers jours """
|
au cours des 30 derniers jours """
|
||||||
DAYS_NUMBER = 30
|
days_number = 30
|
||||||
LIMIT_DATE = timezone.now().date() - datetime.timedelta(DAYS_NUMBER)
|
limit_date = timezone.now().date() - datetime.timedelta(days_number)
|
||||||
|
|
||||||
included_set = set()
|
included_set = set()
|
||||||
for sujet in qs:
|
for sujet in qs:
|
||||||
# Try to find an observation in the range
|
# Try to find an observation in the range
|
||||||
most_recent_obs = Observation.objects.filter(sujet=sujet).order_by("-created_date").first()
|
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)
|
included_set.add(sujet.pk)
|
||||||
return qs.filter(pk__in=included_set)
|
return qs.filter(pk__in=included_set)
|
||||||
|
|
||||||
|
|
||||||
filters = [
|
filters = [
|
||||||
("Connu(e)s cette année",
|
("Connu(e)s cette année",
|
||||||
lambda qs: qs.filter(
|
lambda qs: qs.filter(
|
||||||
@@ -138,10 +135,9 @@ class SujetListView(ListView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def post(self, request, **kwargs):
|
def post(self, request, **kwargs):
|
||||||
from watson import search as watson
|
|
||||||
search_text = request.POST.get('q')
|
search_text = request.POST.get('q')
|
||||||
results = watson.filter(Sujet, search_text)
|
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:
|
if results.count() == 1:
|
||||||
return redirect(results[0].get_absolute_url())
|
return redirect(results[0].get_absolute_url())
|
||||||
self.queryset = results
|
self.queryset = results
|
||||||
@@ -156,6 +152,7 @@ class SujetListView(ListView):
|
|||||||
class DetailView(MaraudeurMixin, generic.DetailView):
|
class DetailView(MaraudeurMixin, generic.DetailView):
|
||||||
template_name = "notes/details.html"
|
template_name = "notes/details.html"
|
||||||
|
|
||||||
|
|
||||||
class CompteRenduDetailsView(DetailView):
|
class CompteRenduDetailsView(DetailView):
|
||||||
""" Vue détaillé d'un compte-rendu de maraude """
|
""" Vue détaillé d'un compte-rendu de maraude """
|
||||||
|
|
||||||
@@ -165,32 +162,38 @@ class CompteRenduDetailsView(DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**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(
|
context['next_maraude'] = Maraude.objects.get_future(
|
||||||
date=self.object.date + datetime.timedelta(1)
|
date=self.object.date + datetime.timedelta(1)
|
||||||
).filter(
|
).filter(
|
||||||
heure_fin__isnull=False
|
heure_fin__isnull=False
|
||||||
).first()
|
).first()
|
||||||
context['prev_maraude'] = Maraude.objects.get_past(
|
context['prev_maraude'] = Maraude.objects.get_past(
|
||||||
date=self.object.date
|
date=self.object.date
|
||||||
).filter(
|
).filter(
|
||||||
heure_fin__isnull=False
|
heure_fin__isnull=False
|
||||||
).last()
|
).last()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class SuiviSujetView(NoteFormMixin, DetailView):
|
class SuiviSujetView(NoteFormMixin, DetailView):
|
||||||
#NoteFormMixin
|
# NoteFormMixin
|
||||||
forms = {
|
forms = {
|
||||||
'note': AutoNoteForm,
|
'note': AutoNoteForm,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('notes:details-sujet', kwargs={'pk': self.get_object().pk})
|
return reverse('notes:details-sujet', kwargs={'pk': self.get_object().pk})
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs['sujet'] = self.get_object()
|
kwargs['sujet'] = self.get_object()
|
||||||
return kwargs
|
return kwargs
|
||||||
#DetailView
|
|
||||||
|
# DetailView
|
||||||
model = Sujet
|
model = Sujet
|
||||||
template_name = "notes/details_sujet.html"
|
template_name = "notes/details_sujet.html"
|
||||||
context_object_name = "sujet"
|
context_object_name = "sujet"
|
||||||
@@ -205,8 +208,8 @@ class SuiviSujetView(NoteFormMixin, DetailView):
|
|||||||
self.page = self.request.GET.get('page', 1)
|
self.page = self.request.GET.get('page', 1)
|
||||||
return super().get(*args, **kwargs)
|
return super().get(*args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
try:
|
try:
|
||||||
notes = self.paginator.page(self.page)
|
notes = self.paginator.page(self.page)
|
||||||
except PageNotAnInteger:
|
except PageNotAnInteger:
|
||||||
@@ -217,10 +220,9 @@ class SuiviSujetView(NoteFormMixin, DetailView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
### Sujet Management Views
|
# Sujet Management Views
|
||||||
|
|
||||||
class SujetAjaxDetailsView(generic.DetailView):
|
class SujetAjaxDetailsView(generic.DetailView):
|
||||||
#DetailView
|
# DetailView
|
||||||
template_name = "notes/details_sujet_inner.html"
|
template_name = "notes/details_sujet_inner.html"
|
||||||
model = Sujet
|
model = Sujet
|
||||||
|
|
||||||
@@ -232,8 +234,9 @@ class SujetAjaxDetailsView(generic.DetailView):
|
|||||||
return redirect("notes:details-sujet", pk=self.get_object().pk)
|
return redirect("notes:details-sujet", pk=self.get_object().pk)
|
||||||
return super().get(*args, **kwargs)
|
return super().get(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SujetAjaxUpdateView(generic.edit.UpdateView):
|
class SujetAjaxUpdateView(generic.edit.UpdateView):
|
||||||
#UpdateView
|
""" View for 'sujet' updates, can be retrieved by ajax requests. """
|
||||||
template_name = "notes/details_sujet_update.html"
|
template_name = "notes/details_sujet_update.html"
|
||||||
model = Sujet
|
model = Sujet
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@@ -241,24 +244,28 @@ class SujetAjaxUpdateView(generic.edit.UpdateView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse("notes:details-sujet", kwargs={'pk': self.object.pk})
|
return reverse("notes:details-sujet", kwargs={'pk': self.object.pk})
|
||||||
|
|
||||||
from website.mixins import AjaxTemplateMixin
|
|
||||||
|
|
||||||
class SujetCreateView(AjaxTemplateMixin, generic.edit.CreateView):
|
class SujetCreateView(AjaxTemplateMixin, generic.edit.CreateView):
|
||||||
#CreateView
|
""" View for 'sujet' creation, can be retrieved by ajax requests. """
|
||||||
template_name = "notes/sujet_create.html"
|
template_name = "notes/sujet_create.html"
|
||||||
form_class = SujetCreateForm
|
form_class = SujetCreateForm
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if 'next' in self.request.POST:
|
if 'next' in self.request.POST:
|
||||||
self.success_url = self.request.POST["next"]
|
self.success_url = self.request.POST["next"]
|
||||||
return super().post(self, request, *args, **kwargs)
|
return super().post(self, request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
try: context['next'] = self.request.GET['next']
|
try:
|
||||||
except:context['next'] = None
|
context['next'] = self.request.GET['next']
|
||||||
|
except:
|
||||||
|
context['next'] = None
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class MergeView(generic.DetailView, generic.FormView):
|
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"
|
template_name = "notes/sujet_merge.html"
|
||||||
model = Sujet
|
model = Sujet
|
||||||
|
|||||||
Reference in New Issue
Block a user