From b35483d2a965d9e6e5bf5c480f427345cb9d704b Mon Sep 17 00:00:00 2001 From: Skia Date: Tue, 15 Dec 2015 17:50:50 +0100 Subject: [PATCH] WIP: Begin ae app with subscription handling --- ae/__init__.py | 0 ae/admin.py | 3 + ae/migrations/0001_initial.py | 35 ++++++++ ae/migrations/0002_auto_20151215_1625.py | 19 ++++ ae/migrations/__init__.py | 0 ae/models.py | 107 +++++++++++++++++++++++ ae/templates/ae/subscription.html | 18 ++++ ae/tests.py | 3 + ae/urls.py | 11 +++ ae/views.py | 21 +++++ sith/settings.py | 29 ++++++ sith/urls.py | 1 + 12 files changed, 247 insertions(+) create mode 100644 ae/__init__.py create mode 100644 ae/admin.py create mode 100644 ae/migrations/0001_initial.py create mode 100644 ae/migrations/0002_auto_20151215_1625.py create mode 100644 ae/migrations/__init__.py create mode 100644 ae/models.py create mode 100644 ae/templates/ae/subscription.html create mode 100644 ae/tests.py create mode 100644 ae/urls.py create mode 100644 ae/views.py diff --git a/ae/__init__.py b/ae/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ae/admin.py b/ae/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/ae/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/ae/migrations/0001_initial.py b/ae/migrations/0001_initial.py new file mode 100644 index 00000000..e36abd65 --- /dev/null +++ b/ae/migrations/0001_initial.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_auto_20151215_0827'), + ] + + operations = [ + migrations.CreateModel( + name='Member', + fields=[ + ('user', models.OneToOneField(primary_key=True, to=settings.AUTH_USER_MODEL, serialize=False)), + ], + ), + migrations.CreateModel( + name='Subscription', + fields=[ + ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), + ('subscription_type', models.CharField(choices=[('cursus branche', 'Cursus Branche'), ('cursus tronc commun', 'Cursus Tronc Commun'), ('deux semestres', 'Deux semestres'), ('un semestre', 'Un semestre')], verbose_name='subscription type', max_length=255)), + ('subscription_start', models.DateField(verbose_name='subscription start')), + ('subscription_end', models.DateField(verbose_name='subscription end')), + ('payment_method', models.CharField(choices=[('cheque', 'Chèque'), ('cash', 'Espèce'), ('other', 'Autre')], verbose_name='payment method', max_length=255)), + ('member', models.ForeignKey(related_name='subscriptions', to='ae.Member')), + ], + options={ + 'ordering': ['subscription_start'], + }, + ), + ] diff --git a/ae/migrations/0002_auto_20151215_1625.py b/ae/migrations/0002_auto_20151215_1625.py new file mode 100644 index 00000000..9a3a8828 --- /dev/null +++ b/ae/migrations/0002_auto_20151215_1625.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ae', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='subscription', + name='subscription_type', + field=models.CharField(verbose_name='subscription type', max_length=255, choices=[('cursus-branche', 'Cursus Branche'), ('cursus-tronc-commun', 'Cursus Tronc Commun'), ('deux-semestres', 'Deux semestres'), ('un-semestre', 'Un semestre')]), + ), + ] diff --git a/ae/migrations/__init__.py b/ae/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ae/models.py b/ae/models.py new file mode 100644 index 00000000..1703f519 --- /dev/null +++ b/ae/models.py @@ -0,0 +1,107 @@ +from datetime import date, timedelta +from django.db import models +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ +from django.conf import settings + +from core.models import User + +def validate_type(value): + if value not in settings.AE_SUBSCRIPTIONS.keys(): + raise ValidationError(_('Bad subscription type')) + +def validate_payment(value): + if value not in settings.AE_PAYMENT_METHOD: + raise ValidationError(_('Bad payment method')) + +class Member(models.Model): + user = models.OneToOneField(User, primary_key=True) + + def is_subscribed(self): + return self.subscriptions.last().is_valid_now() + +class Subscription(models.Model): + member = models.ForeignKey(Member, related_name='subscriptions') + subscription_type = models.CharField(_('subscription type'), + max_length=255, + choices=((k.lower().replace(' ', '-'), k) for k in sorted(settings.AE_SUBSCRIPTIONS.keys()))) + subscription_start = models.DateField(_('subscription start')) + subscription_end = models.DateField(_('subscription end')) + payment_method = models.CharField(_('payment method'), max_length=255, choices=settings.AE_PAYMENT_METHOD) + + class Meta: + ordering = ['subscription_start',] + + @staticmethod + def compute_start(d=date.today()): + """ + This function computes the start date of the subscription with respect to the given date (default is today), + and the start date given in settings.AE_START_DATE. + It takes the nearest past start date. + Exemples: with AE_START_DATE = (8, 15) + Today -> Start date + 2015-03-17 -> 2015-02-15 + 2015-01-11 -> 2014-08-15 + """ + today = d + year = today.year + start = date(year, settings.AE_START_DATE[0], settings.AE_START_DATE[1]) + start2 = start.replace(month=(start.month+6)%12) + if start > start2: + start, start2 = start2, start + if today < start: + return start2.replace(year=year-1) + elif today < start2: + return start + else: + return start2 + + @staticmethod + def compute_end(duration, start=None): + """ + This function compute the end date of the subscription given a start date and a duration in number of semestre + Exemple: + Start - Duration -> End date + 2015-09-18 - 1 -> 2016-03-18 + 2015-09-18 - 2 -> 2016-09-18 + 2015-09-18 - 3 -> 2017-03-18 + 2015-09-18 - 4 -> 2017-09-18 + """ + if start is None: + start = Subscription.compute_start() + # This can certainly be simplified, but it works like this + return start.replace(month=(start.month+6*duration)%12, + year=start.year+int(duration/2)+(1 if start.month > 6 and duration%2 == 1 else 0)) + + def is_valid_now(self): + return self.subscription_start <= date.today() and date.today() <= self.subscription_end + +def guy_test(date): + print(str(date)+" -> "+str(Subscription.compute_start(date))) +def bibou_test(duration, date=None): + print(str(date)+" - "+str(duration)+" -> "+str(Subscription.compute_end(duration, date))) +def guy(): + guy_test(date(2015, 7, 11)) + guy_test(date(2015, 8, 11)) + guy_test(date(2015, 2, 17)) + guy_test(date(2015, 3, 17)) + guy_test(date(2015, 1, 11)) + guy_test(date(2015, 2, 11)) + guy_test(date(2015, 8, 17)) + guy_test(date(2015, 9, 17)) + print('='*80) + bibou_test(1, date(2015, 2, 18)) + bibou_test(2, date(2015, 2, 18)) + bibou_test(3, date(2015, 2, 18)) + bibou_test(4, date(2015, 2, 18)) + bibou_test(1, date(2015, 9, 18)) + bibou_test(2, date(2015, 9, 18)) + bibou_test(3, date(2015, 9, 18)) + bibou_test(4, date(2015, 9, 18)) + bibou_test(1) + bibou_test(2) + bibou_test(3) + bibou_test(4) + +if __name__ == "__main__": + guy() diff --git a/ae/templates/ae/subscription.html b/ae/templates/ae/subscription.html new file mode 100644 index 00000000..2e87e5a6 --- /dev/null +++ b/ae/templates/ae/subscription.html @@ -0,0 +1,18 @@ +{% extends "core/base.html" %} + +{% block title %} +{% if profile %} +Edit {{ member.user.get_display_name }}'s subscription +{% endif %} +{% endblock %} + +{% block content %} +

