models.py 9.99 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

25 26 27
from datetime import date, timedelta
from django.db import models
from django.utils import timezone
Sli's avatar
Sli committed
28
from ast import literal_eval as make_tuple
29 30
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
31 32
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
33
from django.contrib.auth.forms import PasswordResetForm
34

35 36 37 38
from dateutil.relativedelta import relativedelta

import math

39
from core.models import User
40
from core.utils import get_start_of_semester
41

Skia's avatar
Skia committed
42

43
def validate_type(value):
Skia's avatar
Skia committed
44
    if value not in settings.SITH_SUBSCRIPTIONS.keys():
Sli's avatar
Sli committed
45
        raise ValidationError(_("Bad subscription type"))
46

Krophil's avatar
Krophil committed
47

48
def validate_payment(value):
49
    if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD:
Sli's avatar
Sli committed
50
        raise ValidationError(_("Bad payment method"))
51

Krophil's avatar
Krophil committed
52

53
class Subscription(models.Model):
Sli's avatar
Sli committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    member = models.ForeignKey(User, related_name="subscriptions")
    subscription_type = models.CharField(
        _("subscription type"),
        max_length=255,
        choices=(
            (k, v["name"]) for k, v in sorted(settings.SITH_SUBSCRIPTIONS.items())
        ),
    )
    subscription_start = models.DateField(_("subscription start"))
    subscription_end = models.DateField(_("subscription end"))
    payment_method = models.CharField(
        _("payment method"),
        max_length=255,
        choices=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD,
    )
    location = models.CharField(
        choices=settings.SITH_SUBSCRIPTION_LOCATIONS,
        max_length=20,
        verbose_name=_("location"),
    )
74

Sli's avatar
Sli committed
75 76 77 78
    def get_payment_method_display(self):

        return _(make_tuple(self.payment_method)[1])

79
    class Meta:
Sli's avatar
Sli committed
80
        ordering = ["subscription_start"]
81 82

    def clean(self):
83
        try:
Sli's avatar
Sli committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97
            for s in (
                Subscription.objects.filter(member=self.member)
                .exclude(pk=self.pk)
                .all()
            ):
                if (
                    s.is_valid_now()
                    and s.subscription_end
                    - timedelta(weeks=settings.SITH_SUBSCRIPTION_END)
                    > date.today()
                ):
                    raise ValidationError(
                        _("You can not subscribe many time for the same period")
                    )
Krophil's avatar
Krophil committed
98
        except:  # This should not happen, because the form should have handled the data before, but sadly, it still
Sli's avatar
Sli committed
99 100
            # calls the model validation :'(
            # TODO see SubscriptionForm's clean method
Skia's avatar
Skia committed
101
            raise ValidationError(_("Subscription error"))
102

103 104 105
    def save(self):
        super(Subscription, self).save()
        from counter.models import Customer
Sli's avatar
Sli committed
106

107
        if not Customer.objects.filter(user=self.member).exists():
Sli's avatar
Sli committed
108 109 110 111 112 113 114 115 116
            last_id = (
                Customer.objects.count() + 1504
            )  # Number to keep a continuity with the old site
            Customer(
                user=self.member,
                account_id=Customer.generate_account_id(last_id + 1),
                amount=0,
            ).save()
            form = PasswordResetForm({"email": self.member.email})
117
            if form.is_valid():
Sli's avatar
Sli committed
118 119 120 121 122 123
                form.save(
                    use_https=True,
                    email_template_name="core/new_user_email.jinja",
                    subject_template_name="core/new_user_email_subject.jinja",
                    from_email="ae@utbm.fr",
                )
Skia's avatar
Skia committed
124
        self.member.make_home()
Sli's avatar
Sli committed
125
        if settings.IS_OLD_MYSQL_PRESENT:
Skia's avatar
Skia committed
126
            import MySQLdb
Sli's avatar
Sli committed
127

Krophil's avatar
Krophil committed
128
            try:  # Create subscription on the old site: TODO remove me!
Sli's avatar
Sli committed
129
                LOCATION = {"SEVENANS": 5, "BELFORT": 6, "MONTBELIARD": 9, "EBOUTIC": 5}
Sli's avatar
Sli committed
130
                TYPE = {
Sli's avatar
Sli committed
131 132 133 134 135 136 137 138 139 140 141 142 143
                    "un-semestre": 0,
                    "deux-semestres": 1,
                    "cursus-tronc-commun": 2,
                    "cursus-branche": 3,
                    "membre-honoraire": 4,
                    "assidu": 5,
                    "amicale/doceo": 6,
                    "reseau-ut": 7,
                    "crous": 8,
                    "sbarro/esta": 9,
                    "cursus-alternant": 10,
                    "welcome-semestre": 11,
                    "deux-mois-essai": 12,
Krophil's avatar
Krophil committed
144
                }
Sli's avatar
Sli committed
145
                PAYMENT = {
Krophil's avatar
Krophil committed
146 147 148 149 150 151 152
                    "CHECK": 1,
                    "CARD": 2,
                    "CASH": 3,
                    "OTHER": 4,
                    "EBOUTIC": 5,
                    "OTHER": 0,
                }
Sli's avatar
Sli committed
153 154 155

                db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
                c = db.cursor()
