From 76dcaf7d5150b16c9cdcdd97c75121ae7bedefe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sun, 21 May 2017 23:39:36 +0100 Subject: [PATCH 1/6] drop py2 compat --- gestioncof/admin.py | 25 ++++++++----------------- gestioncof/forms.py | 6 ------ gestioncof/models.py | 36 +++++++++--------------------------- gestioncof/views.py | 9 +++------ 4 files changed, 20 insertions(+), 56 deletions(-) diff --git a/gestioncof/admin.py b/gestioncof/admin.py index a177c26c..659d5018 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -1,9 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - from django import forms from django.contrib import admin from django.utils.translation import ugettext_lazy as _ @@ -18,13 +12,12 @@ from django.contrib.auth.admin import UserAdmin from django.core.urlresolvers import reverse from django.utils.safestring import mark_safe from django.db.models import Q -import django.utils.six as six import autocomplete_light -def add_link_field(target_model='', field='', link_text=six.text_type, - desc_text=six.text_type): +def add_link_field(target_model='', field='', link_text=str, + desc_text=str): def add_link(cls): reverse_name = target_model or cls.model.__name__.lower() @@ -215,21 +208,19 @@ class UserProfileAdmin(UserAdmin): # FIXME: This is absolutely horrible. -def user_unicode(self): +def user_str(self): if self.first_name and self.last_name: - return "%s %s (%s)" % (self.first_name, self.last_name, self.username) + return "{} {} ({})".format( + self.first_name, self.last_name, self.username + ) else: return self.username -if six.PY2: - User.__unicode__ = user_unicode -else: - User.__str__ = user_unicode +User.__str__ = user_str class EventRegistrationAdmin(admin.ModelAdmin): form = autocomplete_light.modelform_factory(EventRegistration, exclude=[]) - list_display = ('__unicode__' if six.PY2 else '__str__', 'event', 'user', - 'paid') + list_display = ('__str__', 'event', 'user', 'paid') list_filter = ('paid',) search_fields = ('user__username', 'user__first_name', 'user__last_name', 'user__email', 'event__title') diff --git a/gestioncof/forms.py b/gestioncof/forms.py index 3a519a39..5ee4acf6 100644 --- a/gestioncof/forms.py +++ b/gestioncof/forms.py @@ -1,9 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - from django import forms from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User diff --git a/gestioncof/models.py b/gestioncof/models.py index 2215b296..b1bf443f 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -1,11 +1,7 @@ -# -*- coding: utf-8 -*- - from django.db import models from django.dispatch import receiver from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ -from django.utils.encoding import python_2_unicode_compatible -import django.utils.six as six from django.db.models.signals import post_save, post_delete from gestioncof.petits_cours_models import choices_length @@ -35,7 +31,6 @@ TYPE_COMMENT_FIELD = ( ) -@python_2_unicode_compatible class CofProfile(models.Model): user = models.OneToOneField(User, related_name="profile") login_clipper = models.CharField("Login clipper", max_length=8, blank=True) @@ -72,7 +67,7 @@ class CofProfile(models.Model): verbose_name_plural = "Profils COF" def __str__(self): - return six.text_type(self.user.username) + return self.user.username @receiver(post_save, sender=User) @@ -86,7 +81,6 @@ def post_delete_user(sender, instance, *args, **kwargs): instance.user.delete() -@python_2_unicode_compatible class Club(models.Model): name = models.CharField("Nom", max_length=200, unique=True) description = models.TextField("Description", blank=True) @@ -98,7 +92,6 @@ class Club(models.Model): return self.name -@python_2_unicode_compatible class Event(models.Model): title = models.CharField("Titre", max_length=200) location = models.CharField("Lieu", max_length=200) @@ -115,10 +108,9 @@ class Event(models.Model): verbose_name = "Événement" def __str__(self): - return six.text_type(self.title) + return self.title -@python_2_unicode_compatible class EventCommentField(models.Model): event = models.ForeignKey(Event, related_name="commentfields") name = models.CharField("Champ", max_length=200) @@ -130,10 +122,9 @@ class EventCommentField(models.Model): verbose_name = "Champ" def __str__(self): - return six.text_type(self.name) + return self.name -@python_2_unicode_compatible class EventCommentValue(models.Model): commentfield = models.ForeignKey(EventCommentField, related_name="values") registration = models.ForeignKey("EventRegistration", @@ -144,7 +135,6 @@ class EventCommentValue(models.Model): return "Commentaire de %s" % self.commentfield -@python_2_unicode_compatible class EventOption(models.Model): event = models.ForeignKey(Event, related_name="options") name = models.CharField("Option", max_length=200) @@ -154,10 +144,9 @@ class EventOption(models.Model): verbose_name = "Option" def __str__(self): - return six.text_type(self.name) + return self.name -@python_2_unicode_compatible class EventOptionChoice(models.Model): event_option = models.ForeignKey(EventOption, related_name="choices") value = models.CharField("Valeur", max_length=200) @@ -167,10 +156,9 @@ class EventOptionChoice(models.Model): verbose_name_plural = "Choix" def __str__(self): - return six.text_type(self.value) + return self.value -@python_2_unicode_compatible class EventRegistration(models.Model): user = models.ForeignKey(User) event = models.ForeignKey(Event) @@ -184,11 +172,9 @@ class EventRegistration(models.Model): unique_together = ("user", "event") def __str__(self): - return "Inscription de %s à %s" % (six.text_type(self.user), - six.text_type(self.event.title)) + return "Inscription de {} à {}".format(self.user, self.event.title) -@python_2_unicode_compatible class Survey(models.Model): title = models.CharField("Titre", max_length=200) details = models.TextField("Détails", blank=True) @@ -199,10 +185,9 @@ class Survey(models.Model): verbose_name = "Sondage" def __str__(self): - return six.text_type(self.title) + return self.title -@python_2_unicode_compatible class SurveyQuestion(models.Model): survey = models.ForeignKey(Survey, related_name="questions") question = models.CharField("Question", max_length=200) @@ -212,10 +197,9 @@ class SurveyQuestion(models.Model): verbose_name = "Question" def __str__(self): - return six.text_type(self.question) + return self.question -@python_2_unicode_compatible class SurveyQuestionAnswer(models.Model): survey_question = models.ForeignKey(SurveyQuestion, related_name="answers") answer = models.CharField("Réponse", max_length=200) @@ -224,10 +208,9 @@ class SurveyQuestionAnswer(models.Model): verbose_name = "Réponse" def __str__(self): - return six.text_type(self.answer) + return self.answer -@python_2_unicode_compatible class SurveyAnswer(models.Model): user = models.ForeignKey(User) survey = models.ForeignKey(Survey) @@ -244,7 +227,6 @@ class SurveyAnswer(models.Model): self.survey.title) -@python_2_unicode_compatible class CalendarSubscription(models.Model): token = models.UUIDField() user = models.OneToOneField(User) diff --git a/gestioncof/views.py b/gestioncof/views.py index 457a99c4..68cce3a2 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import unicodecsv import uuid from datetime import timedelta @@ -14,7 +12,6 @@ from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.utils import timezone from django.contrib import messages -import django.utils.six as six from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \ SurveyQuestionAnswer @@ -575,7 +572,7 @@ def export_members(request): bits = [profile.num, user.username, user.first_name, user.last_name, user.email, profile.phone, profile.occupation, profile.departement, profile.type_cotiz] - writer.writerow([six.text_type(bit) for bit in bits]) + writer.writerow([str(bit) for bit in bits]) return response @@ -594,7 +591,7 @@ def csv_export_mega(filename, qs): profile.phone, profile.num, profile.comments if profile.comments else "", comments] - writer.writerow([six.text_type(bit) for bit in bits]) + writer.writerow([str(bit) for bit in bits]) return response @@ -614,7 +611,7 @@ def export_mega_remarksonly(request): profile = user.profile bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num, profile.comments, val.content] - writer.writerow([six.text_type(bit) for bit in bits]) + writer.writerow([str(bit) for bit in bits]) return response From dba8a0a857e631c44cf41f2d7bda584c152fffc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 22 May 2017 00:06:17 +0100 Subject: [PATCH 2/6] Remove the num field in CofProfile --- gestioncof/admin.py | 5 +++-- gestioncof/forms.py | 21 +------------------ .../migrations/0011_remove_cofprofile_num.py | 18 ++++++++++++++++ gestioncof/models.py | 1 - gestioncof/views.py | 16 ++++++-------- 5 files changed, 28 insertions(+), 33 deletions(-) create mode 100644 gestioncof/migrations/0011_remove_cofprofile_num.py diff --git a/gestioncof/admin.py b/gestioncof/admin.py index 659d5018..0a2b190e 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -132,7 +132,6 @@ def ProfileInfo(field, short_description, boolean=False): User.profile_login_clipper = FkeyLookup("profile__login_clipper", "Login clipper") -User.profile_num = FkeyLookup("profile__num", "Numéro") User.profile_phone = ProfileInfo("phone", "Téléphone") User.profile_occupation = ProfileInfo("occupation", "Occupation") User.profile_departement = ProfileInfo("departement", "Departement") @@ -159,10 +158,12 @@ class UserProfileAdmin(UserAdmin): is_cof.short_description = 'Membre du COF' is_cof.boolean = True - list_display = ('profile_num',) + UserAdmin.list_display \ + list_display = ( + UserAdmin.list_display + ('profile_login_clipper', 'profile_phone', 'profile_occupation', 'profile_mailing_cof', 'profile_mailing_bda', 'profile_mailing_bda_revente', 'is_cof', 'is_buro', ) + ) list_display_links = ('username', 'email', 'first_name', 'last_name') list_filter = UserAdmin.list_filter \ + ('profile__is_cof', 'profile__is_buro', 'profile__mailing_cof', diff --git a/gestioncof/forms.py b/gestioncof/forms.py index 5ee4acf6..b52f5417 100644 --- a/gestioncof/forms.py +++ b/gestioncof/forms.py @@ -3,13 +3,11 @@ from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User from django.forms.widgets import RadioSelect, CheckboxSelectMultiple from django.forms.formsets import BaseFormSet, formset_factory -from django.db.models import Max from django.core.validators import MinLengthValidator from gestioncof.models import CofProfile, EventCommentValue, \ CalendarSubscription, Club from gestioncof.widgets import TriStateCheckbox -from gestioncof.shared import lock_table, unlock_table from bda.models import Spectacle @@ -237,7 +235,6 @@ class RegistrationProfileForm(forms.ModelForm): self.fields['mailing_cof'].initial = True self.fields['mailing_bda'].initial = True self.fields['mailing_bda_revente'].initial = True - self.fields['num'].widget.attrs['readonly'] = True self.fields.keyOrder = [ 'login_clipper', @@ -245,7 +242,6 @@ class RegistrationProfileForm(forms.ModelForm): 'occupation', 'departement', 'is_cof', - 'num', 'type_cotiz', 'mailing_cof', 'mailing_bda', @@ -253,24 +249,9 @@ class RegistrationProfileForm(forms.ModelForm): 'comments' ] - def save(self, *args, **kw): - instance = super(RegistrationProfileForm, self).save(*args, **kw) - if instance.is_cof and not instance.num: - # Generate new number - try: - lock_table(CofProfile) - aggregate = CofProfile.objects.aggregate(Max('num')) - instance.num = aggregate['num__max'] + 1 - instance.save() - self.cleaned_data['num'] = instance.num - self.data['num'] = instance.num - finally: - unlock_table(CofProfile) - return instance - class Meta: model = CofProfile - fields = ("login_clipper", "num", "phone", "occupation", + fields = ("login_clipper", "phone", "occupation", "departement", "is_cof", "type_cotiz", "mailing_cof", "mailing_bda", "mailing_bda_revente", "comments") diff --git a/gestioncof/migrations/0011_remove_cofprofile_num.py b/gestioncof/migrations/0011_remove_cofprofile_num.py new file mode 100644 index 00000000..f39ce367 --- /dev/null +++ b/gestioncof/migrations/0011_remove_cofprofile_num.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('gestioncof', '0010_delete_custommail'), + ] + + operations = [ + migrations.RemoveField( + model_name='cofprofile', + name='num', + ), + ] diff --git a/gestioncof/models.py b/gestioncof/models.py index b1bf443f..f1ad35e1 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -35,7 +35,6 @@ class CofProfile(models.Model): user = models.OneToOneField(User, related_name="profile") login_clipper = models.CharField("Login clipper", max_length=8, blank=True) is_cof = models.BooleanField("Membre du COF", default=False) - num = models.IntegerField("Numéro d'adhérent", blank=True, default=0) phone = models.CharField("Téléphone", max_length=20, blank=True) occupation = models.CharField(_("Occupation"), default="1A", diff --git a/gestioncof/views.py b/gestioncof/views.py index 68cce3a2..227a3e0a 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -400,9 +400,6 @@ def registration_form2(request, login_clipper=None, username=None, def registration(request): if request.POST: request_dict = request.POST.copy() - # num ne peut pas être défini manuellement - if "num" in request_dict: - del request_dict["num"] member = None login_clipper = None success = False @@ -442,7 +439,6 @@ def registration(request): member = user_form.save() profile, _ = CofProfile.objects.get_or_create(user=member) was_cof = profile.is_cof - request_dict["num"] = profile.num # Maintenant on remplit le formulaire de profil profile_form = RegistrationProfileForm(request_dict, instance=profile) @@ -500,11 +496,11 @@ def registration(request): # Messages if success: msg = ("L'inscription de {:s} ({:s}) a été " - "enregistrée avec succès" + "enregistrée avec succès." .format(member.get_full_name(), member.email)) if member.profile.is_cof: - msg += "Il est désormais membre du COF n°{:d} !".format( - member.profile.num) + msg += "\nIl est désormais membre du COF n°{:d} !".format( + member.profile.id) messages.success(request, msg, extra_tags='safe') return render(request, "gestioncof/registration_post.html", {"user_form": user_form, @@ -569,7 +565,7 @@ def export_members(request): writer = unicodecsv.writer(response) for profile in CofProfile.objects.filter(is_cof=True).all(): user = profile.user - bits = [profile.num, user.username, user.first_name, user.last_name, + bits = [profile.id, user.username, user.first_name, user.last_name, user.email, profile.phone, profile.occupation, profile.departement, profile.type_cotiz] writer.writerow([str(bit) for bit in bits]) @@ -588,7 +584,7 @@ def csv_export_mega(filename, qs): comments = "---".join( [comment.content for comment in reg.comments.all()]) bits = [user.username, user.first_name, user.last_name, user.email, - profile.phone, profile.num, + profile.phone, profile.id, profile.comments if profile.comments else "", comments] writer.writerow([str(bit) for bit in bits]) @@ -610,7 +606,7 @@ def export_mega_remarksonly(request): user = reg.user profile = user.profile bits = [user.username, user.first_name, user.last_name, user.email, - profile.phone, profile.num, profile.comments, val.content] + profile.phone, profile.id, profile.comments, val.content] writer.writerow([str(bit) for bit in bits]) return response From f0f15856615600de2f8e3ecf0d10b81a531c13be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 May 2017 05:41:57 +0100 Subject: [PATCH 3/6] Registration: remove success var, fix message `member.profile` was not up-to-date where we used to send the success message => move it to the place where the success var is set tu `True` and remove the success var was which becomes irrelevant. --- gestioncof/views.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/gestioncof/views.py b/gestioncof/views.py index 227a3e0a..6a728ea6 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -402,7 +402,6 @@ def registration(request): request_dict = request.POST.copy() member = None login_clipper = None - success = False # ----- # Remplissage des formulaires @@ -492,16 +491,18 @@ def registration(request): for club in clubs_form.cleaned_data['clubs']: club.membres.add(member) club.save() - success = True - # Messages - if success: - msg = ("L'inscription de {:s} ({:s}) a été " - "enregistrée avec succès." - .format(member.get_full_name(), member.email)) - if member.profile.is_cof: - msg += "\nIl est désormais membre du COF n°{:d} !".format( - member.profile.id) - messages.success(request, msg, extra_tags='safe') + + # --- + # Success + # --- + + msg = ("L'inscription de {:s} ({:s}) a été " + "enregistrée avec succès." + .format(member.get_full_name(), member.email)) + if profile.is_cof: + msg += "\nIl est désormais membre du COF n°{:d} !".format( + member.profile.id) + messages.success(request, msg, extra_tags='safe') return render(request, "gestioncof/registration_post.html", {"user_form": user_form, "profile_form": profile_form, From 3c8f1c58c551d8cc227b06fd142449e0df42d84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 May 2017 05:56:29 +0100 Subject: [PATCH 4/6] Use transaction.atomic instead of lock_table lock_table used LOCK which is mysql-specific --- gestioncof/petits_cours_views.py | 44 +++++++++++++++----------------- gestioncof/shared.py | 29 --------------------- 2 files changed, 21 insertions(+), 52 deletions(-) diff --git a/gestioncof/petits_cours_views.py b/gestioncof/petits_cours_views.py index 3fa0dc57..087c9cef 100644 --- a/gestioncof/petits_cours_views.py +++ b/gestioncof/petits_cours_views.py @@ -12,15 +12,15 @@ from django.views.decorators.csrf import csrf_exempt from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib import messages +from django.db import transaction from gestioncof.models import CofProfile from gestioncof.petits_cours_models import ( PetitCoursDemande, PetitCoursAttribution, PetitCoursAttributionCounter, - PetitCoursAbility, PetitCoursSubject + PetitCoursAbility ) from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet from gestioncof.decorators import buro_required -from gestioncof.shared import lock_table, unlock_tables class DemandeListView(ListView): @@ -274,17 +274,17 @@ def _traitement_post(request, demande): headers={'Reply-To': replyto})) connection = mail.get_connection(fail_silently=False) connection.send_messages(mails_to_send) - lock_table(PetitCoursAttributionCounter, PetitCoursAttribution, User) - for matiere in proposals: - for rank, user in enumerate(proposals[matiere]): - counter = PetitCoursAttributionCounter.objects.get(user=user, - matiere=matiere) - counter.count += 1 - counter.save() - attrib = PetitCoursAttribution(user=user, matiere=matiere, - demande=demande, rank=rank + 1) - attrib.save() - unlock_tables() + with transaction.atomic(): + for matiere in proposals: + for rank, user in enumerate(proposals[matiere]): + counter = PetitCoursAttributionCounter.objects.get( + user=user, matiere=matiere + ) + counter.count += 1 + counter.save() + attrib = PetitCoursAttribution(user=user, matiere=matiere, + demande=demande, rank=rank + 1) + attrib.save() demande.traitee = True demande.traitee_par = request.user demande.processed = datetime.now() @@ -309,17 +309,15 @@ def inscription(request): profile.petits_cours_accept = "receive_proposals" in request.POST profile.petits_cours_remarques = request.POST["remarques"] profile.save() - lock_table(PetitCoursAttributionCounter, PetitCoursAbility, User, - PetitCoursSubject) - abilities = ( - PetitCoursAbility.objects.filter(user=request.user).all() - ) - for ability in abilities: - PetitCoursAttributionCounter.get_uptodate( - ability.user, - ability.matiere + with transaction.atomic(): + abilities = ( + PetitCoursAbility.objects.filter(user=request.user).all() ) - unlock_tables() + for ability in abilities: + PetitCoursAttributionCounter.get_uptodate( + ability.user, + ability.matiere + ) success = True formset = MatieresFormSet(instance=request.user) else: diff --git a/gestioncof/shared.py b/gestioncof/shared.py index 63aceae5..e4180b49 100644 --- a/gestioncof/shared.py +++ b/gestioncof/shared.py @@ -1,15 +1,8 @@ -# -*- coding: utf-8 -*- - -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - from django.contrib.sites.models import Site from django.conf import settings from django_cas_ng.backends import CASBackend from django_cas_ng.utils import get_cas_client from django.contrib.auth import get_user_model -from django.db import connection from gestioncof.models import CofProfile @@ -74,25 +67,3 @@ def context_processor(request): "site": Site.objects.get_current(), } return data - - -def lock_table(*models): - query = "LOCK TABLES " - for i, model in enumerate(models): - table = model._meta.db_table - if i > 0: - query += ", " - query += "%s WRITE" % table - cursor = connection.cursor() - cursor.execute(query) - row = cursor.fetchone() - return row - - -def unlock_tables(*models): - cursor = connection.cursor() - cursor.execute("UNLOCK TABLES") - row = cursor.fetchone() - return row - -unlock_table = unlock_tables From 74135f8877396f090fa1f7a933ad0836bd96063d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 May 2017 16:50:43 +0100 Subject: [PATCH 5/6] enhance User.__str__ with get_full_name --- gestioncof/admin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gestioncof/admin.py b/gestioncof/admin.py index 0a2b190e..0d7d7143 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -211,9 +211,7 @@ class UserProfileAdmin(UserAdmin): # FIXME: This is absolutely horrible. def user_str(self): if self.first_name and self.last_name: - return "{} {} ({})".format( - self.first_name, self.last_name, self.username - ) + return "{} ({})".format(self.get_full_name(), self.username) else: return self.username User.__str__ = user_str From 3a69c3371f81233b0a4fbdbb187b0ad0cf763300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 23 May 2017 20:38:53 +0100 Subject: [PATCH 6/6] Simple test: we can query the account-read page --- kfet/tests/test_views.py | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 kfet/tests/test_views.py diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py new file mode 100644 index 00000000..65b953a1 --- /dev/null +++ b/kfet/tests/test_views.py @@ -0,0 +1,57 @@ +from decimal import Decimal + +from django.test import TestCase, Client +from django.contrib.auth.models import User +from django.utils import timezone + +from ..models import Account, OperationGroup, Checkout, Operation + + +class AccountTests(TestCase): + """Account related views""" + + def setUp(self): + # A user and its account + self.user = User.objects.create_user(username="foobar", password="foo") + acc = Account.objects.create( + trigramme="FOO", cofprofile=self.user.profile + ) + + # Dummy operations and operation groups + checkout = Checkout.objects.create( + created_by=acc, name="checkout", + valid_from=timezone.now(), + valid_to=timezone.now() + timezone.timedelta(days=365) + ) + opeg_data = [ + (timezone.now(), Decimal('10')), + (timezone.now() - timezone.timedelta(days=3), Decimal('3')), + ] + OperationGroup.objects.bulk_create([ + OperationGroup( + on_acc=acc, checkout=checkout, at=at, is_cof=False, + amount=amount + ) + for (at, amount) in opeg_data + ]) + self.operation_groups = OperationGroup.objects.order_by("-amount") + Operation.objects.create( + group=self.operation_groups[0], + type=Operation.PURCHASE, + amount=Decimal('10') + ) + Operation.objects.create( + group=self.operation_groups[1], + type=Operation.PURCHASE, + amount=Decimal('3') + ) + + def test_account_read(self): + """We can query the "Accout - Read" page.""" + client = Client() + self.assertTrue(client.login( + username="foobar", + password="foo" + )) + resp = client.get("/k-fet/accounts/FOO") + self.assertEqual(200, resp.status_code)