Edit subscription

+{#

Go to profile

#} +{#

You're editing the subscription of {{ member.user.get_display_name }}

#} +
+ {% csrf_token %} + {{ form.as_p }} +

+
+{% endblock %} diff --git a/ae/tests.py b/ae/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/ae/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ae/urls.py b/ae/urls.py new file mode 100644 index 00000000..b550ad04 --- /dev/null +++ b/ae/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url, include + +from ae.views import * + +urlpatterns = [ + # Subscription views + url(r'^subscription/(?P[0-9]+)/$', NewSubscription.as_view(), name='subscription'), +] + + + diff --git a/ae/views.py b/ae/views.py new file mode 100644 index 00000000..c760693b --- /dev/null +++ b/ae/views.py @@ -0,0 +1,21 @@ +from django.shortcuts import render +from django.views.generic.edit import UpdateView, CreateView +from django import forms +from django.forms import Select +from django.conf import settings + +from ae.models import Member, Subscription +from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin + +class SubscriptionForm(forms.ModelForm): + class Meta: + model = Subscription + fields = ['subscription_type', 'payment_method'] + #widgets = { + # 'subscription_type': Select(choices={(k.lower(), k+" - "+str(v['price'])+"€"+str(Subscription.compute_end(2))) for k,v in settings.AE_SUBSCRIPTIONS.items()}), + #} + + +class NewSubscription(CanEditMixin, CreateView): + template_name = 'ae/subscription.html' + form_class = SubscriptionForm diff --git a/sith/settings.py b/sith/settings.py index fa04c23a..91d73915 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -38,6 +38,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'core', + 'ae', ) MIDDLEWARE_CLASSES = ( @@ -117,6 +118,9 @@ EMAIL_HOST="localhost" EMAIL_PORT=25 # AE configuration +# Define the date in the year serving as reference for the subscriptions calendar +# (month, day) +AE_START_DATE = (8, 15) # 15th August AE_GROUPS = { 'root': { 'id': 1, @@ -136,3 +140,28 @@ AE_GROUPS = { }, } +AE_PAYMENT_METHOD = [('cheque', 'Chèque'), + ('cash', 'Espèce'), + ('other', 'Autre'), + ] + +# Subscription durations are in semestres (should be settingized) +AE_SUBSCRIPTIONS = { + 'Un semestre': { + 'price': 15, + 'duration': 1, + }, + 'Deux semestres': { + 'price': 28, + 'duration': 2, + }, + 'Cursus Tronc Commun': { + 'price': 45, + 'duration': 4, + }, + 'Cursus Branche': { + 'price': 45, + 'duration': 6, + }, +# To be completed.... +} diff --git a/sith/urls.py b/sith/urls.py index aacf3e11..cd36ee73 100644 --- a/sith/urls.py +++ b/sith/urls.py @@ -21,5 +21,6 @@ handler404 = "core.views.not_found" urlpatterns = [ url(r'^', include('core.urls', namespace="core", app_name="core")), + url(r'^ae/', include('ae.urls', namespace="ae", app_name="ae")), url(r'^admin/', include(admin.site.urls)), ]