Sli's avatar
Sli committed
156 157 158 159 160 161 162 163 164 165 166 167
                c.execute(
                    """INSERT INTO ae_cotisations (id_utilisateur, date_cotis, date_fin_cotis, mode_paiement_cotis,
                type_cotis, id_comptoir) VALUES (%s, %s, %s, %s, %s, %s)""",
                    (
                        self.member.id,
                        self.subscription_start,
                        self.subscription_end,
                        PAYMENT[self.payment_method],
                        TYPE[self.subscription_type],
                        LOCATION[self.location],
                    ),
                )
Sli's avatar
Sli committed
168 169
                db.commit()
            except Exception as e:
Krophil's avatar
Krophil committed
170
                with open(settings.BASE_DIR + "/subscription_fail.log", "a") as f:
Sli's avatar
Sli committed
171 172 173 174
                    print(
                        "FAIL to add subscription to %s to old site" % (self.member),
                        file=f,
                    )
Sli's avatar
Sli committed
175 176
                    print("Reason: %s" % (repr(e)), file=f)
                db.rollback()
177

178
    def get_absolute_url(self):
Sli's avatar
Sli committed
179
        return reverse("core:user_edit", kwargs={"user_id": self.member.pk})
180

181
    def __str__(self):
182
        if hasattr(self, "member") and self.member is not None:
Sli's avatar
Sli committed
183
            return self.member.username + " - " + str(self.pk)
184
        else:
Sli's avatar
Sli committed
185
            return "No user - " + str(self.pk)
186

187
    @staticmethod
188
    def compute_start(d=None, duration=1, user=None):
189 190
        """
        This function computes the start date of the subscription with respect to the given date (default is today),
Skia's avatar
Skia committed
191
        and the start date given in settings.SITH_START_DATE.
192
        It takes the nearest past start date.
Skia's avatar
Skia committed
193
        Exemples: with SITH_START_DATE = (8, 15)
194 195 196 197
            Today      -> Start date
            2015-03-17 -> 2015-02-15
            2015-01-11 -> 2014-08-15
        """
Skia's avatar
Skia committed
198 199
        if not d:
            d = date.today()
200
        if user is not None and user.subscriptions.exists():
Skia's avatar
Skia committed
201 202 203
            last = user.subscriptions.last()
            if last.is_valid_now():
                d = last.subscription_end
Krophil's avatar
Krophil committed
204
        if duration <= 2:  # Sliding subscriptions for 1 or 2 semesters
205
            return d
206
        return get_start_of_semester(d)
207 208

    @staticmethod
209
    def compute_end(duration, start=None, user=None):
210 211 212 213 214 215 216 217 218 219
        """
        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:
220 221
            start = Subscription.compute_start(duration=duration, user=user)

Sli's avatar
Sli committed
222 223 224 225 226
        return start + relativedelta(
            months=round(6 * duration),
            days=math.ceil((6 * duration - round(6 * duration)) * 30),
        )

Skia's avatar
Skia committed
227
    def can_be_edited_by(self, user):
Skia's avatar
Skia committed
228
        return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
Skia's avatar
Skia committed
229

230
    def is_valid_now(self):
Sli's avatar
Sli committed
231 232 233 234
        return (
            self.subscription_start <= date.today()
            and date.today() <= self.subscription_end
        )
235

Krophil's avatar
Krophil committed
236

237
def guy_test(date, duration=4):
Sli's avatar
Sli committed
238 239 240 241 242 243 244
    print(
        str(date)
        + " - "
        + str(duration)
        + " -> "
        + str(Subscription.compute_start(date, duration))
    )
Krophil's avatar
Krophil committed
245 246


247
def bibou_test(duration, date=date.today()):
Sli's avatar
Sli committed
248 249 250 251 252 253 254 255 256 257 258
    print(
        str(date)
        + " - "
        + str(duration)
        + " -> "
        + str(
            Subscription.compute_end(
                duration, Subscription.compute_start(date, duration)
            )
        )
    )
Krophil's avatar
Krophil committed
259 260


261 262 263 264 265 266 267 268 269
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))
Sli's avatar
Sli committed
270
    print("=" * 80)
271 272 273 274 275 276 277 278
    guy_test(date(2015, 7, 11), 1)
    guy_test(date(2015, 8, 11), 2)
    guy_test(date(2015, 2, 17), 3)
    guy_test(date(2015, 3, 17), 4)
    guy_test(date(2015, 1, 11), 1)
    guy_test(date(2015, 2, 11), 2)
    guy_test(date(2015, 8, 17), 3)
    guy_test(date(2015, 9, 17), 4)
Sli's avatar
Sli committed
279
    print("=" * 80)
280 281 282 283 284 285 286 287
    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))
Sli's avatar
Sli committed
288
    print("=" * 80)
Skia's avatar
Skia committed
289
    bibou_test(1, date(2000, 2, 29))
290 291 292
    bibou_test(2, date(2000, 2, 29))
    bibou_test(1, date(2000, 5, 31))
    bibou_test(1, date(2000, 7, 31))
293 294 295 296 297
    bibou_test(1)
    bibou_test(2)
    bibou_test(3)
    bibou_test(4)

Krophil's avatar
Krophil committed
298

299 300
if __name__ == "__main__":
    guy()