mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-25 10:34:21 +00:00
commit
fbcf525378
@ -35,5 +35,3 @@ admin.site.register(SimplifiedAccountingType)
|
|||||||
admin.site.register(Operation)
|
admin.site.register(Operation)
|
||||||
admin.site.register(Label)
|
admin.site.register(Label)
|
||||||
admin.site.register(Company)
|
admin.site.register(Company)
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.db.models import Count
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@ -37,6 +36,7 @@ from decimal import Decimal
|
|||||||
from core.models import User, SithFile
|
from core.models import User, SithFile
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
|
|
||||||
|
|
||||||
class CurrencyField(models.DecimalField):
|
class CurrencyField(models.DecimalField):
|
||||||
"""
|
"""
|
||||||
This is a custom database field used for currency
|
This is a custom database field used for currency
|
||||||
@ -50,12 +50,13 @@ class CurrencyField(models.DecimalField):
|
|||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
try:
|
try:
|
||||||
return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
|
return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Accounting classes
|
# Accounting classes
|
||||||
|
|
||||||
|
|
||||||
class Company(models.Model):
|
class Company(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=60)
|
name = models.CharField(_('name'), max_length=60)
|
||||||
street = models.CharField(_('street'), max_length=60, blank=True)
|
street = models.CharField(_('street'), max_length=60, blank=True)
|
||||||
@ -104,6 +105,7 @@ class Company(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class BankAccount(models.Model):
|
class BankAccount(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=30)
|
name = models.CharField(_('name'), max_length=30)
|
||||||
iban = models.CharField(_('iban'), max_length=255, blank=True)
|
iban = models.CharField(_('iban'), max_length=255, blank=True)
|
||||||
@ -131,6 +133,7 @@ class BankAccount(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class ClubAccount(models.Model):
|
class ClubAccount(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=30)
|
name = models.CharField(_('name'), max_length=30)
|
||||||
club = models.ForeignKey(Club, related_name="club_account", verbose_name=_("club"))
|
club = models.ForeignKey(Club, related_name="club_account", verbose_name=_("club"))
|
||||||
@ -244,6 +247,7 @@ class GeneralJournal(models.Model):
|
|||||||
self.amount -= o.amount
|
self.amount -= o.amount
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
class Operation(models.Model):
|
class Operation(models.Model):
|
||||||
"""
|
"""
|
||||||
An operation is a line in the journal, a debit or a credit
|
An operation is a line in the journal, a debit or a credit
|
||||||
@ -258,17 +262,17 @@ class Operation(models.Model):
|
|||||||
invoice = models.ForeignKey(SithFile, related_name='operations', verbose_name=_("invoice"), null=True, blank=True)
|
invoice = models.ForeignKey(SithFile, related_name='operations', verbose_name=_("invoice"), null=True, blank=True)
|
||||||
done = models.BooleanField(_('is done'), default=False)
|
done = models.BooleanField(_('is done'), default=False)
|
||||||
simpleaccounting_type = models.ForeignKey('SimplifiedAccountingType', related_name="operations",
|
simpleaccounting_type = models.ForeignKey('SimplifiedAccountingType', related_name="operations",
|
||||||
verbose_name=_("simple type"), null=True, blank=True)
|
verbose_name=_("simple type"), null=True, blank=True)
|
||||||
accounting_type = models.ForeignKey('AccountingType', related_name="operations",
|
accounting_type = models.ForeignKey('AccountingType', related_name="operations",
|
||||||
verbose_name=_("accounting type"), null=True, blank=True)
|
verbose_name=_("accounting type"), null=True, blank=True)
|
||||||
label = models.ForeignKey('Label', related_name="operations",
|
label = models.ForeignKey('Label', related_name="operations",
|
||||||
verbose_name=_("label"), null=True, blank=True, on_delete=models.SET_NULL)
|
verbose_name=_("label"), null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
target_type = models.CharField(_('target type'), max_length=10,
|
target_type = models.CharField(_('target type'), max_length=10,
|
||||||
choices=[('USER', _('User')), ('CLUB', _('Club')), ('ACCOUNT', _('Account')), ('COMPANY', _('Company')), ('OTHER', _('Other'))])
|
choices=[('USER', _('User')), ('CLUB', _('Club')), ('ACCOUNT', _('Account')), ('COMPANY', _('Company')), ('OTHER', _('Other'))])
|
||||||
target_id = models.IntegerField(_('target id'), null=True, blank=True)
|
target_id = models.IntegerField(_('target id'), null=True, blank=True)
|
||||||
target_label = models.CharField(_('target label'), max_length=32, default="", blank=True)
|
target_label = models.CharField(_('target label'), max_length=32, default="", blank=True)
|
||||||
linked_operation = models.OneToOneField('self', related_name='operation_linked_to', verbose_name=_("linked operation"),
|
linked_operation = models.OneToOneField('self', related_name='operation_linked_to', verbose_name=_("linked operation"),
|
||||||
null=True, blank=True, default=None)
|
null=True, blank=True, default=None)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('number', 'journal')
|
unique_together = ('number', 'journal')
|
||||||
@ -349,8 +353,9 @@ class Operation(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%d € | %s | %s | %s" % (
|
return "%d € | %s | %s | %s" % (
|
||||||
self.amount, self.date, self.accounting_type, self.done,
|
self.amount, self.date, self.accounting_type, self.done,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AccountingType(models.Model):
|
class AccountingType(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -359,13 +364,13 @@ class AccountingType(models.Model):
|
|||||||
Thoses are numbers used in accounting to classify operations
|
Thoses are numbers used in accounting to classify operations
|
||||||
"""
|
"""
|
||||||
code = models.CharField(_('code'), max_length=16,
|
code = models.CharField(_('code'), max_length=16,
|
||||||
validators=[
|
validators=[
|
||||||
validators.RegexValidator(r'^[0-9]*$', _('An accounting type code contains only numbers')),
|
validators.RegexValidator(r'^[0-9]*$', _('An accounting type code contains only numbers')),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
label = models.CharField(_('label'), max_length=128)
|
label = models.CharField(_('label'), max_length=128)
|
||||||
movement_type = models.CharField(_('movement type'), choices=[('CREDIT', _('Credit')), ('DEBIT', _('Debit')),
|
movement_type = models.CharField(_('movement type'), choices=[('CREDIT', _('Credit')), ('DEBIT', _('Debit')),
|
||||||
('NEUTRAL', _('Neutral'))], max_length=12)
|
('NEUTRAL', _('Neutral'))], max_length=12)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("accounting type")
|
verbose_name = _("accounting type")
|
||||||
@ -383,7 +388,8 @@ class AccountingType(models.Model):
|
|||||||
return reverse('accounting:type_list')
|
return reverse('accounting:type_list')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.code+" - "+self.get_movement_type_display()+" - "+self.label
|
return self.code + " - " + self.get_movement_type_display() + " - " + self.label
|
||||||
|
|
||||||
|
|
||||||
class SimplifiedAccountingType(models.Model):
|
class SimplifiedAccountingType(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -391,7 +397,7 @@ class SimplifiedAccountingType(models.Model):
|
|||||||
"""
|
"""
|
||||||
label = models.CharField(_('label'), max_length=128)
|
label = models.CharField(_('label'), max_length=128)
|
||||||
accounting_type = models.ForeignKey(AccountingType, related_name="simplified_types",
|
accounting_type = models.ForeignKey(AccountingType, related_name="simplified_types",
|
||||||
verbose_name=_("simplified accounting types"))
|
verbose_name=_("simplified accounting types"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("simplified type")
|
verbose_name = _("simplified type")
|
||||||
@ -408,7 +414,8 @@ class SimplifiedAccountingType(models.Model):
|
|||||||
return reverse('accounting:simple_type_list')
|
return reverse('accounting:simple_type_list')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.get_movement_type_display()+" - "+self.accounting_type.code+" - "+self.label
|
return self.get_movement_type_display() + " - " + self.accounting_type.code + " - " + self.label
|
||||||
|
|
||||||
|
|
||||||
class Label(models.Model):
|
class Label(models.Model):
|
||||||
"""Label allow a club to sort its operations"""
|
"""Label allow a club to sort its operations"""
|
||||||
@ -432,4 +439,3 @@ class Label(models.Model):
|
|||||||
|
|
||||||
def can_be_viewed_by(self, user):
|
def can_be_viewed_by(self, user):
|
||||||
return self.club_account.can_be_viewed_by(user)
|
return self.club_account.can_be_viewed_by(user)
|
||||||
|
|
||||||
|
@ -22,15 +22,12 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.test import Client, TestCase
|
from django.test import TestCase
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.conf import settings
|
from datetime import date
|
||||||
from datetime import date, datetime
|
|
||||||
|
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from counter.models import Counter
|
|
||||||
from accounting.models import GeneralJournal, Operation, Label, AccountingType, SimplifiedAccountingType
|
from accounting.models import GeneralJournal, Operation, Label, AccountingType, SimplifiedAccountingType
|
||||||
|
|
||||||
|
|
||||||
@ -72,10 +69,11 @@ class RefoundAccountTest(TestCase):
|
|||||||
self.assertFalse(response_post.status_code == 403)
|
self.assertFalse(response_post.status_code == 403)
|
||||||
self.assertTrue(self.skia.customer.amount == 0)
|
self.assertTrue(self.skia.customer.amount == 0)
|
||||||
|
|
||||||
|
|
||||||
class JournalTest(TestCase):
|
class JournalTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
call_command("populate")
|
call_command("populate")
|
||||||
self.journal = GeneralJournal.objects.filter(id = 1).first()
|
self.journal = GeneralJournal.objects.filter(id=1).first()
|
||||||
|
|
||||||
def test_permission_granted(self):
|
def test_permission_granted(self):
|
||||||
self.client.login(username='comptable', password='plop')
|
self.client.login(username='comptable', password='plop')
|
||||||
@ -91,115 +89,116 @@ class JournalTest(TestCase):
|
|||||||
self.assertTrue(response_get.status_code == 403)
|
self.assertTrue(response_get.status_code == 403)
|
||||||
self.assertFalse('<td>M\xc3\xa9thode de paiement</td>' in str(response_get.content))
|
self.assertFalse('<td>M\xc3\xa9thode de paiement</td>' in str(response_get.content))
|
||||||
|
|
||||||
|
|
||||||
class OperationTest(TestCase):
|
class OperationTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
call_command("populate")
|
call_command("populate")
|
||||||
self.journal = GeneralJournal.objects.filter(id = 1).first()
|
self.journal = GeneralJournal.objects.filter(id=1).first()
|
||||||
self.skia = User.objects.filter(username='skia').first()
|
self.skia = User.objects.filter(username='skia').first()
|
||||||
at = AccountingType(code='443', label="Ce code n'existe pas", movement_type='CREDIT')
|
at = AccountingType(code='443', label="Ce code n'existe pas", movement_type='CREDIT')
|
||||||
at.save()
|
at.save()
|
||||||
l = Label(club_account= self.journal.club_account, name='bob')
|
l = Label(club_account=self.journal.club_account, name='bob')
|
||||||
l.save()
|
l.save()
|
||||||
self.client.login(username='comptable', password='plop')
|
self.client.login(username='comptable', password='plop')
|
||||||
self.op1 = Operation(journal=self.journal, date=date.today(), amount=1,
|
self.op1 = Operation(journal=self.journal, date=date.today(), amount=1,
|
||||||
remark="Test bilan", mode='CASH', done=True, label=l,
|
remark="Test bilan", mode='CASH', done=True, label=l,
|
||||||
accounting_type=at, target_type='USER', target_id=self.skia.id)
|
accounting_type=at, target_type='USER', target_id=self.skia.id)
|
||||||
self.op1.save()
|
self.op1.save()
|
||||||
self.op2 = Operation(journal=self.journal, date=date.today(), amount=2,
|
self.op2 = Operation(journal=self.journal, date=date.today(), amount=2,
|
||||||
remark="Test bilan", mode='CASH', done=True, label=l,
|
remark="Test bilan", mode='CASH', done=True, label=l,
|
||||||
accounting_type=at, target_type='USER', target_id=self.skia.id)
|
accounting_type=at, target_type='USER', target_id=self.skia.id)
|
||||||
self.op2.save()
|
self.op2.save()
|
||||||
|
|
||||||
def test_new_operation(self):
|
def test_new_operation(self):
|
||||||
self.client.login(username='comptable', password='plop')
|
self.client.login(username='comptable', password='plop')
|
||||||
at = AccountingType.objects.filter(code = '604').first()
|
at = AccountingType.objects.filter(code='604').first()
|
||||||
response = self.client.post(reverse('accounting:op_new',
|
response = self.client.post(reverse('accounting:op_new',
|
||||||
args=[self.journal.id]),
|
args=[self.journal.id]),
|
||||||
{'amount': 30,
|
{'amount': 30,
|
||||||
'remark' : "Un gros test",
|
'remark': "Un gros test",
|
||||||
'journal' : self.journal.id,
|
'journal': self.journal.id,
|
||||||
'target_type' : 'OTHER',
|
'target_type': 'OTHER',
|
||||||
'target_id' : '',
|
'target_id': '',
|
||||||
'target_label' : "Le fantome de la nuit",
|
'target_label': "Le fantome de la nuit",
|
||||||
'date' : '04/12/2020',
|
'date': '04/12/2020',
|
||||||
'mode' : 'CASH',
|
'mode': 'CASH',
|
||||||
'cheque_number' : '',
|
'cheque_number': '',
|
||||||
'invoice' : '',
|
'invoice': '',
|
||||||
'simpleaccounting_type' : '',
|
'simpleaccounting_type': '',
|
||||||
'accounting_type': at.id,
|
'accounting_type': at.id,
|
||||||
'label' : '',
|
'label': '',
|
||||||
'done' : False,
|
'done': False,
|
||||||
})
|
})
|
||||||
self.assertFalse(response.status_code == 403)
|
self.assertFalse(response.status_code == 403)
|
||||||
self.assertTrue(self.journal.operations.filter(target_label = "Le fantome de la nuit").exists())
|
self.assertTrue(self.journal.operations.filter(target_label="Le fantome de la nuit").exists())
|
||||||
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
|
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
|
||||||
self.assertTrue('<td>Le fantome de la nuit</td>' in str(response_get.content))
|
self.assertTrue('<td>Le fantome de la nuit</td>' in str(response_get.content))
|
||||||
|
|
||||||
def test_bad_new_operation(self):
|
def test_bad_new_operation(self):
|
||||||
self.client.login(username='comptable', password='plop')
|
self.client.login(username='comptable', password='plop')
|
||||||
at = AccountingType.objects.filter(code = '604').first()
|
AccountingType.objects.filter(code='604').first()
|
||||||
response = self.client.post(reverse('accounting:op_new',
|
response = self.client.post(reverse('accounting:op_new',
|
||||||
args=[self.journal.id]),
|
args=[self.journal.id]),
|
||||||
{'amount': 30,
|
{'amount': 30,
|
||||||
'remark' : "Un gros test",
|
'remark': "Un gros test",
|
||||||
'journal' : self.journal.id,
|
'journal': self.journal.id,
|
||||||
'target_type' : 'OTHER',
|
'target_type': 'OTHER',
|
||||||
'target_id' : '',
|
'target_id': '',
|
||||||
'target_label' : "Le fantome de la nuit",
|
'target_label': "Le fantome de la nuit",
|
||||||
'date' : '04/12/2020',
|
'date': '04/12/2020',
|
||||||
'mode' : 'CASH',
|
'mode': 'CASH',
|
||||||
'cheque_number' : '',
|
'cheque_number': '',
|
||||||
'invoice' : '',
|
'invoice': '',
|
||||||
'simpleaccounting_type' : '',
|
'simpleaccounting_type': '',
|
||||||
'accounting_type': '',
|
'accounting_type': '',
|
||||||
'label' : '',
|
'label': '',
|
||||||
'done' : False,
|
'done': False,
|
||||||
})
|
})
|
||||||
self.assertTrue('Vous devez fournir soit un type comptable simplifi\\xc3\\xa9 ou un type comptable standard' in str(response.content))
|
self.assertTrue('Vous devez fournir soit un type comptable simplifi\\xc3\\xa9 ou un type comptable standard' in str(response.content))
|
||||||
|
|
||||||
def test_new_operation_not_authorized(self):
|
def test_new_operation_not_authorized(self):
|
||||||
self.client.login(username='skia', password='plop')
|
self.client.login(username='skia', password='plop')
|
||||||
at = AccountingType.objects.filter(code = '604').first()
|
at = AccountingType.objects.filter(code='604').first()
|
||||||
response = self.client.post(reverse('accounting:op_new',
|
response = self.client.post(reverse('accounting:op_new',
|
||||||
args=[self.journal.id]),
|
args=[self.journal.id]),
|
||||||
{'amount': 30,
|
{'amount': 30,
|
||||||
'remark' : "Un gros test",
|
'remark': "Un gros test",
|
||||||
'journal' : self.journal.id,
|
'journal': self.journal.id,
|
||||||
'target_type' : 'OTHER',
|
'target_type': 'OTHER',
|
||||||
'target_id' : '',
|
'target_id': '',
|
||||||
'target_label' : "Le fantome du jour",
|
'target_label': "Le fantome du jour",
|
||||||
'date' : '04/12/2020',
|
'date': '04/12/2020',
|
||||||
'mode' : 'CASH',
|
'mode': 'CASH',
|
||||||
'cheque_number' : '',
|
'cheque_number': '',
|
||||||
'invoice' : '',
|
'invoice': '',
|
||||||
'simpleaccounting_type' : '',
|
'simpleaccounting_type': '',
|
||||||
'accounting_type': at.id,
|
'accounting_type': at.id,
|
||||||
'label' : '',
|
'label': '',
|
||||||
'done' : False,
|
'done': False,
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 403)
|
self.assertTrue(response.status_code == 403)
|
||||||
self.assertFalse(self.journal.operations.filter(target_label = "Le fantome du jour").exists())
|
self.assertFalse(self.journal.operations.filter(target_label="Le fantome du jour").exists())
|
||||||
|
|
||||||
def test__operation_simple_accounting(self):
|
def test__operation_simple_accounting(self):
|
||||||
self.client.login(username='comptable', password='plop')
|
self.client.login(username='comptable', password='plop')
|
||||||
sat = SimplifiedAccountingType.objects.all().first()
|
sat = SimplifiedAccountingType.objects.all().first()
|
||||||
response = self.client.post(reverse('accounting:op_new',
|
response = self.client.post(reverse('accounting:op_new',
|
||||||
args=[self.journal.id]),
|
args=[self.journal.id]),
|
||||||
{'amount': 23,
|
{'amount': 23,
|
||||||
'remark' : "Un gros test",
|
'remark': "Un gros test",
|
||||||
'journal' : self.journal.id,
|
'journal': self.journal.id,
|
||||||
'target_type' : 'OTHER',
|
'target_type': 'OTHER',
|
||||||
'target_id' : '',
|
'target_id': '',
|
||||||
'target_label' : "Le fantome de l'aurore",
|
'target_label': "Le fantome de l'aurore",
|
||||||
'date' : '04/12/2020',
|
'date': '04/12/2020',
|
||||||
'mode' : 'CASH',
|
'mode': 'CASH',
|
||||||
'cheque_number' : '',
|
'cheque_number': '',
|
||||||
'invoice' : '',
|
'invoice': '',
|
||||||
'simpleaccounting_type' : sat.id,
|
'simpleaccounting_type': sat.id,
|
||||||
'accounting_type': '',
|
'accounting_type': '',
|
||||||
'label' : '',
|
'label': '',
|
||||||
'done' : False,
|
'done': False,
|
||||||
})
|
})
|
||||||
self.assertFalse(response.status_code == 403)
|
self.assertFalse(response.status_code == 403)
|
||||||
self.assertTrue(self.journal.operations.filter(amount=23).exists())
|
self.assertTrue(self.journal.operations.filter(amount=23).exists())
|
||||||
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
|
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
|
||||||
@ -216,8 +215,7 @@ class OperationTest(TestCase):
|
|||||||
response_get = self.client.get(reverse("accounting:journal_person_statement", args=[self.journal.id]))
|
response_get = self.client.get(reverse("accounting:journal_person_statement", args=[self.journal.id]))
|
||||||
self.assertTrue("S' Kia</a></td>\\n \\n <td>3.00</td>" in str(response_get.content))
|
self.assertTrue("S' Kia</a></td>\\n \\n <td>3.00</td>" in str(response_get.content))
|
||||||
|
|
||||||
|
|
||||||
def test_accounting_statement(self):
|
def test_accounting_statement(self):
|
||||||
self.client.login(username='comptable', password='plop')
|
self.client.login(username='comptable', password='plop')
|
||||||
response_get = self.client.get(reverse("accounting:journal_accounting_statement", args=[self.journal.id]))
|
response_get = self.client.get(reverse("accounting:journal_accounting_statement", args=[self.journal.id]))
|
||||||
self.assertTrue("<td>443 - Cr\\xc3\\xa9dit - Ce code n'existe pas</td>\\n <td>3.00</td>" in str(response_get.content))
|
self.assertTrue("<td>443 - Cr\\xc3\\xa9dit - Ce code n'existe pas</td>\\n <td>3.00</td>" in str(response_get.content))
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from accounting.views import *
|
from accounting.views import *
|
||||||
|
|
||||||
@ -71,5 +71,3 @@ urlpatterns = [
|
|||||||
# User account
|
# User account
|
||||||
url(r'^refound/account$', RefoundAccountView.as_view(), name='refound_account'),
|
url(r'^refound/account$', RefoundAccountView.as_view(), name='refound_account'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,25 +22,21 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.views.generic import ListView, DetailView, RedirectView
|
from django.views.generic import ListView, DetailView
|
||||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
|
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
|
||||||
from django.shortcuts import render
|
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.forms import HiddenInput, TextInput
|
from django.forms import HiddenInput
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
from ajax_select.fields import AutoCompleteSelectField
|
||||||
|
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||||
from core.views.forms import SelectFile, SelectDate
|
from core.views.forms import SelectFile, SelectDate
|
||||||
@ -49,6 +45,7 @@ from counter.models import Counter, Selling, Product
|
|||||||
|
|
||||||
# Main accounting view
|
# Main accounting view
|
||||||
|
|
||||||
|
|
||||||
class BankAccountListView(CanViewMixin, ListView):
|
class BankAccountListView(CanViewMixin, ListView):
|
||||||
"""
|
"""
|
||||||
A list view for the admins
|
A list view for the admins
|
||||||
@ -57,6 +54,7 @@ class BankAccountListView(CanViewMixin, ListView):
|
|||||||
template_name = 'accounting/bank_account_list.jinja'
|
template_name = 'accounting/bank_account_list.jinja'
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
|
|
||||||
|
|
||||||
# Simplified accounting types
|
# Simplified accounting types
|
||||||
|
|
||||||
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
|
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
|
||||||
@ -66,6 +64,7 @@ class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
|
|||||||
model = SimplifiedAccountingType
|
model = SimplifiedAccountingType
|
||||||
template_name = 'accounting/simplifiedaccountingtype_list.jinja'
|
template_name = 'accounting/simplifiedaccountingtype_list.jinja'
|
||||||
|
|
||||||
|
|
||||||
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
|
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
An edit view for the admins
|
An edit view for the admins
|
||||||
@ -75,6 +74,7 @@ class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
|
|||||||
fields = ['label', 'accounting_type']
|
fields = ['label', 'accounting_type']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
|
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create an accounting type (for the admins)
|
Create an accounting type (for the admins)
|
||||||
@ -83,6 +83,7 @@ class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
|
|||||||
fields = ['label', 'accounting_type']
|
fields = ['label', 'accounting_type']
|
||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
|
|
||||||
|
|
||||||
# Accounting types
|
# Accounting types
|
||||||
|
|
||||||
class AccountingTypeListView(CanViewMixin, ListView):
|
class AccountingTypeListView(CanViewMixin, ListView):
|
||||||
@ -92,6 +93,7 @@ class AccountingTypeListView(CanViewMixin, ListView):
|
|||||||
model = AccountingType
|
model = AccountingType
|
||||||
template_name = 'accounting/accountingtype_list.jinja'
|
template_name = 'accounting/accountingtype_list.jinja'
|
||||||
|
|
||||||
|
|
||||||
class AccountingTypeEditView(CanViewMixin, UpdateView):
|
class AccountingTypeEditView(CanViewMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
An edit view for the admins
|
An edit view for the admins
|
||||||
@ -101,6 +103,7 @@ class AccountingTypeEditView(CanViewMixin, UpdateView):
|
|||||||
fields = ['code', 'label', 'movement_type']
|
fields = ['code', 'label', 'movement_type']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class AccountingTypeCreateView(CanCreateMixin, CreateView):
|
class AccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create an accounting type (for the admins)
|
Create an accounting type (for the admins)
|
||||||
@ -109,6 +112,7 @@ class AccountingTypeCreateView(CanCreateMixin, CreateView):
|
|||||||
fields = ['code', 'label', 'movement_type']
|
fields = ['code', 'label', 'movement_type']
|
||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
|
|
||||||
|
|
||||||
# BankAccount views
|
# BankAccount views
|
||||||
|
|
||||||
class BankAccountEditView(CanViewMixin, UpdateView):
|
class BankAccountEditView(CanViewMixin, UpdateView):
|
||||||
@ -120,6 +124,7 @@ class BankAccountEditView(CanViewMixin, UpdateView):
|
|||||||
fields = ['name', 'iban', 'number', 'club']
|
fields = ['name', 'iban', 'number', 'club']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class BankAccountDetailView(CanViewMixin, DetailView):
|
class BankAccountDetailView(CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
A detail view, listing every club account
|
A detail view, listing every club account
|
||||||
@ -128,6 +133,7 @@ class BankAccountDetailView(CanViewMixin, DetailView):
|
|||||||
pk_url_kwarg = "b_account_id"
|
pk_url_kwarg = "b_account_id"
|
||||||
template_name = 'accounting/bank_account_details.jinja'
|
template_name = 'accounting/bank_account_details.jinja'
|
||||||
|
|
||||||
|
|
||||||
class BankAccountCreateView(CanCreateMixin, CreateView):
|
class BankAccountCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create a bank account (for the admins)
|
Create a bank account (for the admins)
|
||||||
@ -136,7 +142,8 @@ class BankAccountCreateView(CanCreateMixin, CreateView):
|
|||||||
fields = ['name', 'club', 'iban', 'number']
|
fields = ['name', 'club', 'iban', 'number']
|
||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
|
|
||||||
class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
|
||||||
|
class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
||||||
"""
|
"""
|
||||||
Delete a bank account (for the admins)
|
Delete a bank account (for the admins)
|
||||||
"""
|
"""
|
||||||
@ -145,6 +152,7 @@ class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
|
|||||||
template_name = 'core/delete_confirm.jinja'
|
template_name = 'core/delete_confirm.jinja'
|
||||||
success_url = reverse_lazy('accounting:bank_list')
|
success_url = reverse_lazy('accounting:bank_list')
|
||||||
|
|
||||||
|
|
||||||
# ClubAccount views
|
# ClubAccount views
|
||||||
|
|
||||||
class ClubAccountEditView(CanViewMixin, UpdateView):
|
class ClubAccountEditView(CanViewMixin, UpdateView):
|
||||||
@ -156,6 +164,7 @@ class ClubAccountEditView(CanViewMixin, UpdateView):
|
|||||||
fields = ['name', 'club', 'bank_account']
|
fields = ['name', 'club', 'bank_account']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class ClubAccountDetailView(CanViewMixin, DetailView):
|
class ClubAccountDetailView(CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
A detail view, listing every journal
|
A detail view, listing every journal
|
||||||
@ -164,6 +173,7 @@ class ClubAccountDetailView(CanViewMixin, DetailView):
|
|||||||
pk_url_kwarg = "c_account_id"
|
pk_url_kwarg = "c_account_id"
|
||||||
template_name = 'accounting/club_account_details.jinja'
|
template_name = 'accounting/club_account_details.jinja'
|
||||||
|
|
||||||
|
|
||||||
class ClubAccountCreateView(CanCreateMixin, CreateView):
|
class ClubAccountCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create a club account (for the admins)
|
Create a club account (for the admins)
|
||||||
@ -180,7 +190,8 @@ class ClubAccountCreateView(CanCreateMixin, CreateView):
|
|||||||
ret['bank_account'] = obj.id
|
ret['bank_account'] = obj.id
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
|
||||||
|
class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
||||||
"""
|
"""
|
||||||
Delete a club account (for the admins)
|
Delete a club account (for the admins)
|
||||||
"""
|
"""
|
||||||
@ -189,6 +200,7 @@ class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
|
|||||||
template_name = 'core/delete_confirm.jinja'
|
template_name = 'core/delete_confirm.jinja'
|
||||||
success_url = reverse_lazy('accounting:bank_list')
|
success_url = reverse_lazy('accounting:bank_list')
|
||||||
|
|
||||||
|
|
||||||
# Journal views
|
# Journal views
|
||||||
|
|
||||||
class JournalTabsMixin(TabedViewMixin):
|
class JournalTabsMixin(TabedViewMixin):
|
||||||
@ -198,34 +210,35 @@ class JournalTabsMixin(TabedViewMixin):
|
|||||||
def get_list_of_tabs(self):
|
def get_list_of_tabs(self):
|
||||||
tab_list = []
|
tab_list = []
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('accounting:journal_details', kwargs={'j_id': self.object.id}),
|
'url': reverse('accounting:journal_details', kwargs={'j_id': self.object.id}),
|
||||||
'slug': 'journal',
|
'slug': 'journal',
|
||||||
'name': _("Journal"),
|
'name': _("Journal"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('accounting:journal_nature_statement', kwargs={'j_id': self.object.id}),
|
'url': reverse('accounting:journal_nature_statement', kwargs={'j_id': self.object.id}),
|
||||||
'slug': 'nature_statement',
|
'slug': 'nature_statement',
|
||||||
'name': _("Statement by nature"),
|
'name': _("Statement by nature"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('accounting:journal_person_statement', kwargs={'j_id': self.object.id}),
|
'url': reverse('accounting:journal_person_statement', kwargs={'j_id': self.object.id}),
|
||||||
'slug': 'person_statement',
|
'slug': 'person_statement',
|
||||||
'name': _("Statement by person"),
|
'name': _("Statement by person"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('accounting:journal_accounting_statement', kwargs={'j_id': self.object.id}),
|
'url': reverse('accounting:journal_accounting_statement', kwargs={'j_id': self.object.id}),
|
||||||
'slug': 'accounting_statement',
|
'slug': 'accounting_statement',
|
||||||
'name': _("Accounting statement"),
|
'name': _("Accounting statement"),
|
||||||
})
|
})
|
||||||
return tab_list
|
return tab_list
|
||||||
|
|
||||||
|
|
||||||
class JournalCreateView(CanCreateMixin, CreateView):
|
class JournalCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create a general journal
|
Create a general journal
|
||||||
"""
|
"""
|
||||||
model = GeneralJournal
|
model = GeneralJournal
|
||||||
form_class = modelform_factory(GeneralJournal, fields=['name', 'start_date', 'club_account'],
|
form_class = modelform_factory(GeneralJournal, fields=['name', 'start_date', 'club_account'],
|
||||||
widgets={ 'start_date': SelectDate, })
|
widgets={'start_date': SelectDate, })
|
||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
@ -236,6 +249,7 @@ class JournalCreateView(CanCreateMixin, CreateView):
|
|||||||
ret['club_account'] = obj.id
|
ret['club_account'] = obj.id
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
|
class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
A detail view, listing every operation
|
A detail view, listing every operation
|
||||||
@ -245,6 +259,7 @@ class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
|
|||||||
template_name = 'accounting/journal_details.jinja'
|
template_name = 'accounting/journal_details.jinja'
|
||||||
current_tab = 'journal'
|
current_tab = 'journal'
|
||||||
|
|
||||||
|
|
||||||
class JournalEditView(CanEditMixin, UpdateView):
|
class JournalEditView(CanEditMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Update a general journal
|
Update a general journal
|
||||||
@ -254,6 +269,7 @@ class JournalEditView(CanEditMixin, UpdateView):
|
|||||||
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
|
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class JournalDeleteView(CanEditPropMixin, DeleteView):
|
class JournalDeleteView(CanEditPropMixin, DeleteView):
|
||||||
"""
|
"""
|
||||||
Delete a club account (for the admins)
|
Delete a club account (for the admins)
|
||||||
@ -264,11 +280,12 @@ class JournalDeleteView(CanEditPropMixin, DeleteView):
|
|||||||
success_url = reverse_lazy('accounting:club_details')
|
success_url = reverse_lazy('accounting:club_details')
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
if self.object.operations.count() == 0:
|
if self.object.operations.count() == 0:
|
||||||
return super(JournalDeleteView, self).dispatch(request, *args, **kwargs)
|
return super(JournalDeleteView, self).dispatch(request, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
# Operation views
|
# Operation views
|
||||||
|
|
||||||
@ -276,13 +293,13 @@ class OperationForm(forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Operation
|
model = Operation
|
||||||
fields = ['amount', 'remark', 'journal', 'target_type', 'target_id', 'target_label', 'date', 'mode',
|
fields = ['amount', 'remark', 'journal', 'target_type', 'target_id', 'target_label', 'date', 'mode',
|
||||||
'cheque_number', 'invoice', 'simpleaccounting_type', 'accounting_type', 'label', 'done' ]
|
'cheque_number', 'invoice', 'simpleaccounting_type', 'accounting_type', 'label', 'done']
|
||||||
widgets = {
|
widgets = {
|
||||||
'journal': HiddenInput,
|
'journal': HiddenInput,
|
||||||
'target_id': HiddenInput,
|
'target_id': HiddenInput,
|
||||||
'date': SelectDate,
|
'date': SelectDate,
|
||||||
'invoice': SelectFile,
|
'invoice': SelectFile,
|
||||||
}
|
}
|
||||||
user = AutoCompleteSelectField('users', help_text=None, required=False)
|
user = AutoCompleteSelectField('users', help_text=None, required=False)
|
||||||
club_account = AutoCompleteSelectField('club_accounts', help_text=None, required=False)
|
club_account = AutoCompleteSelectField('club_accounts', help_text=None, required=False)
|
||||||
club = AutoCompleteSelectField('clubs', help_text=None, required=False)
|
club = AutoCompleteSelectField('clubs', help_text=None, required=False)
|
||||||
@ -328,28 +345,29 @@ class OperationForm(forms.ModelForm):
|
|||||||
inst = self.instance
|
inst = self.instance
|
||||||
club_account = inst.target
|
club_account = inst.target
|
||||||
acc_type = AccountingType.objects.exclude(movement_type="NEUTRAL").exclude(
|
acc_type = AccountingType.objects.exclude(movement_type="NEUTRAL").exclude(
|
||||||
movement_type=inst.accounting_type.movement_type).order_by('code').first() # Select a random opposite accounting type
|
movement_type=inst.accounting_type.movement_type).order_by('code').first() # Select a random opposite accounting type
|
||||||
op = Operation(
|
op = Operation(
|
||||||
journal=club_account.get_open_journal(),
|
journal=club_account.get_open_journal(),
|
||||||
amount=inst.amount,
|
amount=inst.amount,
|
||||||
date=inst.date,
|
date=inst.date,
|
||||||
remark=inst.remark,
|
remark=inst.remark,
|
||||||
mode=inst.mode,
|
mode=inst.mode,
|
||||||
cheque_number=inst.cheque_number,
|
cheque_number=inst.cheque_number,
|
||||||
invoice=inst.invoice,
|
invoice=inst.invoice,
|
||||||
done=False, # Has to be checked by hand
|
done=False, # Has to be checked by hand
|
||||||
simpleaccounting_type=None,
|
simpleaccounting_type=None,
|
||||||
accounting_type=acc_type,
|
accounting_type=acc_type,
|
||||||
target_type="ACCOUNT",
|
target_type="ACCOUNT",
|
||||||
target_id=inst.journal.club_account.id,
|
target_id=inst.journal.club_account.id,
|
||||||
target_label="",
|
target_label="",
|
||||||
linked_operation=inst,
|
linked_operation=inst,
|
||||||
)
|
)
|
||||||
op.save()
|
op.save()
|
||||||
self.instance.linked_operation = op
|
self.instance.linked_operation = op
|
||||||
self.save()
|
self.save()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class OperationCreateView(CanCreateMixin, CreateView):
|
class OperationCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create an operation
|
Create an operation
|
||||||
@ -376,6 +394,7 @@ class OperationCreateView(CanCreateMixin, CreateView):
|
|||||||
kwargs['object'] = self.journal
|
kwargs['object'] = self.journal
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class OperationEditView(CanEditMixin, UpdateView):
|
class OperationEditView(CanEditMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
An edit view, working as detail for the moment
|
An edit view, working as detail for the moment
|
||||||
@ -391,6 +410,7 @@ class OperationEditView(CanEditMixin, UpdateView):
|
|||||||
kwargs['object'] = self.object.journal
|
kwargs['object'] = self.object.journal
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class OperationPDFView(CanViewMixin, DetailView):
|
class OperationPDFView(CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display the PDF of a given operation
|
Display the PDF of a given operation
|
||||||
@ -402,11 +422,10 @@ class OperationPDFView(CanViewMixin, DetailView):
|
|||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
from reportlab.pdfgen import canvas
|
from reportlab.pdfgen import canvas
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
|
from reportlab.platypus import Table, TableStyle
|
||||||
from reportlab.lib import colors
|
from reportlab.lib import colors
|
||||||
from reportlab.lib.pagesizes import letter
|
from reportlab.lib.pagesizes import letter
|
||||||
from reportlab.lib.utils import ImageReader
|
from reportlab.lib.utils import ImageReader
|
||||||
from reportlab.graphics.shapes import Drawing
|
|
||||||
from reportlab.pdfbase.ttfonts import TTFont
|
from reportlab.pdfbase.ttfonts import TTFont
|
||||||
from reportlab.pdfbase import pdfmetrics
|
from reportlab.pdfbase import pdfmetrics
|
||||||
|
|
||||||
@ -419,7 +438,6 @@ class OperationPDFView(CanViewMixin, DetailView):
|
|||||||
num = self.object.number
|
num = self.object.number
|
||||||
date = self.object.date
|
date = self.object.date
|
||||||
mode = self.object.mode
|
mode = self.object.mode
|
||||||
cheque_number = self.object.cheque_number
|
|
||||||
club_name = self.object.journal.club_account.name
|
club_name = self.object.journal.club_account.name
|
||||||
ti = self.object.journal.name
|
ti = self.object.journal.name
|
||||||
op_label = self.object.label
|
op_label = self.object.label
|
||||||
@ -432,7 +450,7 @@ class OperationPDFView(CanViewMixin, DetailView):
|
|||||||
target = self.object.target.get_display_name()
|
target = self.object.target.get_display_name()
|
||||||
|
|
||||||
response = HttpResponse(content_type='application/pdf')
|
response = HttpResponse(content_type='application/pdf')
|
||||||
response['Content-Disposition'] = 'filename="op-%d(%s_on_%s).pdf"' %(num, ti, club_name)
|
response['Content-Disposition'] = 'filename="op-%d(%s_on_%s).pdf"' % (num, ti, club_name)
|
||||||
p = canvas.Canvas(response)
|
p = canvas.Canvas(response)
|
||||||
|
|
||||||
p.setFont('DejaVu', 12)
|
p.setFont('DejaVu', 12)
|
||||||
@ -441,21 +459,21 @@ class OperationPDFView(CanViewMixin, DetailView):
|
|||||||
width, height = letter
|
width, height = letter
|
||||||
im = ImageReader("core/static/core/img/logo.jpg")
|
im = ImageReader("core/static/core/img/logo.jpg")
|
||||||
iw, ih = im.getSize()
|
iw, ih = im.getSize()
|
||||||
p.drawImage(im, 40, height - 50, width=iw/2, height=ih/2)
|
p.drawImage(im, 40, height - 50, width=iw / 2, height=ih / 2)
|
||||||
|
|
||||||
labelStr = [["%s %s - %s %s" % (_("Journal"), ti, _("Operation"), num)]]
|
labelStr = [["%s %s - %s %s" % (_("Journal"), ti, _("Operation"), num)]]
|
||||||
|
|
||||||
label = Table(labelStr, colWidths=[150], rowHeights=[20])
|
label = Table(labelStr, colWidths=[150], rowHeights=[20])
|
||||||
|
|
||||||
label.setStyle(TableStyle([
|
label.setStyle(TableStyle([
|
||||||
('ALIGN',(0,0),(-1,-1),'RIGHT'),
|
('ALIGN', (0, 0), (-1, -1), 'RIGHT'),
|
||||||
]))
|
]))
|
||||||
w, h = label.wrapOn(label, 0, 0)
|
w, h = label.wrapOn(label, 0, 0)
|
||||||
label.drawOn(p, width-180, height)
|
label.drawOn(p, width - 180, height)
|
||||||
|
|
||||||
p.drawString(90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)) #Justificatif du libellé
|
p.drawString(90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)) # Justificatif du libellé
|
||||||
p.drawString(90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name}))
|
p.drawString(90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name}))
|
||||||
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label != None else ""})
|
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label is not None else ""})
|
||||||
p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date})
|
p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date})
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
@ -470,7 +488,7 @@ class OperationPDFView(CanViewMixin, DetailView):
|
|||||||
payment_mode += "[\u00D7]"
|
payment_mode += "[\u00D7]"
|
||||||
else:
|
else:
|
||||||
payment_mode += "[ ]"
|
payment_mode += "[ ]"
|
||||||
payment_mode += " %s\n" %(m[1])
|
payment_mode += " %s\n" % (m[1])
|
||||||
|
|
||||||
data += [[payment_mode]]
|
data += [[payment_mode]]
|
||||||
|
|
||||||
@ -478,29 +496,29 @@ class OperationPDFView(CanViewMixin, DetailView):
|
|||||||
|
|
||||||
data += [["%s \n%s" % (_("Comment:"), remark)]]
|
data += [["%s \n%s" % (_("Comment:"), remark)]]
|
||||||
|
|
||||||
t = Table(data, colWidths=[(width-90*2)/2]*2, rowHeights=[20, 20, 70, 20, 80])
|
t = Table(data, colWidths=[(width - 90 * 2) / 2] * 2, rowHeights=[20, 20, 70, 20, 80])
|
||||||
t.setStyle(TableStyle([
|
t.setStyle(TableStyle([
|
||||||
('ALIGN',(0,0),(-1,-1),'CENTER'),
|
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
|
||||||
('VALIGN',(-2,-1),(-1,-1),'TOP'),
|
('VALIGN', (-2, -1), (-1, -1), 'TOP'),
|
||||||
('VALIGN',(0,0),(-1,-2),'MIDDLE'),
|
('VALIGN', (0, 0), (-1, -2), 'MIDDLE'),
|
||||||
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
|
('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
|
||||||
('SPAN', (0, 0), (1, 0)), # line DEBIT/CREDIT
|
('SPAN', (0, 0), (1, 0)), # line DEBIT/CREDIT
|
||||||
('SPAN', (0, 1), (1, 1)), # line amount
|
('SPAN', (0, 1), (1, 1)), # line amount
|
||||||
('SPAN',(-2, -1), (-1,-1)), # line comment
|
('SPAN', (-2, -1), (-1, -1)), # line comment
|
||||||
('SPAN', (0, -2), (-1, -2)), # line creditor/debtor
|
('SPAN', (0, -2), (-1, -2)), # line creditor/debtor
|
||||||
('SPAN', (0, 2), (1, 2)), # line payment_mode
|
('SPAN', (0, 2), (1, 2)), # line payment_mode
|
||||||
('ALIGN',(0, 2), (1, 2),'LEFT'), # line payment_mode
|
('ALIGN', (0, 2), (1, 2), 'LEFT'), # line payment_mode
|
||||||
('ALIGN', (-2, -1), (-1, -1), 'LEFT'),
|
('ALIGN', (-2, -1), (-1, -1), 'LEFT'),
|
||||||
('BOX', (0,0), (-1,-1), 0.25, colors.black),
|
('BOX', (0, 0), (-1, -1), 0.25, colors.black),
|
||||||
]))
|
]))
|
||||||
|
|
||||||
signature = []
|
signature = []
|
||||||
signature += [[_("Signature:")]]
|
signature += [[_("Signature:")]]
|
||||||
|
|
||||||
tSig = Table(signature, colWidths=[(width-90*2)], rowHeights=[80])
|
tSig = Table(signature, colWidths=[(width - 90 * 2)], rowHeights=[80])
|
||||||
tSig.setStyle(TableStyle([
|
tSig.setStyle(TableStyle([
|
||||||
('VALIGN',(0,0),(-1,-1),'TOP'),
|
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||||
('BOX', (0,0), (-1,-1), 0.25, colors.black)]))
|
('BOX', (0, 0), (-1, -1), 0.25, colors.black)]))
|
||||||
|
|
||||||
w, h = tSig.wrapOn(p, 0, 0)
|
w, h = tSig.wrapOn(p, 0, 0)
|
||||||
tSig.drawOn(p, 90, 200)
|
tSig.drawOn(p, 90, 200)
|
||||||
@ -516,14 +534,15 @@ class OperationPDFView(CanViewMixin, DetailView):
|
|||||||
p.save()
|
p.save()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display a statement sorted by labels
|
Display a statement sorted by labels
|
||||||
"""
|
"""
|
||||||
model = GeneralJournal
|
model = GeneralJournal
|
||||||
pk_url_kwarg = "j_id"
|
pk_url_kwarg = "j_id"
|
||||||
template_name='accounting/journal_statement_nature.jinja'
|
template_name = 'accounting/journal_statement_nature.jinja'
|
||||||
current_tab='nature_statement'
|
current_tab = 'nature_statement'
|
||||||
|
|
||||||
def statement(self, queryset, movement_type):
|
def statement(self, queryset, movement_type):
|
||||||
ret = collections.OrderedDict()
|
ret = collections.OrderedDict()
|
||||||
@ -531,14 +550,16 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
|||||||
total_sum = 0
|
total_sum = 0
|
||||||
for sat in [None] + list(SimplifiedAccountingType.objects.order_by('label').all()):
|
for sat in [None] + list(SimplifiedAccountingType.objects.order_by('label').all()):
|
||||||
sum = queryset.filter(accounting_type__movement_type=movement_type,
|
sum = queryset.filter(accounting_type__movement_type=movement_type,
|
||||||
simpleaccounting_type=sat).aggregate(amount_sum=Sum('amount'))['amount_sum']
|
simpleaccounting_type=sat).aggregate(amount_sum=Sum('amount'))['amount_sum']
|
||||||
if sat: sat = sat.label
|
if sat:
|
||||||
else: sat = ""
|
sat = sat.label
|
||||||
|
else:
|
||||||
|
sat = ""
|
||||||
if sum:
|
if sum:
|
||||||
total_sum += sum
|
total_sum += sum
|
||||||
statement[sat] = sum
|
statement[sat] = sum
|
||||||
ret[movement_type] = statement
|
ret[movement_type] = statement
|
||||||
ret[movement_type+"_sum"] = total_sum
|
ret[movement_type + "_sum"] = total_sum
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def big_statement(self):
|
def big_statement(self):
|
||||||
@ -566,23 +587,24 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['statement'] = self.big_statement()
|
kwargs['statement'] = self.big_statement()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Calculate a dictionary with operation target and sum of operations
|
Calculate a dictionary with operation target and sum of operations
|
||||||
"""
|
"""
|
||||||
model = GeneralJournal
|
model = GeneralJournal
|
||||||
pk_url_kwarg = "j_id"
|
pk_url_kwarg = "j_id"
|
||||||
template_name='accounting/journal_statement_person.jinja'
|
template_name = 'accounting/journal_statement_person.jinja'
|
||||||
current_tab='person_statement'
|
current_tab = 'person_statement'
|
||||||
|
|
||||||
def sum_by_target(self, target_id, target_type, movement_type):
|
def sum_by_target(self, target_id, target_type, movement_type):
|
||||||
return self.object.operations.filter(accounting_type__movement_type=movement_type,
|
return self.object.operations.filter(accounting_type__movement_type=movement_type,
|
||||||
target_id=target_id, target_type=target_type).aggregate(amount_sum=Sum('amount'))['amount_sum']
|
target_id=target_id, target_type=target_type).aggregate(amount_sum=Sum('amount'))['amount_sum']
|
||||||
|
|
||||||
def statement(self, movement_type):
|
def statement(self, movement_type):
|
||||||
statement = collections.OrderedDict()
|
statement = collections.OrderedDict()
|
||||||
for op in self.object.operations.filter(accounting_type__movement_type=movement_type).order_by('target_type',
|
for op in self.object.operations.filter(accounting_type__movement_type=movement_type).order_by('target_type',
|
||||||
'target_id').distinct():
|
'target_id').distinct():
|
||||||
statement[op.target] = self.sum_by_target(op.target_id, op.target_type, movement_type)
|
statement[op.target] = self.sum_by_target(op.target_id, op.target_type, movement_type)
|
||||||
return statement
|
return statement
|
||||||
|
|
||||||
@ -598,13 +620,14 @@ class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['total_debit'] = self.total("DEBIT")
|
kwargs['total_debit'] = self.total("DEBIT")
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Calculate a dictionary with operation type and sum of operations
|
Calculate a dictionary with operation type and sum of operations
|
||||||
"""
|
"""
|
||||||
model = GeneralJournal
|
model = GeneralJournal
|
||||||
pk_url_kwarg = "j_id"
|
pk_url_kwarg = "j_id"
|
||||||
template_name='accounting/journal_statement_accounting.jinja'
|
template_name = 'accounting/journal_statement_accounting.jinja'
|
||||||
current_tab = "accounting_statement"
|
current_tab = "accounting_statement"
|
||||||
|
|
||||||
def statement(self):
|
def statement(self):
|
||||||
@ -624,10 +647,12 @@ class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView)
|
|||||||
|
|
||||||
# Company views
|
# Company views
|
||||||
|
|
||||||
|
|
||||||
class CompanyListView(CanViewMixin, ListView):
|
class CompanyListView(CanViewMixin, ListView):
|
||||||
model = Company
|
model = Company
|
||||||
template_name = 'accounting/co_list.jinja'
|
template_name = 'accounting/co_list.jinja'
|
||||||
|
|
||||||
|
|
||||||
class CompanyCreateView(CanCreateMixin, CreateView):
|
class CompanyCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create a company
|
Create a company
|
||||||
@ -648,6 +673,7 @@ class CompanyEditView(CanCreateMixin, UpdateView):
|
|||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
success_url = reverse_lazy('accounting:co_list')
|
success_url = reverse_lazy('accounting:co_list')
|
||||||
|
|
||||||
|
|
||||||
# Label views
|
# Label views
|
||||||
|
|
||||||
class LabelListView(CanViewMixin, DetailView):
|
class LabelListView(CanViewMixin, DetailView):
|
||||||
@ -655,11 +681,12 @@ class LabelListView(CanViewMixin, DetailView):
|
|||||||
pk_url_kwarg = "clubaccount_id"
|
pk_url_kwarg = "clubaccount_id"
|
||||||
template_name = 'accounting/label_list.jinja'
|
template_name = 'accounting/label_list.jinja'
|
||||||
|
|
||||||
class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the rights before creating the object
|
|
||||||
|
class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the rights before creating the object
|
||||||
model = Label
|
model = Label
|
||||||
form_class = modelform_factory(Label, fields=['name', 'club_account'], widgets={
|
form_class = modelform_factory(Label, fields=['name', 'club_account'], widgets={
|
||||||
'club_account': HiddenInput,
|
'club_account': HiddenInput,
|
||||||
})
|
})
|
||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
@ -670,12 +697,14 @@ class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the
|
|||||||
ret['club_account'] = obj.id
|
ret['club_account'] = obj.id
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class LabelEditView(CanEditMixin, UpdateView):
|
class LabelEditView(CanEditMixin, UpdateView):
|
||||||
model = Label
|
model = Label
|
||||||
pk_url_kwarg = "label_id"
|
pk_url_kwarg = "label_id"
|
||||||
fields = ['name']
|
fields = ['name']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class LabelDeleteView(CanEditMixin, DeleteView):
|
class LabelDeleteView(CanEditMixin, DeleteView):
|
||||||
model = Label
|
model = Label
|
||||||
pk_url_kwarg = "label_id"
|
pk_url_kwarg = "label_id"
|
||||||
@ -684,9 +713,11 @@ class LabelDeleteView(CanEditMixin, DeleteView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return self.object.get_absolute_url()
|
return self.object.get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
class CloseCustomerAccountForm(forms.Form):
|
class CloseCustomerAccountForm(forms.Form):
|
||||||
user = AutoCompleteSelectField('users', label=_('Refound this account'), help_text=None, required=True)
|
user = AutoCompleteSelectField('users', label=_('Refound this account'), help_text=None, required=True)
|
||||||
|
|
||||||
|
|
||||||
class RefoundAccountView(FormView):
|
class RefoundAccountView(FormView):
|
||||||
"""
|
"""
|
||||||
Create a selling with the same amount than the current user money
|
Create a selling with the same amount than the current user money
|
||||||
|
@ -29,4 +29,3 @@ from club.models import Club, Membership
|
|||||||
|
|
||||||
admin.site.register(Club)
|
admin.site.register(Club)
|
||||||
admin.site.register(Membership)
|
admin.site.register(Membership)
|
||||||
|
|
||||||
|
@ -27,12 +27,13 @@ from django.core import validators
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import transaction
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from core.models import User, MetaGroup, Group, SithFile
|
from core.models import User, MetaGroup, Group, SithFile
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
class Club(models.Model):
|
class Club(models.Model):
|
||||||
@ -43,17 +44,17 @@ class Club(models.Model):
|
|||||||
name = models.CharField(_('name'), max_length=64)
|
name = models.CharField(_('name'), max_length=64)
|
||||||
parent = models.ForeignKey('Club', related_name='children', null=True, blank=True)
|
parent = models.ForeignKey('Club', related_name='children', null=True, blank=True)
|
||||||
unix_name = models.CharField(_('unix name'), max_length=30, unique=True,
|
unix_name = models.CharField(_('unix name'), max_length=30, unique=True,
|
||||||
validators=[
|
validators=[
|
||||||
validators.RegexValidator(
|
validators.RegexValidator(
|
||||||
r'^[a-z0-9][a-z0-9._-]*[a-z0-9]$',
|
r'^[a-z0-9][a-z0-9._-]*[a-z0-9]$',
|
||||||
_('Enter a valid unix name. This value may contain only '
|
_('Enter a valid unix name. This value may contain only '
|
||||||
'letters, numbers ./-/_ characters.')
|
'letters, numbers ./-/_ characters.')
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
error_messages={
|
error_messages={
|
||||||
'unique': _("A club with that unix name already exists."),
|
'unique': _("A club with that unix name already exists."),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
address = models.CharField(_('address'), max_length=254)
|
address = models.CharField(_('address'), max_length=254)
|
||||||
# email = models.EmailField(_('email address'), unique=True) # This should, and will be generated automatically
|
# email = models.EmailField(_('email address'), unique=True) # This should, and will be generated automatically
|
||||||
owner_group = models.ForeignKey(Group, related_name="owned_club",
|
owner_group = models.ForeignKey(Group, related_name="owned_club",
|
||||||
@ -61,7 +62,7 @@ class Club(models.Model):
|
|||||||
edit_groups = models.ManyToManyField(Group, related_name="editable_club", blank=True)
|
edit_groups = models.ManyToManyField(Group, related_name="editable_club", blank=True)
|
||||||
view_groups = models.ManyToManyField(Group, related_name="viewable_club", blank=True)
|
view_groups = models.ManyToManyField(Group, related_name="viewable_club", blank=True)
|
||||||
home = models.OneToOneField(SithFile, related_name='home_of_club', verbose_name=_("home"), null=True, blank=True,
|
home = models.OneToOneField(SithFile, related_name='home_of_club', verbose_name=_("home"), null=True, blank=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name', 'unix_name']
|
ordering = ['name', 'unix_name']
|
||||||
@ -109,9 +110,9 @@ class Club(models.Model):
|
|||||||
self._change_unixname(self.unix_name)
|
self._change_unixname(self.unix_name)
|
||||||
super(Club, self).save(*args, **kwargs)
|
super(Club, self).save(*args, **kwargs)
|
||||||
if creation:
|
if creation:
|
||||||
board = MetaGroup(name=self.unix_name+settings.SITH_BOARD_SUFFIX)
|
board = MetaGroup(name=self.unix_name + settings.SITH_BOARD_SUFFIX)
|
||||||
board.save()
|
board.save()
|
||||||
member = MetaGroup(name=self.unix_name+settings.SITH_MEMBER_SUFFIX)
|
member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX)
|
||||||
member.save()
|
member.save()
|
||||||
subscribers = Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first()
|
subscribers = Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first()
|
||||||
self.make_home()
|
self.make_home()
|
||||||
@ -153,6 +154,7 @@ class Club(models.Model):
|
|||||||
return sub.is_subscribed
|
return sub.is_subscribed
|
||||||
|
|
||||||
_memberships = {}
|
_memberships = {}
|
||||||
|
|
||||||
def get_membership_for(self, user):
|
def get_membership_for(self, user):
|
||||||
"""
|
"""
|
||||||
Returns the current membership the given user
|
Returns the current membership the given user
|
||||||
@ -168,6 +170,7 @@ class Club(models.Model):
|
|||||||
Club._memberships[self.id][user.id] = m
|
Club._memberships[self.id][user.id] = m
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class Membership(models.Model):
|
class Membership(models.Model):
|
||||||
"""
|
"""
|
||||||
The Membership class makes the connection between User and Clubs
|
The Membership class makes the connection between User and Clubs
|
||||||
@ -184,7 +187,7 @@ class Membership(models.Model):
|
|||||||
start_date = models.DateField(_('start date'), default=timezone.now)
|
start_date = models.DateField(_('start date'), default=timezone.now)
|
||||||
end_date = models.DateField(_('end date'), null=True, blank=True)
|
end_date = models.DateField(_('end date'), null=True, blank=True)
|
||||||
role = models.IntegerField(_('role'), choices=sorted(settings.SITH_CLUB_ROLES.items()),
|
role = models.IntegerField(_('role'), choices=sorted(settings.SITH_CLUB_ROLES.items()),
|
||||||
default=sorted(settings.SITH_CLUB_ROLES.items())[0][0])
|
default=sorted(settings.SITH_CLUB_ROLES.items())[0][0])
|
||||||
description = models.CharField(_('description'), max_length=128, null=False, blank=True)
|
description = models.CharField(_('description'), max_length=128, null=False, blank=True)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
@ -195,9 +198,9 @@ class Membership(models.Model):
|
|||||||
raise ValidationError(_('User is already member of that club'))
|
raise ValidationError(_('User is already member of that club'))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.club.name+' - '+self.user.username+' - '+str(settings.SITH_CLUB_ROLES[self.role])+str(
|
return self.club.name + ' - ' + self.user.username + ' - ' + str(settings.SITH_CLUB_ROLES[self.role]) + str(
|
||||||
" - "+str(_('past member')) if self.end_date is not None else ""
|
" - " + str(_('past member')) if self.end_date is not None else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_owned_by(self, user):
|
def is_owned_by(self, user):
|
||||||
"""
|
"""
|
||||||
@ -216,4 +219,3 @@ class Membership(models.Model):
|
|||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('club:club_members', kwargs={'club_id': self.club.id})
|
return reverse('club:club_members', kwargs={'club_id': self.club.id})
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ from club.models import Club
|
|||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
||||||
|
|
||||||
class ClubTest(TestCase):
|
class ClubTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
call_command("populate")
|
call_command("populate")
|
||||||
@ -41,34 +42,34 @@ class ClubTest(TestCase):
|
|||||||
|
|
||||||
def test_create_add_user_to_club_from_root_ok(self):
|
def test_create_add_user_to_club_from_root_ok(self):
|
||||||
self.client.login(username='root', password='plop')
|
self.client.login(username='root', password='plop')
|
||||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.skia.id,
|
"user": self.skia.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 3})
|
"role": 3})
|
||||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue("S' Kia</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
self.assertTrue("S' Kia</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
||||||
|
|
||||||
def test_create_add_user_to_club_from_root_fail_not_subscriber(self):
|
def test_create_add_user_to_club_from_root_fail_not_subscriber(self):
|
||||||
self.client.login(username='root', password='plop')
|
self.client.login(username='root', password='plop')
|
||||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.guy.id,
|
"user": self.guy.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 3})
|
"role": 3})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('<ul class="errorlist nonfield"><li>' in str(response.content))
|
self.assertTrue('<ul class="errorlist nonfield"><li>' in str(response.content))
|
||||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||||
self.assertFalse("Guy Carlier</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
self.assertFalse("Guy Carlier</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
||||||
|
|
||||||
def test_create_add_user_to_club_from_root_fail_already_in_club(self):
|
def test_create_add_user_to_club_from_root_fail_already_in_club(self):
|
||||||
self.client.login(username='root', password='plop')
|
self.client.login(username='root', password='plop')
|
||||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.skia.id,
|
"user": self.skia.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 3})
|
"role": 3})
|
||||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||||
self.assertTrue("S' Kia</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
self.assertTrue("S' Kia</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
||||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.skia.id,
|
"user": self.skia.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 4})
|
"role": 4})
|
||||||
@ -77,30 +78,29 @@ class ClubTest(TestCase):
|
|||||||
|
|
||||||
def test_create_add_user_to_club_from_skia_ok(self):
|
def test_create_add_user_to_club_from_skia_ok(self):
|
||||||
self.client.login(username='root', password='plop')
|
self.client.login(username='root', password='plop')
|
||||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.skia.id,
|
"user": self.skia.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 10})
|
"role": 10})
|
||||||
self.client.login(username='skia', password='plop')
|
self.client.login(username='skia', password='plop')
|
||||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.rbatsbak.id,
|
"user": self.rbatsbak.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 9})
|
"role": 9})
|
||||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue("""Richard Batsbak</a></td>\\n <td>Vice-Pr\\xc3\\xa9sident</td>""" in str(response.content))
|
self.assertTrue("""Richard Batsbak</a></td>\\n <td>Vice-Pr\\xc3\\xa9sident</td>""" in str(response.content))
|
||||||
|
|
||||||
def test_create_add_user_to_club_from_richard_fail(self):
|
def test_create_add_user_to_club_from_richard_fail(self):
|
||||||
self.client.login(username='root', password='plop')
|
self.client.login(username='root', password='plop')
|
||||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.rbatsbak.id,
|
"user": self.rbatsbak.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 3})
|
"role": 3})
|
||||||
self.client.login(username='rbatsbak', password='plop')
|
self.client.login(username='rbatsbak', password='plop')
|
||||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||||
"user": self.skia.id,
|
"user": self.skia.id,
|
||||||
"start_date": "12/06/2016",
|
"start_date": "12/06/2016",
|
||||||
"role": 10})
|
"role": 10})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue("<li>Vous n'avez pas la permission de faire cela</li>" in str(response.content))
|
self.assertTrue("<li>Vous n'avez pas la permission de faire cela</li>" in str(response.content))
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from club.views import *
|
from club.views import *
|
||||||
|
|
||||||
@ -40,4 +40,3 @@ urlpatterns = [
|
|||||||
url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
|
url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
|
||||||
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
|
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
106
club/views.py
106
club/views.py
@ -23,27 +23,21 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.shortcuts import render
|
|
||||||
from django.views.generic import ListView, DetailView, TemplateView
|
from django.views.generic import ListView, DetailView, TemplateView
|
||||||
from django.views.generic.edit import UpdateView, CreateView
|
from django.views.generic.edit import UpdateView, CreateView
|
||||||
from django.forms import CheckboxSelectMultiple
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext as _t
|
from django.utils.translation import ugettext as _t
|
||||||
from django.conf import settings
|
|
||||||
from ajax_select.fields import AutoCompleteSelectField
|
from ajax_select.fields import AutoCompleteSelectField
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
|
||||||
from core.views.forms import SelectDate, SelectSingle, SelectDateTime
|
from core.views.forms import SelectDate, SelectDateTime
|
||||||
from club.models import Club, Membership
|
from club.models import Club, Membership
|
||||||
from core.models import User
|
from sith.settings import SITH_MAXIMUM_FREE_ROLE
|
||||||
from sith.settings import SITH_MAXIMUM_FREE_ROLE, SITH_MAIN_BOARD_GROUP
|
from counter.models import Selling, Counter
|
||||||
from counter.models import Product, Selling, Counter
|
|
||||||
|
|
||||||
class ClubTabsMixin(TabedViewMixin):
|
class ClubTabsMixin(TabedViewMixin):
|
||||||
def get_tabs_title(self):
|
def get_tabs_title(self):
|
||||||
@ -52,45 +46,46 @@ class ClubTabsMixin(TabedViewMixin):
|
|||||||
def get_list_of_tabs(self):
|
def get_list_of_tabs(self):
|
||||||
tab_list = []
|
tab_list = []
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('club:club_view', kwargs={'club_id': self.object.id}),
|
'url': reverse('club:club_view', kwargs={'club_id': self.object.id}),
|
||||||
'slug': 'infos',
|
'slug': 'infos',
|
||||||
'name': _("Infos"),
|
'name': _("Infos"),
|
||||||
})
|
})
|
||||||
if self.request.user.can_view(self.object):
|
if self.request.user.can_view(self.object):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('club:club_members', kwargs={'club_id': self.object.id}),
|
'url': reverse('club:club_members', kwargs={'club_id': self.object.id}),
|
||||||
'slug': 'members',
|
'slug': 'members',
|
||||||
'name': _("Members"),
|
'name': _("Members"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('club:club_old_members', kwargs={'club_id': self.object.id}),
|
'url': reverse('club:club_old_members', kwargs={'club_id': self.object.id}),
|
||||||
'slug': 'elderlies',
|
'slug': 'elderlies',
|
||||||
'name': _("Old members"),
|
'name': _("Old members"),
|
||||||
})
|
})
|
||||||
if self.request.user.can_edit(self.object):
|
if self.request.user.can_edit(self.object):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('club:tools', kwargs={'club_id': self.object.id}),
|
'url': reverse('club:tools', kwargs={'club_id': self.object.id}),
|
||||||
'slug': 'tools',
|
'slug': 'tools',
|
||||||
'name': _("Tools"),
|
'name': _("Tools"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('club:club_edit', kwargs={'club_id': self.object.id}),
|
'url': reverse('club:club_edit', kwargs={'club_id': self.object.id}),
|
||||||
'slug': 'edit',
|
'slug': 'edit',
|
||||||
'name': _("Edit"),
|
'name': _("Edit"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('club:club_sellings', kwargs={'club_id': self.object.id}),
|
'url': reverse('club:club_sellings', kwargs={'club_id': self.object.id}),
|
||||||
'slug': 'sellings',
|
'slug': 'sellings',
|
||||||
'name': _("Sellings"),
|
'name': _("Sellings"),
|
||||||
})
|
})
|
||||||
if self.request.user.is_owner(self.object):
|
if self.request.user.is_owner(self.object):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('club:club_prop', kwargs={'club_id': self.object.id}),
|
'url': reverse('club:club_prop', kwargs={'club_id': self.object.id}),
|
||||||
'slug': 'props',
|
'slug': 'props',
|
||||||
'name': _("Props"),
|
'name': _("Props"),
|
||||||
})
|
})
|
||||||
return tab_list
|
return tab_list
|
||||||
|
|
||||||
|
|
||||||
class ClubListView(ListView):
|
class ClubListView(ListView):
|
||||||
"""
|
"""
|
||||||
List the Clubs
|
List the Clubs
|
||||||
@ -98,6 +93,7 @@ class ClubListView(ListView):
|
|||||||
model = Club
|
model = Club
|
||||||
template_name = 'club/club_list.jinja'
|
template_name = 'club/club_list.jinja'
|
||||||
|
|
||||||
|
|
||||||
class ClubView(ClubTabsMixin, DetailView):
|
class ClubView(ClubTabsMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Front page of a Club
|
Front page of a Club
|
||||||
@ -107,6 +103,7 @@ class ClubView(ClubTabsMixin, DetailView):
|
|||||||
template_name = 'club/club_detail.jinja'
|
template_name = 'club/club_detail.jinja'
|
||||||
current_tab = "infos"
|
current_tab = "infos"
|
||||||
|
|
||||||
|
|
||||||
class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
|
class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Tools page of a Club
|
Tools page of a Club
|
||||||
@ -116,27 +113,30 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
|
|||||||
template_name = 'club/club_tools.jinja'
|
template_name = 'club/club_tools.jinja'
|
||||||
current_tab = "tools"
|
current_tab = "tools"
|
||||||
|
|
||||||
|
|
||||||
class ClubMemberForm(forms.ModelForm):
|
class ClubMemberForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
Form handling the members of a club
|
Form handling the members of a club
|
||||||
"""
|
"""
|
||||||
error_css_class = 'error'
|
error_css_class = 'error'
|
||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Membership
|
model = Membership
|
||||||
fields = ['user', 'role', 'start_date', 'description']
|
fields = ['user', 'role', 'start_date', 'description']
|
||||||
widgets = {
|
widgets = {
|
||||||
'start_date': SelectDate
|
'start_date': SelectDate
|
||||||
}
|
}
|
||||||
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
|
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Overloaded to return the club, and not to a Membership object that has no view
|
Overloaded to return the club, and not to a Membership object that has no view
|
||||||
"""
|
"""
|
||||||
ret = super(ClubMemberForm, self).save(*args, **kwargs)
|
super(ClubMemberForm, self).save(*args, **kwargs)
|
||||||
return self.instance.club
|
return self.instance.club
|
||||||
|
|
||||||
|
|
||||||
class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
View of a club's members
|
View of a club's members
|
||||||
@ -153,9 +153,9 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
|||||||
That's why the save method of ClubMemberForm is overridden.
|
That's why the save method of ClubMemberForm is overridden.
|
||||||
"""
|
"""
|
||||||
form = super(ClubMembersView, self).get_form()
|
form = super(ClubMembersView, self).get_form()
|
||||||
if 'user' in form.data and form.data.get('user') != '': # Load an existing membership if possible
|
if 'user' in form.data and form.data.get('user') != '': # Load an existing membership if possible
|
||||||
form.instance = Membership.objects.filter(club=self.object).filter(user=form.data.get('user')).filter(end_date=None).first()
|
form.instance = Membership.objects.filter(club=self.object).filter(user=form.data.get('user')).filter(end_date=None).first()
|
||||||
if form.instance is None: # Instanciate a new membership
|
if form.instance is None: # Instanciate a new membership
|
||||||
form.instance = Membership(club=self.object, user=self.request.user)
|
form.instance = Membership(club=self.object, user=self.request.user)
|
||||||
if not self.request.user.is_root:
|
if not self.request.user.is_root:
|
||||||
form.fields.pop('start_date', None)
|
form.fields.pop('start_date', None)
|
||||||
@ -174,7 +174,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
|||||||
if (form.cleaned_data['role'] <= SITH_MAXIMUM_FREE_ROLE or
|
if (form.cleaned_data['role'] <= SITH_MAXIMUM_FREE_ROLE or
|
||||||
(ms is not None and ms.role >= form.cleaned_data['role']) or
|
(ms is not None and ms.role >= form.cleaned_data['role']) or
|
||||||
request.user.is_board_member or
|
request.user.is_board_member or
|
||||||
request.user.is_root):
|
request.user.is_root):
|
||||||
return self.form_valid(form)
|
return self.form_valid(form)
|
||||||
else:
|
else:
|
||||||
form.add_error(None, _("You do not have the permission to do that"))
|
form.add_error(None, _("You do not have the permission to do that"))
|
||||||
@ -182,6 +182,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
|||||||
else:
|
else:
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Old members of a club
|
Old members of a club
|
||||||
@ -191,11 +192,13 @@ class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
|||||||
template_name = 'club/club_old_members.jinja'
|
template_name = 'club/club_old_members.jinja'
|
||||||
current_tab = "elderlies"
|
current_tab = "elderlies"
|
||||||
|
|
||||||
|
|
||||||
class SellingsFormBase(forms.Form):
|
class SellingsFormBase(forms.Form):
|
||||||
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
||||||
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
||||||
counter = forms.ModelChoiceField(Counter.objects.order_by('name').all(), label=_("Counter"), required=False)
|
counter = forms.ModelChoiceField(Counter.objects.order_by('name').all(), label=_("Counter"), required=False)
|
||||||
|
|
||||||
|
|
||||||
class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Sellings of a club
|
Sellings of a club
|
||||||
@ -207,8 +210,8 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
|||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'product': forms.ModelChoiceField(self.object.products.order_by('name').all(), label=_("Product"), required=False)
|
'product': forms.ModelChoiceField(self.object.products.order_by('name').all(), label=_("Product"), required=False)
|
||||||
}
|
}
|
||||||
return type('SellingsForm', (SellingsFormBase,), kwargs)
|
return type('SellingsForm', (SellingsFormBase,), kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
@ -235,6 +238,7 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
|||||||
kwargs['form'] = form
|
kwargs['form'] = form
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class ClubSellingCSVView(ClubSellingView):
|
class ClubSellingCSVView(ClubSellingView):
|
||||||
"""
|
"""
|
||||||
Generate sellings in csv for a given period
|
Generate sellings in csv for a given period
|
||||||
@ -252,27 +256,31 @@ class ClubSellingCSVView(ClubSellingView):
|
|||||||
writer.writerow([_t('Quantity'), kwargs['total_quantity']])
|
writer.writerow([_t('Quantity'), kwargs['total_quantity']])
|
||||||
writer.writerow([_t('Total'), kwargs['total']])
|
writer.writerow([_t('Total'), kwargs['total']])
|
||||||
writer.writerow([_t('Benefit'), kwargs['benefit']])
|
writer.writerow([_t('Benefit'), kwargs['benefit']])
|
||||||
writer.writerow([_t('Date'),_t('Counter'),_t('Barman'),_t('Customer'),_t('Label'),
|
writer.writerow([_t('Date'), _t('Counter'), _t('Barman'), _t('Customer'), _t('Label'),
|
||||||
_t('Quantity'), _t('Total'),_t('Payment method'), _t('Selling price'), _t('Purchase price'), _t('Benefit')])
|
_t('Quantity'), _t('Total'), _t('Payment method'), _t('Selling price'), _t('Purchase price'), _t('Benefit')])
|
||||||
for o in kwargs['result']:
|
for o in kwargs['result']:
|
||||||
row = [o.date, o.counter]
|
row = [o.date, o.counter]
|
||||||
if o.seller:
|
if o.seller:
|
||||||
row.append(o.seller.get_display_name())
|
row.append(o.seller.get_display_name())
|
||||||
else: row.append('')
|
else:
|
||||||
|
row.append('')
|
||||||
if o.customer:
|
if o.customer:
|
||||||
row.append(o.customer.user.get_display_name())
|
row.append(o.customer.user.get_display_name())
|
||||||
else: row.append('')
|
else:
|
||||||
row = row +[o.label, o.quantity, o.quantity * o.unit_price,
|
row.append('')
|
||||||
o.get_payment_method_display()]
|
row = row + [o.label, o.quantity, o.quantity * o.unit_price,
|
||||||
|
o.get_payment_method_display()]
|
||||||
if o.product:
|
if o.product:
|
||||||
row.append(o.product.selling_price)
|
row.append(o.product.selling_price)
|
||||||
row.append(o.product.purchase_price)
|
row.append(o.product.purchase_price)
|
||||||
row.append(o.product.selling_price - o.product.purchase_price)
|
row.append(o.product.selling_price - o.product.purchase_price)
|
||||||
else: row = row + ['', '', '']
|
else:
|
||||||
|
row = row + ['', '', '']
|
||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit a Club's main informations (for the club's members)
|
Edit a Club's main informations (for the club's members)
|
||||||
@ -283,6 +291,7 @@ class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
|||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
current_tab = "edit"
|
current_tab = "edit"
|
||||||
|
|
||||||
|
|
||||||
class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
|
class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit the properties of a Club object (for the Sith admins)
|
Edit the properties of a Club object (for the Sith admins)
|
||||||
@ -293,6 +302,7 @@ class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
|
|||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
current_tab = "props"
|
current_tab = "props"
|
||||||
|
|
||||||
|
|
||||||
class ClubCreateView(CanEditPropMixin, CreateView):
|
class ClubCreateView(CanEditPropMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create a club (for the Sith admin)
|
Create a club (for the Sith admin)
|
||||||
@ -302,6 +312,7 @@ class ClubCreateView(CanEditPropMixin, CreateView):
|
|||||||
fields = ['name', 'unix_name', 'parent']
|
fields = ['name', 'unix_name', 'parent']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class MembershipSetOldView(CanEditMixin, DetailView):
|
class MembershipSetOldView(CanEditMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Set a membership as beeing old
|
Set a membership as beeing old
|
||||||
@ -319,8 +330,9 @@ class MembershipSetOldView(CanEditMixin, DetailView):
|
|||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return HttpResponseRedirect(reverse('club:club_members', args=self.args, kwargs={'club_id': self.object.club.id}))
|
return HttpResponseRedirect(reverse('club:club_members', args=self.args, kwargs={'club_id': self.object.club.id}))
|
||||||
|
|
||||||
|
|
||||||
class ClubStatView(TemplateView):
|
class ClubStatView(TemplateView):
|
||||||
template_name="club/stats.jinja"
|
template_name = "club/stats.jinja"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs = super(ClubStatView, self).get_context_data(**kwargs)
|
kwargs = super(ClubStatView, self).get_context_data(**kwargs)
|
||||||
|
@ -30,5 +30,3 @@ admin.site.register(Sith)
|
|||||||
admin.site.register(News)
|
admin.site.register(News)
|
||||||
admin.site.register(Weekmail)
|
admin.site.register(Weekmail)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,15 +25,14 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
|
|
||||||
from core.models import User, Preferences
|
from core.models import User, Preferences
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
import os
|
|
||||||
|
|
||||||
class Sith(models.Model):
|
class Sith(models.Model):
|
||||||
"""A one instance class storing all the modifiable infos"""
|
"""A one instance class storing all the modifiable infos"""
|
||||||
@ -48,12 +47,14 @@ class Sith(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "⛩ Sith ⛩"
|
return "⛩ Sith ⛩"
|
||||||
|
|
||||||
|
|
||||||
NEWS_TYPES = [
|
NEWS_TYPES = [
|
||||||
('NOTICE', _('Notice')),
|
('NOTICE', _('Notice')),
|
||||||
('EVENT', _('Event')),
|
('EVENT', _('Event')),
|
||||||
('WEEKLY', _('Weekly')),
|
('WEEKLY', _('Weekly')),
|
||||||
('CALL', _('Call')),
|
('CALL', _('Call')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class News(models.Model):
|
class News(models.Model):
|
||||||
"""The news class"""
|
"""The news class"""
|
||||||
@ -81,6 +82,7 @@ class News(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s: %s" % (self.type, self.title)
|
return "%s: %s" % (self.type, self.title)
|
||||||
|
|
||||||
|
|
||||||
class NewsDate(models.Model):
|
class NewsDate(models.Model):
|
||||||
"""
|
"""
|
||||||
A date class, useful for weekly events, or for events that just have no date
|
A date class, useful for weekly events, or for events that just have no date
|
||||||
@ -95,6 +97,7 @@ class NewsDate(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s: %s - %s" % (self.news.title, self.start_date, self.end_date)
|
return "%s: %s - %s" % (self.news.title, self.start_date, self.end_date)
|
||||||
|
|
||||||
|
|
||||||
class Weekmail(models.Model):
|
class Weekmail(models.Model):
|
||||||
"""
|
"""
|
||||||
The weekmail class
|
The weekmail class
|
||||||
@ -113,12 +116,12 @@ class Weekmail(models.Model):
|
|||||||
dest = [i[0] for i in Preferences.objects.filter(receive_weekmail=True).values_list('user__email')]
|
dest = [i[0] for i in Preferences.objects.filter(receive_weekmail=True).values_list('user__email')]
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
email = EmailMultiAlternatives(
|
email = EmailMultiAlternatives(
|
||||||
subject=self.title,
|
subject=self.title,
|
||||||
body=self.render_text(),
|
body=self.render_text(),
|
||||||
from_email=settings.SITH_COM_EMAIL,
|
from_email=settings.SITH_COM_EMAIL,
|
||||||
to=Sith.objects.first().weekmail_destinations.split(' '),
|
to=Sith.objects.first().weekmail_destinations.split(' '),
|
||||||
bcc=dest,
|
bcc=dest,
|
||||||
)
|
)
|
||||||
email.attach_alternative(self.render_html(), "text/html")
|
email.attach_alternative(self.render_html(), "text/html")
|
||||||
email.send()
|
email.send()
|
||||||
self.sent = True
|
self.sent = True
|
||||||
@ -128,12 +131,12 @@ class Weekmail(models.Model):
|
|||||||
def render_text(self):
|
def render_text(self):
|
||||||
return render(None, "com/weekmail_renderer_text.jinja", context={
|
return render(None, "com/weekmail_renderer_text.jinja", context={
|
||||||
'weekmail': self,
|
'weekmail': self,
|
||||||
}).content.decode('utf-8')
|
}).content.decode('utf-8')
|
||||||
|
|
||||||
def render_html(self):
|
def render_html(self):
|
||||||
return render(None, "com/weekmail_renderer_html.jinja", context={
|
return render(None, "com/weekmail_renderer_html.jinja", context={
|
||||||
'weekmail': self,
|
'weekmail': self,
|
||||||
}).content.decode('utf-8')
|
}).content.decode('utf-8')
|
||||||
|
|
||||||
def get_banner(self):
|
def get_banner(self):
|
||||||
return "http://" + settings.SITH_URL + static("com/img/weekmail_banner.png")
|
return "http://" + settings.SITH_URL + static("com/img/weekmail_banner.png")
|
||||||
@ -144,6 +147,7 @@ class Weekmail(models.Model):
|
|||||||
def is_owned_by(self, user):
|
def is_owned_by(self, user):
|
||||||
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
|
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
|
||||||
|
|
||||||
|
|
||||||
class WeekmailArticle(models.Model):
|
class WeekmailArticle(models.Model):
|
||||||
weekmail = models.ForeignKey(Weekmail, related_name="articles", verbose_name=_("weekmail"), null=True)
|
weekmail = models.ForeignKey(Weekmail, related_name="articles", verbose_name=_("weekmail"), null=True)
|
||||||
title = models.CharField(_("title"), max_length=64)
|
title = models.CharField(_("title"), max_length=64)
|
||||||
|
@ -28,7 +28,7 @@ from django.core.urlresolvers import reverse
|
|||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
|
||||||
from core.models import User, RealGroup
|
from core.models import User, RealGroup
|
||||||
from com.models import Sith
|
|
||||||
|
|
||||||
class ComTest(TestCase):
|
class ComTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -56,4 +56,3 @@ class ComTest(TestCase):
|
|||||||
r = self.client.get(reverse("core:index"))
|
r = self.client.get(reverse("core:index"))
|
||||||
self.assertTrue(r.status_code == 200)
|
self.assertTrue(r.status_code == 200)
|
||||||
self.assertTrue("""<div id="info_box">\\n <div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""" in str(r.content))
|
self.assertTrue("""<div id="info_box">\\n <div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""" in str(r.content))
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from com.views import *
|
from com.views import *
|
||||||
|
|
||||||
|
94
com/views.py
94
com/views.py
@ -22,9 +22,9 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import redirect, get_object_or_404
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.views.generic import ListView, DetailView, RedirectView
|
from django.views.generic import ListView, DetailView
|
||||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@ -49,6 +49,7 @@ from club.models import Club
|
|||||||
|
|
||||||
sith = Sith.objects.first
|
sith = Sith.objects.first
|
||||||
|
|
||||||
|
|
||||||
class ComTabsMixin(TabedViewMixin):
|
class ComTabsMixin(TabedViewMixin):
|
||||||
def get_tabs_title(self):
|
def get_tabs_title(self):
|
||||||
return _("Communication administration")
|
return _("Communication administration")
|
||||||
@ -56,32 +57,33 @@ class ComTabsMixin(TabedViewMixin):
|
|||||||
def get_list_of_tabs(self):
|
def get_list_of_tabs(self):
|
||||||
tab_list = []
|
tab_list = []
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('com:weekmail'),
|
'url': reverse('com:weekmail'),
|
||||||
'slug': 'weekmail',
|
'slug': 'weekmail',
|
||||||
'name': _("Weekmail"),
|
'name': _("Weekmail"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('com:weekmail_destinations'),
|
'url': reverse('com:weekmail_destinations'),
|
||||||
'slug': 'weekmail_destinations',
|
'slug': 'weekmail_destinations',
|
||||||
'name': _("Weekmail destinations"),
|
'name': _("Weekmail destinations"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('com:index_edit'),
|
'url': reverse('com:index_edit'),
|
||||||
'slug': 'index',
|
'slug': 'index',
|
||||||
'name': _("Index page"),
|
'name': _("Index page"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('com:info_edit'),
|
'url': reverse('com:info_edit'),
|
||||||
'slug': 'info',
|
'slug': 'info',
|
||||||
'name': _("Info message"),
|
'name': _("Info message"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('com:alert_edit'),
|
'url': reverse('com:alert_edit'),
|
||||||
'slug': 'alert',
|
'slug': 'alert',
|
||||||
'name': _("Alert message"),
|
'name': _("Alert message"),
|
||||||
})
|
})
|
||||||
return tab_list
|
return tab_list
|
||||||
|
|
||||||
|
|
||||||
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
||||||
model = Sith
|
model = Sith
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
@ -89,21 +91,25 @@ class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
|||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
return Sith.objects.first()
|
return Sith.objects.first()
|
||||||
|
|
||||||
|
|
||||||
class AlertMsgEditView(ComEditView):
|
class AlertMsgEditView(ComEditView):
|
||||||
fields = ['alert_msg']
|
fields = ['alert_msg']
|
||||||
current_tab = "alert"
|
current_tab = "alert"
|
||||||
success_url = reverse_lazy('com:alert_edit')
|
success_url = reverse_lazy('com:alert_edit')
|
||||||
|
|
||||||
|
|
||||||
class InfoMsgEditView(ComEditView):
|
class InfoMsgEditView(ComEditView):
|
||||||
fields = ['info_msg']
|
fields = ['info_msg']
|
||||||
current_tab = "info"
|
current_tab = "info"
|
||||||
success_url = reverse_lazy('com:info_edit')
|
success_url = reverse_lazy('com:info_edit')
|
||||||
|
|
||||||
|
|
||||||
class IndexEditView(ComEditView):
|
class IndexEditView(ComEditView):
|
||||||
fields = ['index_page']
|
fields = ['index_page']
|
||||||
current_tab = "index"
|
current_tab = "index"
|
||||||
success_url = reverse_lazy('com:index_edit')
|
success_url = reverse_lazy('com:index_edit')
|
||||||
|
|
||||||
|
|
||||||
class WeekmailDestinationEditView(ComEditView):
|
class WeekmailDestinationEditView(ComEditView):
|
||||||
fields = ['weekmail_destinations']
|
fields = ['weekmail_destinations']
|
||||||
current_tab = "weekmail_destinations"
|
current_tab = "weekmail_destinations"
|
||||||
@ -111,14 +117,15 @@ class WeekmailDestinationEditView(ComEditView):
|
|||||||
|
|
||||||
# News
|
# News
|
||||||
|
|
||||||
|
|
||||||
class NewsForm(forms.ModelForm):
|
class NewsForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = News
|
model = News
|
||||||
fields = ['type', 'title', 'club', 'summary', 'content', 'author']
|
fields = ['type', 'title', 'club', 'summary', 'content', 'author']
|
||||||
widgets = {
|
widgets = {
|
||||||
'author': forms.HiddenInput,
|
'author': forms.HiddenInput,
|
||||||
'type': forms.RadioSelect,
|
'type': forms.RadioSelect,
|
||||||
}
|
}
|
||||||
start_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Start date"), widget=SelectDateTime, required=False)
|
start_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Start date"), widget=SelectDateTime, required=False)
|
||||||
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), widget=SelectDateTime, required=False)
|
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), widget=SelectDateTime, required=False)
|
||||||
until = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Until"), widget=SelectDateTime, required=False)
|
until = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Until"), widget=SelectDateTime, required=False)
|
||||||
@ -142,19 +149,20 @@ class NewsForm(forms.ModelForm):
|
|||||||
self.instance.dates.all().delete()
|
self.instance.dates.all().delete()
|
||||||
if self.instance.type == "EVENT" or self.instance.type == "CALL":
|
if self.instance.type == "EVENT" or self.instance.type == "CALL":
|
||||||
NewsDate(start_date=self.cleaned_data['start_date'],
|
NewsDate(start_date=self.cleaned_data['start_date'],
|
||||||
end_date=self.cleaned_data['end_date'],
|
end_date=self.cleaned_data['end_date'],
|
||||||
news=self.instance).save()
|
news=self.instance).save()
|
||||||
elif self.instance.type == "WEEKLY":
|
elif self.instance.type == "WEEKLY":
|
||||||
start_date = self.cleaned_data['start_date']
|
start_date = self.cleaned_data['start_date']
|
||||||
end_date = self.cleaned_data['end_date']
|
end_date = self.cleaned_data['end_date']
|
||||||
while start_date <= self.cleaned_data['until']:
|
while start_date <= self.cleaned_data['until']:
|
||||||
NewsDate(start_date=start_date,
|
NewsDate(start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
news=self.instance).save()
|
news=self.instance).save()
|
||||||
start_date += timedelta(days=7)
|
start_date += timedelta(days=7)
|
||||||
end_date += timedelta(days=7)
|
end_date += timedelta(days=7)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class NewsEditView(CanEditMixin, UpdateView):
|
class NewsEditView(CanEditMixin, UpdateView):
|
||||||
model = News
|
model = News
|
||||||
form_class = NewsForm
|
form_class = NewsForm
|
||||||
@ -165,15 +173,17 @@ class NewsEditView(CanEditMixin, UpdateView):
|
|||||||
init = {}
|
init = {}
|
||||||
try:
|
try:
|
||||||
init['start_date'] = self.object.dates.order_by('id').first().start_date.strftime('%Y-%m-%d %H:%M:%S')
|
init['start_date'] = self.object.dates.order_by('id').first().start_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
init['end_date'] = self.object.dates.order_by('id').first().end_date.strftime('%Y-%m-%d %H:%M:%S')
|
init['end_date'] = self.object.dates.order_by('id').first().end_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return init
|
return init
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
if form.is_valid() and not 'preview' in request.POST.keys():
|
if form.is_valid() and 'preview' not in request.POST.keys():
|
||||||
return self.form_valid(form)
|
return self.form_valid(form)
|
||||||
else:
|
else:
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
@ -192,6 +202,7 @@ class NewsEditView(CanEditMixin, UpdateView):
|
|||||||
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
||||||
return super(NewsEditView, self).form_valid(form)
|
return super(NewsEditView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class NewsCreateView(CanCreateMixin, CreateView):
|
class NewsCreateView(CanCreateMixin, CreateView):
|
||||||
model = News
|
model = News
|
||||||
form_class = NewsForm
|
form_class = NewsForm
|
||||||
@ -201,12 +212,13 @@ class NewsCreateView(CanCreateMixin, CreateView):
|
|||||||
init = {'author': self.request.user}
|
init = {'author': self.request.user}
|
||||||
try:
|
try:
|
||||||
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return init
|
return init
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
if form.is_valid() and not 'preview' in request.POST.keys():
|
if form.is_valid() and 'preview' not in request.POST.keys():
|
||||||
return self.form_valid(form)
|
return self.form_valid(form)
|
||||||
else:
|
else:
|
||||||
self.object = form.instance
|
self.object = form.instance
|
||||||
@ -224,6 +236,7 @@ class NewsCreateView(CanCreateMixin, CreateView):
|
|||||||
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
||||||
return super(NewsCreateView, self).form_valid(form)
|
return super(NewsCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
||||||
model = News
|
model = News
|
||||||
pk_url_kwarg = 'news_id'
|
pk_url_kwarg = 'news_id'
|
||||||
@ -240,11 +253,13 @@ class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
|||||||
return redirect(self.request.GET['next'])
|
return redirect(self.request.GET['next'])
|
||||||
return redirect('com:news_admin_list')
|
return redirect('com:news_admin_list')
|
||||||
|
|
||||||
|
|
||||||
class NewsAdminListView(CanEditMixin, ListView):
|
class NewsAdminListView(CanEditMixin, ListView):
|
||||||
model = News
|
model = News
|
||||||
template_name = 'com/news_admin_list.jinja'
|
template_name = 'com/news_admin_list.jinja'
|
||||||
queryset = News.objects.filter(dates__end_date__gte=timezone.now()).distinct().order_by('id')
|
queryset = News.objects.filter(dates__end_date__gte=timezone.now()).distinct().order_by('id')
|
||||||
|
|
||||||
|
|
||||||
class NewsListView(CanViewMixin, ListView):
|
class NewsListView(CanViewMixin, ListView):
|
||||||
model = News
|
model = News
|
||||||
template_name = 'com/news_list.jinja'
|
template_name = 'com/news_list.jinja'
|
||||||
@ -255,6 +270,7 @@ class NewsListView(CanViewMixin, ListView):
|
|||||||
kwargs['timedelta'] = timedelta
|
kwargs['timedelta'] = timedelta
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class NewsDetailView(CanViewMixin, DetailView):
|
class NewsDetailView(CanViewMixin, DetailView):
|
||||||
model = News
|
model = News
|
||||||
template_name = 'com/news_detail.jinja'
|
template_name = 'com/news_detail.jinja'
|
||||||
@ -262,6 +278,7 @@ class NewsDetailView(CanViewMixin, DetailView):
|
|||||||
|
|
||||||
# Weekmail
|
# Weekmail
|
||||||
|
|
||||||
|
|
||||||
class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
||||||
model = Weekmail
|
model = Weekmail
|
||||||
template_name = 'com/weekmail_preview.jinja'
|
template_name = 'com/weekmail_preview.jinja'
|
||||||
@ -274,7 +291,8 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
|||||||
if request.POST['send'] == "validate":
|
if request.POST['send'] == "validate":
|
||||||
self.object.send()
|
self.object.send()
|
||||||
return HttpResponseRedirect(reverse('com:weekmail') + "?qn_weekmail_send_success")
|
return HttpResponseRedirect(reverse('com:weekmail') + "?qn_weekmail_send_success")
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return super(WeekmailEditView, self).get(request, *args, **kwargs)
|
return super(WeekmailEditView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
@ -286,11 +304,12 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
|||||||
kwargs['weekmail_rendered'] = self.object.render_html()
|
kwargs['weekmail_rendered'] = self.object.render_html()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
||||||
model = Weekmail
|
model = Weekmail
|
||||||
template_name = 'com/weekmail.jinja'
|
template_name = 'com/weekmail.jinja'
|
||||||
form_class = modelform_factory(Weekmail, fields=['title', 'intro', 'joke', 'protip', 'conclusion'],
|
form_class = modelform_factory(Weekmail, fields=['title', 'intro', 'joke', 'protip', 'conclusion'],
|
||||||
help_texts={'title': _("Delete and save to regenerate")})
|
help_texts={'title': _("Delete and save to regenerate")})
|
||||||
success_url = reverse_lazy('com:weekmail')
|
success_url = reverse_lazy('com:weekmail')
|
||||||
current_tab = "weekmail"
|
current_tab = "weekmail"
|
||||||
|
|
||||||
@ -341,6 +360,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
|
|||||||
kwargs['orphans'] = WeekmailArticle.objects.filter(weekmail=None)
|
kwargs['orphans'] = WeekmailArticle.objects.filter(weekmail=None)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
||||||
"""Edit an article"""
|
"""Edit an article"""
|
||||||
model = WeekmailArticle
|
model = WeekmailArticle
|
||||||
@ -351,6 +371,7 @@ class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, U
|
|||||||
quick_notif_url_arg = "qn_weekmail_article_edit"
|
quick_notif_url_arg = "qn_weekmail_article_edit"
|
||||||
current_tab = "weekmail"
|
current_tab = "weekmail"
|
||||||
|
|
||||||
|
|
||||||
class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||||
"""Post an article"""
|
"""Post an article"""
|
||||||
model = WeekmailArticle
|
model = WeekmailArticle
|
||||||
@ -363,13 +384,14 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
|||||||
init = {}
|
init = {}
|
||||||
try:
|
try:
|
||||||
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return init
|
return init
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
self.object = form.instance
|
self.object = form.instance
|
||||||
form.is_valid() # Valid a first time to populate club field
|
form.is_valid() # Valid a first time to populate club field
|
||||||
try:
|
try:
|
||||||
m = form.instance.club.get_membership_for(request.user)
|
m = form.instance.club.get_membership_for(request.user)
|
||||||
if m.role <= settings.SITH_MAXIMUM_FREE_ROLE:
|
if m.role <= settings.SITH_MAXIMUM_FREE_ROLE:
|
||||||
@ -385,14 +407,10 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
|||||||
form.instance.author = self.request.user
|
form.instance.author = self.request.user
|
||||||
return super(WeekmailArticleCreateView, self).form_valid(form)
|
return super(WeekmailArticleCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
|
class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
|
||||||
"""Delete an article"""
|
"""Delete an article"""
|
||||||
model = WeekmailArticle
|
model = WeekmailArticle
|
||||||
template_name = 'core/delete_confirm.jinja'
|
template_name = 'core/delete_confirm.jinja'
|
||||||
success_url = reverse_lazy('com:weekmail')
|
success_url = reverse_lazy('com:weekmail')
|
||||||
pk_url_kwarg = "article_id"
|
pk_url_kwarg = "article_id"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,8 +33,9 @@ admin.site.unregister(AuthGroup)
|
|||||||
admin.site.register(RealGroup)
|
admin.site.register(RealGroup)
|
||||||
admin.site.register(Page)
|
admin.site.register(Page)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(SithFile)
|
@admin.register(SithFile)
|
||||||
class SithFileAdmin(admin.ModelAdmin):
|
class SithFileAdmin(admin.ModelAdmin):
|
||||||
form = make_ajax_form(SithFile, {
|
form = make_ajax_form(SithFile, {
|
||||||
'parent': 'files', # ManyToManyField
|
'parent': 'files', # ManyToManyField
|
||||||
})
|
})
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.dispatch import receiver
|
|
||||||
from django.core.signals import request_started
|
from django.core.signals import request_started
|
||||||
|
|
||||||
|
|
||||||
class SithConfig(AppConfig):
|
class SithConfig(AppConfig):
|
||||||
name = 'core'
|
name = 'core'
|
||||||
verbose_name = "Core app of the Sith"
|
verbose_name = "Core app of the Sith"
|
||||||
@ -48,4 +48,3 @@ class SithConfig(AppConfig):
|
|||||||
request_started.connect(clear_cached_groups, weak=False, dispatch_uid="clear_cached_groups")
|
request_started.connect(clear_cached_groups, weak=False, dispatch_uid="clear_cached_groups")
|
||||||
request_started.connect(clear_cached_memberships, weak=False, dispatch_uid="clear_cached_memberships")
|
request_started.connect(clear_cached_memberships, weak=False, dispatch_uid="clear_cached_memberships")
|
||||||
# TODO: there may be a need to add more cache clearing
|
# TODO: there may be a need to add more cache clearing
|
||||||
|
|
||||||
|
@ -31,16 +31,19 @@ from club.models import Club
|
|||||||
from counter.models import Product, Counter
|
from counter.models import Product, Counter
|
||||||
from accounting.models import ClubAccount, Company
|
from accounting.models import ClubAccount, Company
|
||||||
|
|
||||||
|
|
||||||
def check_token(request):
|
def check_token(request):
|
||||||
return ('counter_token' in request.session.keys() and
|
return ('counter_token' in request.session.keys() and
|
||||||
request.session['counter_token'] and
|
request.session['counter_token'] and
|
||||||
Counter.objects.filter(token=request.session['counter_token']).exists())
|
Counter.objects.filter(token=request.session['counter_token']).exists())
|
||||||
|
|
||||||
|
|
||||||
class RightManagedLookupChannel(LookupChannel):
|
class RightManagedLookupChannel(LookupChannel):
|
||||||
def check_auth(self, request):
|
def check_auth(self, request):
|
||||||
if not request.user.was_subscribed and not check_token(request):
|
if not request.user.was_subscribed and not check_token(request):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
@register('users')
|
@register('users')
|
||||||
class UsersLookup(RightManagedLookupChannel):
|
class UsersLookup(RightManagedLookupChannel):
|
||||||
model = User
|
model = User
|
||||||
@ -54,6 +57,7 @@ class UsersLookup(RightManagedLookupChannel):
|
|||||||
def format_item_display(self, item):
|
def format_item_display(self, item):
|
||||||
return item.get_display_name()
|
return item.get_display_name()
|
||||||
|
|
||||||
|
|
||||||
@register('groups')
|
@register('groups')
|
||||||
class GroupsLookup(RightManagedLookupChannel):
|
class GroupsLookup(RightManagedLookupChannel):
|
||||||
model = Group
|
model = Group
|
||||||
@ -67,6 +71,7 @@ class GroupsLookup(RightManagedLookupChannel):
|
|||||||
def format_item_display(self, item):
|
def format_item_display(self, item):
|
||||||
return item.name
|
return item.name
|
||||||
|
|
||||||
|
|
||||||
@register('clubs')
|
@register('clubs')
|
||||||
class ClubLookup(RightManagedLookupChannel):
|
class ClubLookup(RightManagedLookupChannel):
|
||||||
model = Club
|
model = Club
|
||||||
@ -80,6 +85,7 @@ class ClubLookup(RightManagedLookupChannel):
|
|||||||
def format_item_display(self, item):
|
def format_item_display(self, item):
|
||||||
return item.name
|
return item.name
|
||||||
|
|
||||||
|
|
||||||
@register('counters')
|
@register('counters')
|
||||||
class CountersLookup(RightManagedLookupChannel):
|
class CountersLookup(RightManagedLookupChannel):
|
||||||
model = Counter
|
model = Counter
|
||||||
@ -90,6 +96,7 @@ class CountersLookup(RightManagedLookupChannel):
|
|||||||
def format_item_display(self, item):
|
def format_item_display(self, item):
|
||||||
return item.name
|
return item.name
|
||||||
|
|
||||||
|
|
||||||
@register('products')
|
@register('products')
|
||||||
class ProductsLookup(RightManagedLookupChannel):
|
class ProductsLookup(RightManagedLookupChannel):
|
||||||
model = Product
|
model = Product
|
||||||
@ -101,6 +108,7 @@ class ProductsLookup(RightManagedLookupChannel):
|
|||||||
def format_item_display(self, item):
|
def format_item_display(self, item):
|
||||||
return "%s (%s)" % (item.name, item.code)
|
return "%s (%s)" % (item.name, item.code)
|
||||||
|
|
||||||
|
|
||||||
@register('files')
|
@register('files')
|
||||||
class SithFileLookup(RightManagedLookupChannel):
|
class SithFileLookup(RightManagedLookupChannel):
|
||||||
model = SithFile
|
model = SithFile
|
||||||
@ -108,6 +116,7 @@ class SithFileLookup(RightManagedLookupChannel):
|
|||||||
def get_query(self, q, request):
|
def get_query(self, q, request):
|
||||||
return self.model.objects.filter(name__icontains=q)[:50]
|
return self.model.objects.filter(name__icontains=q)[:50]
|
||||||
|
|
||||||
|
|
||||||
@register('club_accounts')
|
@register('club_accounts')
|
||||||
class ClubAccountLookup(RightManagedLookupChannel):
|
class ClubAccountLookup(RightManagedLookupChannel):
|
||||||
model = ClubAccount
|
model = ClubAccount
|
||||||
@ -118,6 +127,7 @@ class ClubAccountLookup(RightManagedLookupChannel):
|
|||||||
def format_item_display(self, item):
|
def format_item_display(self, item):
|
||||||
return item.name
|
return item.name
|
||||||
|
|
||||||
|
|
||||||
@register('companies')
|
@register('companies')
|
||||||
class CompaniesLookup(RightManagedLookupChannel):
|
class CompaniesLookup(RightManagedLookupChannel):
|
||||||
model = Company
|
model = Company
|
||||||
|
@ -21,4 +21,3 @@
|
|||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ class Command(BaseCommand):
|
|||||||
args['precision'] = settings.SASS_PRECISION
|
args['precision'] = settings.SASS_PRECISION
|
||||||
return sass.compile(**args)
|
return sass.compile(**args)
|
||||||
|
|
||||||
|
|
||||||
def is_compilable(self, file, ext_list):
|
def is_compilable(self, file, ext_list):
|
||||||
path, ext = os.path.splitext(file)
|
path, ext = os.path.splitext(file)
|
||||||
return ext in ext_list
|
return ext in ext_list
|
||||||
|
@ -26,7 +26,7 @@ import os
|
|||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from io import StringIO, BytesIO
|
from io import StringIO, BytesIO
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
@ -42,7 +42,7 @@ from subscription.models import Subscription
|
|||||||
from counter.models import Customer, ProductType, Product, Counter
|
from counter.models import Customer, ProductType, Product, Counter
|
||||||
from com.models import Sith, Weekmail
|
from com.models import Sith, Weekmail
|
||||||
from election.models import Election, Role, Candidature, ElectionList
|
from election.models import Election, Role, Candidature, ElectionList
|
||||||
from forum.models import Forum, ForumMessage, ForumTopic
|
from forum.models import Forum, ForumTopic
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@ -75,9 +75,9 @@ class Command(BaseCommand):
|
|||||||
Group(name="Forum admin").save()
|
Group(name="Forum admin").save()
|
||||||
self.reset_index("core", "auth")
|
self.reset_index("core", "auth")
|
||||||
root = User(id=0, username='root', last_name="", first_name="Bibou",
|
root = User(id=0, username='root', last_name="", first_name="Bibou",
|
||||||
email="ae.info@utbm.fr",
|
email="ae.info@utbm.fr",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12",
|
||||||
is_superuser=True, is_staff=True)
|
is_superuser=True, is_staff=True)
|
||||||
root.set_password("plop")
|
root.set_password("plop")
|
||||||
root.save()
|
root.save()
|
||||||
profiles_root = SithFile(parent=None, name="profiles", is_folder=True, owner=root)
|
profiles_root = SithFile(parent=None, name="profiles", is_folder=True, owner=root)
|
||||||
@ -88,18 +88,18 @@ class Command(BaseCommand):
|
|||||||
club_root.save()
|
club_root.save()
|
||||||
SithFile(parent=None, name="SAS", is_folder=True, owner=root).save()
|
SithFile(parent=None, name="SAS", is_folder=True, owner=root).save()
|
||||||
main_club = Club(id=1, name=settings.SITH_MAIN_CLUB['name'], unix_name=settings.SITH_MAIN_CLUB['unix_name'],
|
main_club = Club(id=1, name=settings.SITH_MAIN_CLUB['name'], unix_name=settings.SITH_MAIN_CLUB['unix_name'],
|
||||||
address=settings.SITH_MAIN_CLUB['address'])
|
address=settings.SITH_MAIN_CLUB['address'])
|
||||||
main_club.save()
|
main_club.save()
|
||||||
bar_club = Club(id=2, name=settings.SITH_BAR_MANAGER['name'], unix_name=settings.SITH_BAR_MANAGER['unix_name'],
|
bar_club = Club(id=2, name=settings.SITH_BAR_MANAGER['name'], unix_name=settings.SITH_BAR_MANAGER['unix_name'],
|
||||||
address=settings.SITH_BAR_MANAGER['address'])
|
address=settings.SITH_BAR_MANAGER['address'])
|
||||||
bar_club.save()
|
bar_club.save()
|
||||||
launderette_club = Club(id=84, name=settings.SITH_LAUNDERETTE_MANAGER['name'],
|
launderette_club = Club(id=84, name=settings.SITH_LAUNDERETTE_MANAGER['name'],
|
||||||
unix_name=settings.SITH_LAUNDERETTE_MANAGER['unix_name'],
|
unix_name=settings.SITH_LAUNDERETTE_MANAGER['unix_name'],
|
||||||
address=settings.SITH_LAUNDERETTE_MANAGER['address'])
|
address=settings.SITH_LAUNDERETTE_MANAGER['address'])
|
||||||
launderette_club.save()
|
launderette_club.save()
|
||||||
self.reset_index("club")
|
self.reset_index("club")
|
||||||
for b in settings.SITH_COUNTER_BARS:
|
for b in settings.SITH_COUNTER_BARS:
|
||||||
g = Group(name=b[1]+" admin")
|
g = Group(name=b[1] + " admin")
|
||||||
g.save()
|
g.save()
|
||||||
c = Counter(id=b[0], name=b[1], club=bar_club, type='BAR')
|
c = Counter(id=b[0], name=b[1], club=bar_club, type='BAR')
|
||||||
c.save()
|
c.save()
|
||||||
@ -120,7 +120,7 @@ class Command(BaseCommand):
|
|||||||
p = Page(name='Index')
|
p = Page(name='Index')
|
||||||
p.set_lock(root)
|
p.set_lock(root)
|
||||||
p.save()
|
p.save()
|
||||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||||
p.set_lock(root)
|
p.set_lock(root)
|
||||||
p.save()
|
p.save()
|
||||||
PageRev(page=p, title="Wiki index", author=root, content="""
|
PageRev(page=p, title="Wiki index", author=root, content="""
|
||||||
@ -130,7 +130,7 @@ Welcome to the wiki page!
|
|||||||
p = Page(name="services")
|
p = Page(name="services")
|
||||||
p.set_lock(root)
|
p.set_lock(root)
|
||||||
p.save()
|
p.save()
|
||||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||||
p.set_lock(root)
|
p.set_lock(root)
|
||||||
PageRev(page=p, title="Services", author=root, content="""
|
PageRev(page=p, title="Services", author=root, content="""
|
||||||
| | | |
|
| | | |
|
||||||
@ -150,18 +150,18 @@ Welcome to the wiki page!
|
|||||||
if not options['prod']:
|
if not options['prod']:
|
||||||
# Adding user Skia
|
# Adding user Skia
|
||||||
skia = User(username='skia', last_name="Kia", first_name="S'",
|
skia = User(username='skia', last_name="Kia", first_name="S'",
|
||||||
email="skia@git.an",
|
email="skia@git.an",
|
||||||
date_of_birth="1942-06-12")
|
date_of_birth="1942-06-12")
|
||||||
skia.set_password("plop")
|
skia.set_password("plop")
|
||||||
skia.save()
|
skia.save()
|
||||||
skia.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
skia.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
skia.save()
|
skia.save()
|
||||||
skia_profile_path = os.path.join(root_path, 'core/fixtures/images/3.jpg')
|
skia_profile_path = os.path.join(root_path, 'core/fixtures/images/3.jpg')
|
||||||
with open(skia_profile_path, 'rb') as f:
|
with open(skia_profile_path, 'rb') as f:
|
||||||
name = str(skia.id) + "_profile.jpg"
|
name = str(skia.id) + "_profile.jpg"
|
||||||
skia_profile = SithFile(parent=profiles_root, name=name,
|
skia_profile = SithFile(parent=profiles_root, name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, 'JPEG'),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, 'JPEG'),
|
||||||
owner=skia, is_folder=False, mime_type='image/jpeg', size=os.path.getsize(skia_profile_path))
|
owner=skia, is_folder=False, mime_type='image/jpeg', size=os.path.getsize(skia_profile_path))
|
||||||
skia_profile.file.name = name
|
skia_profile.file.name = name
|
||||||
skia_profile.save()
|
skia_profile.save()
|
||||||
skia.profile_pict = skia_profile
|
skia.profile_pict = skia_profile
|
||||||
@ -169,50 +169,50 @@ Welcome to the wiki page!
|
|||||||
|
|
||||||
# Adding user public
|
# Adding user public
|
||||||
public = User(username='public', last_name="Not subscribed", first_name="Public",
|
public = User(username='public', last_name="Not subscribed", first_name="Public",
|
||||||
email="public@git.an",
|
email="public@git.an",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12",
|
||||||
is_superuser=False, is_staff=False)
|
is_superuser=False, is_staff=False)
|
||||||
public.set_password("plop")
|
public.set_password("plop")
|
||||||
public.save()
|
public.save()
|
||||||
public.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
public.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
public.save()
|
public.save()
|
||||||
# Adding user Subscriber
|
# Adding user Subscriber
|
||||||
subscriber = User(username='subscriber', last_name="User", first_name="Subscribed",
|
subscriber = User(username='subscriber', last_name="User", first_name="Subscribed",
|
||||||
email="Subscribed@git.an",
|
email="Subscribed@git.an",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12",
|
||||||
is_superuser=False, is_staff=False)
|
is_superuser=False, is_staff=False)
|
||||||
subscriber.set_password("plop")
|
subscriber.set_password("plop")
|
||||||
subscriber.save()
|
subscriber.save()
|
||||||
subscriber.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
subscriber.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
subscriber.save()
|
subscriber.save()
|
||||||
# Adding user old Subscriber
|
# Adding user old Subscriber
|
||||||
old_subscriber = User(username='old_subscriber', last_name="Subscriber", first_name="Old",
|
old_subscriber = User(username='old_subscriber', last_name="Subscriber", first_name="Old",
|
||||||
email="old_subscriber@git.an",
|
email="old_subscriber@git.an",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12",
|
||||||
is_superuser=False, is_staff=False)
|
is_superuser=False, is_staff=False)
|
||||||
old_subscriber.set_password("plop")
|
old_subscriber.set_password("plop")
|
||||||
old_subscriber.save()
|
old_subscriber.save()
|
||||||
old_subscriber.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
old_subscriber.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
old_subscriber.save()
|
old_subscriber.save()
|
||||||
# Adding user Counter admin
|
# Adding user Counter admin
|
||||||
counter = User(username='counter', last_name="Ter", first_name="Coun",
|
counter = User(username='counter', last_name="Ter", first_name="Coun",
|
||||||
email="counter@git.an",
|
email="counter@git.an",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12",
|
||||||
is_superuser=False, is_staff=False)
|
is_superuser=False, is_staff=False)
|
||||||
counter.set_password("plop")
|
counter.set_password("plop")
|
||||||
counter.save()
|
counter.save()
|
||||||
counter.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
counter.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
counter.groups=[Group.objects.filter(id=settings.SITH_GROUP_COUNTER_ADMIN_ID).first().id]
|
counter.groups = [Group.objects.filter(id=settings.SITH_GROUP_COUNTER_ADMIN_ID).first().id]
|
||||||
counter.save()
|
counter.save()
|
||||||
# Adding user Comptable
|
# Adding user Comptable
|
||||||
comptable = User(username='comptable', last_name="Able", first_name="Compte",
|
comptable = User(username='comptable', last_name="Able", first_name="Compte",
|
||||||
email="compta@git.an",
|
email="compta@git.an",
|
||||||
date_of_birth="1942-06-12",
|
date_of_birth="1942-06-12",
|
||||||
is_superuser=False, is_staff=False)
|
is_superuser=False, is_staff=False)
|
||||||
comptable.set_password("plop")
|
comptable.set_password("plop")
|
||||||
comptable.save()
|
comptable.save()
|
||||||
comptable.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
comptable.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
comptable.groups=[Group.objects.filter(id=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID).first().id]
|
comptable.groups = [Group.objects.filter(id=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID).first().id]
|
||||||
comptable.save()
|
comptable.save()
|
||||||
# Adding user Guy
|
# Adding user Guy
|
||||||
u = User(username='guy', last_name="Carlier", first_name="Guy",
|
u = User(username='guy', last_name="Carlier", first_name="Guy",
|
||||||
@ -221,7 +221,7 @@ Welcome to the wiki page!
|
|||||||
is_superuser=False, is_staff=False)
|
is_superuser=False, is_staff=False)
|
||||||
u.set_password("plop")
|
u.set_password("plop")
|
||||||
u.save()
|
u.save()
|
||||||
u.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
u.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
u.save()
|
u.save()
|
||||||
# Adding user Richard Batsbak
|
# Adding user Richard Batsbak
|
||||||
r = User(username='rbatsbak', last_name="Batsbak", first_name="Richard",
|
r = User(username='rbatsbak', last_name="Batsbak", first_name="Richard",
|
||||||
@ -229,18 +229,18 @@ Welcome to the wiki page!
|
|||||||
date_of_birth="1982-06-12")
|
date_of_birth="1982-06-12")
|
||||||
r.set_password("plop")
|
r.set_password("plop")
|
||||||
r.save()
|
r.save()
|
||||||
r.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
r.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
r.save()
|
r.save()
|
||||||
# Adding syntax help page
|
# Adding syntax help page
|
||||||
p = Page(name='Aide_sur_la_syntaxe')
|
p = Page(name='Aide_sur_la_syntaxe')
|
||||||
p.save(force_lock=True)
|
p.save(force_lock=True)
|
||||||
with open(os.path.join(root_path)+'/doc/SYNTAX.md', 'r') as rm:
|
with open(os.path.join(root_path) + '/doc/SYNTAX.md', 'r') as rm:
|
||||||
PageRev(page=p, title="Aide sur la syntaxe", author=skia, content=rm.read()).save()
|
PageRev(page=p, title="Aide sur la syntaxe", author=skia, content=rm.read()).save()
|
||||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||||
p.save(force_lock=True)
|
p.save(force_lock=True)
|
||||||
p = Page(name='Services')
|
p = Page(name='Services')
|
||||||
p.save(force_lock=True)
|
p.save(force_lock=True)
|
||||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||||
p.save(force_lock=True)
|
p.save(force_lock=True)
|
||||||
PageRev(page=p, title="Services", author=skia, content="""
|
PageRev(page=p, title="Services", author=skia, content="""
|
||||||
| | | |
|
| | | |
|
||||||
@ -252,83 +252,83 @@ Welcome to the wiki page!
|
|||||||
# Adding README
|
# Adding README
|
||||||
p = Page(name='README')
|
p = Page(name='README')
|
||||||
p.save(force_lock=True)
|
p.save(force_lock=True)
|
||||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||||
p.save(force_lock=True)
|
p.save(force_lock=True)
|
||||||
with open(os.path.join(root_path)+'/README.md', 'r') as rm:
|
with open(os.path.join(root_path) + '/README.md', 'r') as rm:
|
||||||
PageRev(page=p, title="README", author=skia, content=rm.read()).save()
|
PageRev(page=p, title="README", author=skia, content=rm.read()).save()
|
||||||
|
|
||||||
# Subscription
|
# Subscription
|
||||||
## Root
|
# Root
|
||||||
s = Subscription(member=User.objects.filter(pk=root.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=root.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
## Skia
|
# Skia
|
||||||
s = Subscription(member=User.objects.filter(pk=skia.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=skia.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
## Counter admin
|
# Counter admin
|
||||||
s = Subscription(member=User.objects.filter(pk=counter.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=counter.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
## Comptable
|
# Comptable
|
||||||
s = Subscription(member=User.objects.filter(pk=comptable.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=comptable.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
## Richard
|
# Richard
|
||||||
s = Subscription(member=User.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
## User
|
# User
|
||||||
s = Subscription(member=User.objects.filter(pk=subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
## Old subscriber
|
# Old subscriber
|
||||||
s = Subscription(member=User.objects.filter(pk=old_subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=old_subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start(datetime(year=2012, month=9, day=4))
|
s.subscription_start = s.compute_start(datetime(year=2012, month=9, day=4))
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
|
|
||||||
# Clubs
|
# Clubs
|
||||||
Club(name="Bibo'UT", unix_name="bibout",
|
Club(name="Bibo'UT", unix_name="bibout",
|
||||||
address="46 de la Boustifaille", parent=main_club).save()
|
address="46 de la Boustifaille", parent=main_club).save()
|
||||||
guyut = Club(name="Guy'UT", unix_name="guyut",
|
guyut = Club(name="Guy'UT", unix_name="guyut",
|
||||||
address="42 de la Boustifaille", parent=main_club)
|
address="42 de la Boustifaille", parent=main_club)
|
||||||
guyut.save()
|
guyut.save()
|
||||||
Club(name="Woenzel'UT", unix_name="woenzel",
|
Club(name="Woenzel'UT", unix_name="woenzel",
|
||||||
address="Woenzel", parent=guyut).save()
|
address="Woenzel", parent=guyut).save()
|
||||||
Membership(user=skia, club=main_club, role=3, description="").save()
|
Membership(user=skia, club=main_club, role=3, description="").save()
|
||||||
troll = Club(name="Troll Penché", unix_name="troll",
|
troll = Club(name="Troll Penché", unix_name="troll",
|
||||||
address="Terre Du Milieu", parent=main_club)
|
address="Terre Du Milieu", parent=main_club)
|
||||||
troll.save()
|
troll.save()
|
||||||
refound = Club(name="Carte AE", unix_name="carte_ae",
|
refound = Club(name="Carte AE", unix_name="carte_ae",
|
||||||
address="Jamais imprimée", parent=main_club)
|
address="Jamais imprimée", parent=main_club)
|
||||||
refound.save()
|
refound.save()
|
||||||
|
|
||||||
# Counters
|
# Counters
|
||||||
@ -341,19 +341,19 @@ Welcome to the wiki page!
|
|||||||
r = ProductType(name="Rechargements")
|
r = ProductType(name="Rechargements")
|
||||||
r.save()
|
r.save()
|
||||||
cotis = Product(name="Cotis 1 semestre", code="1SCOTIZ", product_type=c, purchase_price="15", selling_price="15",
|
cotis = Product(name="Cotis 1 semestre", code="1SCOTIZ", product_type=c, purchase_price="15", selling_price="15",
|
||||||
special_selling_price="15", club=main_club)
|
special_selling_price="15", club=main_club)
|
||||||
cotis.save()
|
cotis.save()
|
||||||
cotis2 = Product(name="Cotis 2 semestres", code="2SCOTIZ", product_type=c, purchase_price="28", selling_price="28",
|
cotis2 = Product(name="Cotis 2 semestres", code="2SCOTIZ", product_type=c, purchase_price="28", selling_price="28",
|
||||||
special_selling_price="28", club=main_club)
|
special_selling_price="28", club=main_club)
|
||||||
cotis2.save()
|
cotis2.save()
|
||||||
refill = Product(name="Rechargement 15 €", code="15REFILL", product_type=r, purchase_price="15", selling_price="15",
|
refill = Product(name="Rechargement 15 €", code="15REFILL", product_type=r, purchase_price="15", selling_price="15",
|
||||||
special_selling_price="15", club=main_club)
|
special_selling_price="15", club=main_club)
|
||||||
refill.save()
|
refill.save()
|
||||||
barb = Product(name="Barbar", code="BARB", product_type=p, purchase_price="1.50", selling_price="1.7",
|
barb = Product(name="Barbar", code="BARB", product_type=p, purchase_price="1.50", selling_price="1.7",
|
||||||
special_selling_price="1.6", club=main_club)
|
special_selling_price="1.6", club=main_club)
|
||||||
barb.save()
|
barb.save()
|
||||||
cble = Product(name="Chimay Bleue", code="CBLE", product_type=p, purchase_price="1.50", selling_price="1.7",
|
cble = Product(name="Chimay Bleue", code="CBLE", product_type=p, purchase_price="1.50", selling_price="1.7",
|
||||||
special_selling_price="1.6", club=main_club)
|
special_selling_price="1.6", club=main_club)
|
||||||
cble.save()
|
cble.save()
|
||||||
Product(name="Corsendonk", code="CORS", product_type=p, purchase_price="1.50", selling_price="1.7",
|
Product(name="Corsendonk", code="CORS", product_type=p, purchase_price="1.50", selling_price="1.7",
|
||||||
special_selling_price="1.6", club=main_club).save()
|
special_selling_price="1.6", club=main_club).save()
|
||||||
@ -375,7 +375,7 @@ Welcome to the wiki page!
|
|||||||
refound_counter = Counter(name="Carte AE", club=refound, type='OFFICE')
|
refound_counter = Counter(name="Carte AE", club=refound, type='OFFICE')
|
||||||
refound_counter.save()
|
refound_counter.save()
|
||||||
refound_product = Product(name="remboursement", code="REMBOURS", purchase_price="0", selling_price="0",
|
refound_product = Product(name="remboursement", code="REMBOURS", purchase_price="0", selling_price="0",
|
||||||
special_selling_price="0", club=refound)
|
special_selling_price="0", club=refound)
|
||||||
refound_product.save()
|
refound_product.save()
|
||||||
|
|
||||||
# Accounting test values:
|
# Accounting test values:
|
||||||
@ -397,28 +397,28 @@ Welcome to the wiki page!
|
|||||||
buying.save()
|
buying.save()
|
||||||
comptes = AccountingType(code='6', label="Comptes de charge", movement_type='DEBIT')
|
comptes = AccountingType(code='6', label="Comptes de charge", movement_type='DEBIT')
|
||||||
comptes.save()
|
comptes.save()
|
||||||
simple = SimplifiedAccountingType(label = 'Je fais du simple 6', accounting_type = comptes, movement_type='DEBIT')
|
simple = SimplifiedAccountingType(label='Je fais du simple 6', accounting_type=comptes, movement_type='DEBIT')
|
||||||
simple.save()
|
simple.save()
|
||||||
woenzco = Company(name="Woenzel & co")
|
woenzco = Company(name="Woenzel & co")
|
||||||
woenzco.save()
|
woenzco.save()
|
||||||
|
|
||||||
operation_list = [
|
operation_list = [
|
||||||
(27, "J'avais trop de bière", 'CASH', None, buying, 'USER', skia.id, "", None),
|
(27, "J'avais trop de bière", 'CASH', None, buying, 'USER', skia.id, "", None),
|
||||||
(4000, "Ceci n'est pas une opération... en fait si mais non", 'CHECK', None, debit,'COMPANY', woenzco.id, "", 23),
|
(4000, "Ceci n'est pas une opération... en fait si mais non", 'CHECK', None, debit, 'COMPANY', woenzco.id, "", 23),
|
||||||
(22, "C'est de l'argent ?", 'CARD', None, credit, 'CLUB', troll.id, "", None),
|
(22, "C'est de l'argent ?", 'CARD', None, credit, 'CLUB', troll.id, "", None),
|
||||||
(37, "Je paye CASH", 'CASH', None, debit2, 'OTHER', None, "tous les étudiants <3", None),
|
(37, "Je paye CASH", 'CASH', None, debit2, 'OTHER', None, "tous les étudiants <3", None),
|
||||||
(300, "Paiement Guy", 'CASH', None, buying, 'USER', skia.id, "", None),
|
(300, "Paiement Guy", 'CASH', None, buying, 'USER', skia.id, "", None),
|
||||||
(32.3, "Essence", 'CASH', None, buying, 'OTHER', None, "station", None),
|
(32.3, "Essence", 'CASH', None, buying, 'OTHER', None, "station", None),
|
||||||
(46.42, "Allumette", 'CHECK', None, credit, 'CLUB', main_club.id, "", 57),
|
(46.42, "Allumette", 'CHECK', None, credit, 'CLUB', main_club.id, "", 57),
|
||||||
(666.42, "Subvention de far far away", 'CASH', None, comptes, 'CLUB', main_club.id, "", None),
|
(666.42, "Subvention de far far away", 'CASH', None, comptes, 'CLUB', main_club.id, "", None),
|
||||||
(496, "Ça, c'est un 6", 'CARD', simple, None, 'USER', skia.id, "", None),
|
(496, "Ça, c'est un 6", 'CARD', simple, None, 'USER', skia.id, "", None),
|
||||||
(17, "La Gargotte du Korrigan", 'CASH', None, debit2, 'CLUB', bar_club.id, "", None),
|
(17, "La Gargotte du Korrigan", 'CASH', None, debit2, 'CLUB', bar_club.id, "", None),
|
||||||
]
|
]
|
||||||
for op in operation_list:
|
for op in operation_list:
|
||||||
operation = Operation(journal=gj, date=date.today(), amount=op[0],
|
operation = Operation(journal=gj, date=date.today(), amount=op[0],
|
||||||
remark=op[1], mode=op[2], done=True, simpleaccounting_type=op[3],
|
remark=op[1], mode=op[2], done=True, simpleaccounting_type=op[3],
|
||||||
accounting_type=op[4], target_type=op[5], target_id=op[6],
|
accounting_type=op[4], target_type=op[5], target_id=op[6],
|
||||||
target_label=op[7], cheque_number=op[8])
|
target_label=op[7], cheque_number=op[8])
|
||||||
operation.clean()
|
operation.clean()
|
||||||
operation.save()
|
operation.save()
|
||||||
|
|
||||||
@ -428,14 +428,14 @@ Welcome to the wiki page!
|
|||||||
date_of_birth="1942-06-12")
|
date_of_birth="1942-06-12")
|
||||||
sli.set_password("plop")
|
sli.set_password("plop")
|
||||||
sli.save()
|
sli.save()
|
||||||
sli.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
sli.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||||
sli.save()
|
sli.save()
|
||||||
sli_profile_path = os.path.join(root_path, 'core/fixtures/images/5.jpg')
|
sli_profile_path = os.path.join(root_path, 'core/fixtures/images/5.jpg')
|
||||||
with open(sli_profile_path, 'rb') as f:
|
with open(sli_profile_path, 'rb') as f:
|
||||||
name = str(sli.id) + "_profile.jpg"
|
name = str(sli.id) + "_profile.jpg"
|
||||||
sli_profile = SithFile(parent=profiles_root, name=name,
|
sli_profile = SithFile(parent=profiles_root, name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, 'JPEG'),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, 'JPEG'),
|
||||||
owner=sli, is_folder=False, mime_type='image/jpeg', size=os.path.getsize(sli_profile_path))
|
owner=sli, is_folder=False, mime_type='image/jpeg', size=os.path.getsize(sli_profile_path))
|
||||||
sli_profile.file.name = name
|
sli_profile.file.name = name
|
||||||
sli_profile.save()
|
sli_profile.save()
|
||||||
sli.profile_pict = sli_profile
|
sli.profile_pict = sli_profile
|
||||||
@ -450,27 +450,27 @@ Welcome to the wiki page!
|
|||||||
with open(krophil_profile_path, 'rb') as f:
|
with open(krophil_profile_path, 'rb') as f:
|
||||||
name = str(krophil.id) + "_profile.jpg"
|
name = str(krophil.id) + "_profile.jpg"
|
||||||
krophil_profile = SithFile(parent=profiles_root, name=name,
|
krophil_profile = SithFile(parent=profiles_root, name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, 'JPEG'),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, 'JPEG'),
|
||||||
owner=krophil, is_folder=False, mime_type='image/jpeg', size=os.path.getsize(krophil_profile_path))
|
owner=krophil, is_folder=False, mime_type='image/jpeg', size=os.path.getsize(krophil_profile_path))
|
||||||
krophil_profile.file.name = name
|
krophil_profile.file.name = name
|
||||||
krophil_profile.save()
|
krophil_profile.save()
|
||||||
krophil.profile_pict = krophil_profile
|
krophil.profile_pict = krophil_profile
|
||||||
krophil.save()
|
krophil.save()
|
||||||
## Adding subscription for sli
|
# Adding subscription for sli
|
||||||
s = Subscription(member=User.objects.filter(pk=sli.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=sli.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
## Adding subscription for Krophil
|
# Adding subscription for Krophil
|
||||||
s = Subscription(member=User.objects.filter(pk=krophil.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
s = Subscription(member=User.objects.filter(pk=krophil.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||||
s.subscription_start = s.compute_start()
|
s.subscription_start = s.compute_start()
|
||||||
s.subscription_end = s.compute_end(
|
s.subscription_end = s.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||||
start=s.subscription_start)
|
start=s.subscription_start)
|
||||||
s.save()
|
s.save()
|
||||||
|
|
||||||
# Add barman to counter
|
# Add barman to counter
|
||||||
@ -483,8 +483,8 @@ Welcome to the wiki page!
|
|||||||
subscriber_group = Group.objects.get(name=settings.SITH_MAIN_MEMBERS_GROUP)
|
subscriber_group = Group.objects.get(name=settings.SITH_MAIN_MEMBERS_GROUP)
|
||||||
ae_board_group = Group.objects.get(name=settings.SITH_MAIN_BOARD_GROUP)
|
ae_board_group = Group.objects.get(name=settings.SITH_MAIN_BOARD_GROUP)
|
||||||
el = Election(title="Élection 2017", description="La roue tourne", start_candidature='1942-06-12 10:28:45+01',
|
el = Election(title="Élection 2017", description="La roue tourne", start_candidature='1942-06-12 10:28:45+01',
|
||||||
end_candidature='2042-06-12 10:28:45+01',start_date='1942-06-12 10:28:45+01',
|
end_candidature='2042-06-12 10:28:45+01', start_date='1942-06-12 10:28:45+01',
|
||||||
end_date='7942-06-12 10:28:45+01')
|
end_date='7942-06-12 10:28:45+01')
|
||||||
el.save()
|
el.save()
|
||||||
el.view_groups.add(public_group)
|
el.view_groups.add(public_group)
|
||||||
el.edit_groups.add(ae_board_group)
|
el.edit_groups.add(ae_board_group)
|
||||||
@ -519,4 +519,3 @@ Welcome to the wiki page!
|
|||||||
various.save()
|
various.save()
|
||||||
Forum(name="Promos", description="Réservé aux Promos", parent=various).save()
|
Forum(name="Promos", description="Réservé aux Promos", parent=various).save()
|
||||||
ForumTopic(forum=hall)
|
ForumTopic(forum=hall)
|
||||||
|
|
||||||
|
@ -23,9 +23,8 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@ -37,7 +36,7 @@ class Command(BaseCommand):
|
|||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||||
try:
|
try:
|
||||||
os.mkdir(os.path.join(root_path)+'/data')
|
os.mkdir(os.path.join(root_path) + '/data')
|
||||||
print("Data dir created")
|
print("Data dir created")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
repr(e)
|
repr(e)
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
from mistune import Renderer, InlineGrammar, InlineLexer, Markdown, escape, escape_link
|
from mistune import Renderer, InlineGrammar, InlineLexer, Markdown, escape, escape_link
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
|
||||||
class SithRenderer(Renderer):
|
class SithRenderer(Renderer):
|
||||||
@ -54,13 +54,16 @@ class SithRenderer(Renderer):
|
|||||||
src = original_src
|
src = original_src
|
||||||
else:
|
else:
|
||||||
width = m.group(1)
|
width = m.group(1)
|
||||||
if not width.endswith('%'): width += "px"
|
if not width.endswith('%'):
|
||||||
|
width += "px"
|
||||||
style = "width: %s; " % width
|
style = "width: %s; " % width
|
||||||
try:
|
try:
|
||||||
height = m.group(3)
|
height = m.group(3)
|
||||||
if not height.endswith('%'): height += "px"
|
if not height.endswith('%'):
|
||||||
|
height += "px"
|
||||||
style += "height: %s; " % height
|
style += "height: %s; " % height
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
params = None
|
params = None
|
||||||
src = original_src
|
src = original_src
|
||||||
@ -77,6 +80,7 @@ class SithRenderer(Renderer):
|
|||||||
return '%s />' % html
|
return '%s />' % html
|
||||||
return '%s>' % html
|
return '%s>' % html
|
||||||
|
|
||||||
|
|
||||||
class SithInlineGrammar(InlineGrammar):
|
class SithInlineGrammar(InlineGrammar):
|
||||||
double_emphasis = re.compile(
|
double_emphasis = re.compile(
|
||||||
r'^\*{2}([\s\S]+?)\*{2}(?!\*)' # **word**
|
r'^\*{2}([\s\S]+?)\*{2}(?!\*)' # **word**
|
||||||
@ -94,6 +98,7 @@ class SithInlineGrammar(InlineGrammar):
|
|||||||
r'^<sub>([\s\S]+?)</sub>' # <sub>text</sub>
|
r'^<sub>([\s\S]+?)</sub>' # <sub>text</sub>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SithInlineLexer(InlineLexer):
|
class SithInlineLexer(InlineLexer):
|
||||||
grammar_class = SithInlineGrammar
|
grammar_class = SithInlineGrammar
|
||||||
|
|
||||||
@ -159,15 +164,16 @@ class SithInlineLexer(InlineLexer):
|
|||||||
return self.renderer.emphasis(text)
|
return self.renderer.emphasis(text)
|
||||||
|
|
||||||
def _process_link(self, m, link, title=None):
|
def _process_link(self, m, link, title=None):
|
||||||
try: # Add page:// support for links
|
try: # Add page:// support for links
|
||||||
page = re.compile(
|
page = re.compile(
|
||||||
r'^page://(\S*)' # page://nom_de_ma_page
|
r'^page://(\S*)' # page://nom_de_ma_page
|
||||||
)
|
)
|
||||||
match = page.search(link)
|
match = page.search(link)
|
||||||
page = match.group(1) or ""
|
page = match.group(1) or ""
|
||||||
link = reverse('core:page', kwargs={'page_name': page})
|
link = reverse('core:page', kwargs={'page_name': page})
|
||||||
except: pass
|
except:
|
||||||
try: # Add file:// support for links
|
pass
|
||||||
|
try: # Add file:// support for links
|
||||||
file_link = re.compile(
|
file_link = re.compile(
|
||||||
r'^file://(\d*)/?(\S*)?' # file://4000/download
|
r'^file://(\d*)/?(\S*)?' # file://4000/download
|
||||||
)
|
)
|
||||||
@ -175,9 +181,11 @@ class SithInlineLexer(InlineLexer):
|
|||||||
id = match.group(1)
|
id = match.group(1)
|
||||||
suffix = match.group(2) or ""
|
suffix = match.group(2) or ""
|
||||||
link = reverse('core:file_detail', kwargs={'file_id': id}) + suffix
|
link = reverse('core:file_detail', kwargs={'file_id': id}) + suffix
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return super(SithInlineLexer, self)._process_link(m, link, title)
|
return super(SithInlineLexer, self)._process_link(m, link, title)
|
||||||
|
|
||||||
|
|
||||||
renderer = SithRenderer(escape=True)
|
renderer = SithRenderer(escape=True)
|
||||||
inline = SithInlineLexer(renderer)
|
inline = SithInlineLexer(renderer)
|
||||||
|
|
||||||
@ -222,4 +230,3 @@ Petit *test* _sur_ ^une^ **seule** ^ligne pour voir^
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
print(markdown(text))
|
print(markdown(text))
|
||||||
|
|
||||||
|
@ -52,4 +52,3 @@ class AuthenticationMiddleware(DjangoAuthenticationMiddleware):
|
|||||||
"'account.middleware.AuthenticationMiddleware'."
|
"'account.middleware.AuthenticationMiddleware'."
|
||||||
)
|
)
|
||||||
request.user = SimpleLazyObject(lambda: get_cached_user(request))
|
request.user = SimpleLazyObject(lambda: get_cached_user(request))
|
||||||
|
|
||||||
|
@ -44,14 +44,17 @@ from datetime import datetime, timedelta, date
|
|||||||
|
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
|
||||||
class RealGroupManager(AuthGroupManager):
|
class RealGroupManager(AuthGroupManager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(RealGroupManager, self).get_queryset().filter(is_meta=False)
|
return super(RealGroupManager, self).get_queryset().filter(is_meta=False)
|
||||||
|
|
||||||
|
|
||||||
class MetaGroupManager(AuthGroupManager):
|
class MetaGroupManager(AuthGroupManager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(MetaGroupManager, self).get_queryset().filter(is_meta=True)
|
return super(MetaGroupManager, self).get_queryset().filter(is_meta=True)
|
||||||
|
|
||||||
|
|
||||||
class Group(AuthGroup):
|
class Group(AuthGroup):
|
||||||
is_meta = models.BooleanField(
|
is_meta = models.BooleanField(
|
||||||
_('meta group status'),
|
_('meta group status'),
|
||||||
@ -69,8 +72,10 @@ class Group(AuthGroup):
|
|||||||
"""
|
"""
|
||||||
return reverse('core:group_list')
|
return reverse('core:group_list')
|
||||||
|
|
||||||
|
|
||||||
class MetaGroup(Group):
|
class MetaGroup(Group):
|
||||||
objects = MetaGroupManager()
|
objects = MetaGroupManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
@ -78,20 +83,24 @@ class MetaGroup(Group):
|
|||||||
super(MetaGroup, self).__init__(*args, **kwargs)
|
super(MetaGroup, self).__init__(*args, **kwargs)
|
||||||
self.is_meta = True
|
self.is_meta = True
|
||||||
|
|
||||||
|
|
||||||
class RealGroup(Group):
|
class RealGroup(Group):
|
||||||
objects = RealGroupManager()
|
objects = RealGroupManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
|
|
||||||
def validate_promo(value):
|
def validate_promo(value):
|
||||||
start_year = settings.SITH_SCHOOL_START_YEAR
|
start_year = settings.SITH_SCHOOL_START_YEAR
|
||||||
delta = (date.today()+timedelta(days=180)).year - start_year
|
delta = (date.today() + timedelta(days=180)).year - start_year
|
||||||
if value < 0 or delta < value:
|
if value < 0 or delta < value:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_('%(value)s is not a valid promo (between 0 and %(end)s)'),
|
_('%(value)s is not a valid promo (between 0 and %(end)s)'),
|
||||||
params={'value': value, 'end': delta},
|
params={'value': value, 'end': delta},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractBaseUser):
|
class User(AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
Defines the base user class, useable in every app
|
Defines the base user class, useable in every app
|
||||||
@ -148,13 +157,13 @@ class User(AbstractBaseUser):
|
|||||||
)
|
)
|
||||||
groups = models.ManyToManyField(RealGroup, related_name='users', blank=True)
|
groups = models.ManyToManyField(RealGroup, related_name='users', blank=True)
|
||||||
home = models.OneToOneField('SithFile', related_name='home_of', verbose_name=_("home"), null=True, blank=True,
|
home = models.OneToOneField('SithFile', related_name='home_of', verbose_name=_("home"), null=True, blank=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
profile_pict = models.OneToOneField('SithFile', related_name='profile_of', verbose_name=_("profile"), null=True,
|
profile_pict = models.OneToOneField('SithFile', related_name='profile_of', verbose_name=_("profile"), null=True,
|
||||||
blank=True, on_delete=models.SET_NULL)
|
blank=True, on_delete=models.SET_NULL)
|
||||||
avatar_pict = models.OneToOneField('SithFile', related_name='avatar_of', verbose_name=_("avatar"), null=True,
|
avatar_pict = models.OneToOneField('SithFile', related_name='avatar_of', verbose_name=_("avatar"), null=True,
|
||||||
blank=True, on_delete=models.SET_NULL)
|
blank=True, on_delete=models.SET_NULL)
|
||||||
scrub_pict = models.OneToOneField('SithFile', related_name='scrub_of', verbose_name=_("scrub"), null=True,
|
scrub_pict = models.OneToOneField('SithFile', related_name='scrub_of', verbose_name=_("scrub"), null=True,
|
||||||
blank=True, on_delete=models.SET_NULL)
|
blank=True, on_delete=models.SET_NULL)
|
||||||
sex = models.CharField(_("sex"), max_length=10, choices=[("MAN", _("Man")), ("WOMAN", _("Woman"))], default="MAN")
|
sex = models.CharField(_("sex"), max_length=10, choices=[("MAN", _("Man")), ("WOMAN", _("Woman"))], default="MAN")
|
||||||
tshirt_size = models.CharField(_("tshirt size"), max_length=5, choices=[
|
tshirt_size = models.CharField(_("tshirt size"), max_length=5, choices=[
|
||||||
("-", _("-")),
|
("-", _("-")),
|
||||||
@ -165,7 +174,7 @@ class User(AbstractBaseUser):
|
|||||||
("XL", _("XL")),
|
("XL", _("XL")),
|
||||||
("XXL", _("XXL")),
|
("XXL", _("XXL")),
|
||||||
("XXXL", _("XXXL")),
|
("XXXL", _("XXXL")),
|
||||||
], default="-")
|
], default="-")
|
||||||
role = models.CharField(_("role"), max_length=15, choices=[
|
role = models.CharField(_("role"), max_length=15, choices=[
|
||||||
("STUDENT", _("Student")),
|
("STUDENT", _("Student")),
|
||||||
("ADMINISTRATIVE", _("Administrative agent")),
|
("ADMINISTRATIVE", _("Administrative agent")),
|
||||||
@ -174,9 +183,9 @@ class User(AbstractBaseUser):
|
|||||||
("DOCTOR", _("Doctor")),
|
("DOCTOR", _("Doctor")),
|
||||||
("FORMER STUDENT", _("Former student")),
|
("FORMER STUDENT", _("Former student")),
|
||||||
("SERVICE", _("Service")),
|
("SERVICE", _("Service")),
|
||||||
], blank=True, default="")
|
], blank=True, default="")
|
||||||
department = models.CharField(_("department"), max_length=15, choices=settings.SITH_PROFILE_DEPARTMENTS,
|
department = models.CharField(_("department"), max_length=15, choices=settings.SITH_PROFILE_DEPARTMENTS,
|
||||||
default="NA", blank=True)
|
default="NA", blank=True)
|
||||||
dpt_option = models.CharField(_("dpt option"), max_length=32, blank=True, default="")
|
dpt_option = models.CharField(_("dpt option"), max_length=32, blank=True, default="")
|
||||||
semester = models.CharField(_("semester"), max_length=5, blank=True, default="")
|
semester = models.CharField(_("semester"), max_length=5, blank=True, default="")
|
||||||
quote = models.CharField(_("quote"), max_length=256, blank=True, default="")
|
quote = models.CharField(_("quote"), max_length=256, blank=True, default="")
|
||||||
@ -226,11 +235,12 @@ class User(AbstractBaseUser):
|
|||||||
_club_memberships = {}
|
_club_memberships = {}
|
||||||
_group_names = {}
|
_group_names = {}
|
||||||
_group_ids = {}
|
_group_ids = {}
|
||||||
|
|
||||||
def is_in_group(self, group_name):
|
def is_in_group(self, group_name):
|
||||||
"""If the user is in the group passed in argument (as string or by id)"""
|
"""If the user is in the group passed in argument (as string or by id)"""
|
||||||
group_id = 0
|
group_id = 0
|
||||||
g = None
|
g = None
|
||||||
if isinstance(group_name, int): # Handle the case where group_name is an ID
|
if isinstance(group_name, int): # Handle the case where group_name is an ID
|
||||||
if group_name in User._group_ids.keys():
|
if group_name in User._group_ids.keys():
|
||||||
g = User._group_ids[group_name]
|
g = User._group_ids[group_name]
|
||||||
else:
|
else:
|
||||||
@ -253,7 +263,7 @@ class User(AbstractBaseUser):
|
|||||||
return self.is_subscribed
|
return self.is_subscribed
|
||||||
if group_id == settings.SITH_GROUP_OLD_SUBSCRIBERS_ID:
|
if group_id == settings.SITH_GROUP_OLD_SUBSCRIBERS_ID:
|
||||||
return self.was_subscribed
|
return self.was_subscribed
|
||||||
if group_name == settings.SITH_MAIN_MEMBERS_GROUP: # We check the subscription if asked
|
if group_name == settings.SITH_MAIN_MEMBERS_GROUP: # We check the subscription if asked
|
||||||
return self.is_subscribed
|
return self.is_subscribed
|
||||||
if group_name[-len(settings.SITH_BOARD_SUFFIX):] == settings.SITH_BOARD_SUFFIX:
|
if group_name[-len(settings.SITH_BOARD_SUFFIX):] == settings.SITH_BOARD_SUFFIX:
|
||||||
name = group_name[:-len(settings.SITH_BOARD_SUFFIX)]
|
name = group_name[:-len(settings.SITH_BOARD_SUFFIX)]
|
||||||
@ -315,7 +325,7 @@ class User(AbstractBaseUser):
|
|||||||
else:
|
else:
|
||||||
create = True
|
create = True
|
||||||
super(User, self).save(*args, **kwargs)
|
super(User, self).save(*args, **kwargs)
|
||||||
if create and settings.IS_OLD_MYSQL_PRESENT: # Create user on the old site: TODO remove me!
|
if create and settings.IS_OLD_MYSQL_PRESENT: # Create user on the old site: TODO remove me!
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
try:
|
try:
|
||||||
db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
|
db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
|
||||||
@ -324,9 +334,9 @@ class User(AbstractBaseUser):
|
|||||||
(%s, %s, %s, %s, %s, %s)""", (self.id, self.last_name, self.first_name, self.email, "valid", "0"))
|
(%s, %s, %s, %s, %s, %s)""", (self.id, self.last_name, self.first_name, self.email, "valid", "0"))
|
||||||
db.commit()
|
db.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
with open(settings.BASE_DIR+"/user_fail.log", "a") as f:
|
with open(settings.BASE_DIR + "/user_fail.log", "a") as f:
|
||||||
print("FAIL to add user %s (%s %s - %s) to old site" % (self.id, self.first_name, self.last_name,
|
print("FAIL to add user %s (%s %s - %s) to old site" % (self.id, self.first_name, self.last_name,
|
||||||
self.email), file=f)
|
self.email), file=f)
|
||||||
print("Reason: %s" % (repr(e)), file=f)
|
print("Reason: %s" % (repr(e)), file=f)
|
||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
@ -401,13 +411,13 @@ class User(AbstractBaseUser):
|
|||||||
Returns the generated username
|
Returns the generated username
|
||||||
"""
|
"""
|
||||||
def remove_accents(data):
|
def remove_accents(data):
|
||||||
return ''.join(x for x in unicodedata.normalize('NFKD', data) if \
|
return ''.join(x for x in unicodedata.normalize('NFKD', data) if
|
||||||
unicodedata.category(x)[0] == 'L').lower()
|
unicodedata.category(x)[0] == 'L').lower()
|
||||||
user_name = remove_accents(self.first_name[0]+self.last_name).encode('ascii', 'ignore').decode('utf-8')
|
user_name = remove_accents(self.first_name[0] + self.last_name).encode('ascii', 'ignore').decode('utf-8')
|
||||||
un_set = [u.username for u in User.objects.all()]
|
un_set = [u.username for u in User.objects.all()]
|
||||||
if user_name in un_set:
|
if user_name in un_set:
|
||||||
i = 1
|
i = 1
|
||||||
while user_name+str(i) in un_set:
|
while user_name + str(i) in un_set:
|
||||||
i += 1
|
i += 1
|
||||||
user_name += str(i)
|
user_name += str(i)
|
||||||
self.username = user_name
|
self.username = user_name
|
||||||
@ -473,7 +483,7 @@ class User(AbstractBaseUser):
|
|||||||
self.profile_pict.get_download_url() if self.profile_pict else staticfiles_storage.url("core/img/unknown.jpg"),
|
self.profile_pict.get_download_url() if self.profile_pict else staticfiles_storage.url("core/img/unknown.jpg"),
|
||||||
_("Profile"),
|
_("Profile"),
|
||||||
escape(self.get_display_name()),
|
escape(self.get_display_name()),
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def subscribed(self):
|
def subscribed(self):
|
||||||
@ -489,6 +499,7 @@ class User(AbstractBaseUser):
|
|||||||
infos.save()
|
infos.save()
|
||||||
return infos
|
return infos
|
||||||
|
|
||||||
|
|
||||||
class AnonymousUser(AuthAnonymousUser):
|
class AnonymousUser(AuthAnonymousUser):
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
super(AnonymousUser, self).__init__()
|
super(AnonymousUser, self).__init__()
|
||||||
@ -530,7 +541,7 @@ class AnonymousUser(AuthAnonymousUser):
|
|||||||
The anonymous user is only the public group
|
The anonymous user is only the public group
|
||||||
"""
|
"""
|
||||||
group_id = 0
|
group_id = 0
|
||||||
if isinstance(group_name, int): # Handle the case where group_name is an ID
|
if isinstance(group_name, int): # Handle the case where group_name is an ID
|
||||||
g = Group.objects.filter(id=group_name).first()
|
g = Group.objects.filter(id=group_name).first()
|
||||||
if g:
|
if g:
|
||||||
group_name = g.name
|
group_name = g.name
|
||||||
@ -557,6 +568,7 @@ class AnonymousUser(AuthAnonymousUser):
|
|||||||
def get_display_name(self):
|
def get_display_name(self):
|
||||||
return _("Visitor")
|
return _("Visitor")
|
||||||
|
|
||||||
|
|
||||||
class Preferences(models.Model):
|
class Preferences(models.Model):
|
||||||
user = models.OneToOneField(User, related_name="preferences")
|
user = models.OneToOneField(User, related_name="preferences")
|
||||||
receive_weekmail = models.BooleanField(
|
receive_weekmail = models.BooleanField(
|
||||||
@ -576,15 +588,19 @@ class Preferences(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return self.user.get_absolute_url()
|
return self.user.get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
def get_directory(instance, filename):
|
def get_directory(instance, filename):
|
||||||
return '.{0}/{1}'.format(instance.get_parent_path(), filename)
|
return '.{0}/{1}'.format(instance.get_parent_path(), filename)
|
||||||
|
|
||||||
|
|
||||||
def get_compressed_directory(instance, filename):
|
def get_compressed_directory(instance, filename):
|
||||||
return '.{0}/compressed/{1}'.format(instance.get_parent_path(), filename)
|
return '.{0}/compressed/{1}'.format(instance.get_parent_path(), filename)
|
||||||
|
|
||||||
|
|
||||||
def get_thumbnail_directory(instance, filename):
|
def get_thumbnail_directory(instance, filename):
|
||||||
return '.{0}/thumbnail/{1}'.format(instance.get_parent_path(), filename)
|
return '.{0}/thumbnail/{1}'.format(instance.get_parent_path(), filename)
|
||||||
|
|
||||||
|
|
||||||
class SithFile(models.Model):
|
class SithFile(models.Model):
|
||||||
name = models.CharField(_('file name'), max_length=256, blank=False)
|
name = models.CharField(_('file name'), max_length=256, blank=False)
|
||||||
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True)
|
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True)
|
||||||
@ -601,7 +617,7 @@ class SithFile(models.Model):
|
|||||||
is_moderated = models.BooleanField(_("is moderated"), default=False)
|
is_moderated = models.BooleanField(_("is moderated"), default=False)
|
||||||
moderator = models.ForeignKey(User, related_name="moderated_files", verbose_name=_("owner"), null=True, blank=True)
|
moderator = models.ForeignKey(User, related_name="moderated_files", verbose_name=_("owner"), null=True, blank=True)
|
||||||
asked_for_removal = models.BooleanField(_("asked for removal"), default=False)
|
asked_for_removal = models.BooleanField(_("asked for removal"), default=False)
|
||||||
is_in_sas = models.BooleanField(_("is in the SAS"), default=False) # Allows to query this flag, updated at each call to save()
|
is_in_sas = models.BooleanField(_("is in the SAS"), default=False) # Allows to query this flag, updated at each call to save()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("file")
|
verbose_name = _("file")
|
||||||
@ -763,18 +779,22 @@ class SithFile(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.get_parent_path() + "/" + self.name
|
return self.get_parent_path() + "/" + self.name
|
||||||
|
|
||||||
|
|
||||||
class LockError(Exception):
|
class LockError(Exception):
|
||||||
"""There was a lock error on the object"""
|
"""There was a lock error on the object"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AlreadyLocked(LockError):
|
class AlreadyLocked(LockError):
|
||||||
"""The object is already locked"""
|
"""The object is already locked"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NotLocked(LockError):
|
class NotLocked(LockError):
|
||||||
"""The object is not locked"""
|
"""The object is not locked"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Page(models.Model):
|
class Page(models.Model):
|
||||||
"""
|
"""
|
||||||
The page class to build a Wiki
|
The page class to build a Wiki
|
||||||
@ -787,14 +807,13 @@ class Page(models.Model):
|
|||||||
query, but don't rely on it when playing with a Page object, use get_full_name() instead!
|
query, but don't rely on it when playing with a Page object, use get_full_name() instead!
|
||||||
"""
|
"""
|
||||||
name = models.CharField(_('page unix name'), max_length=30,
|
name = models.CharField(_('page unix name'), max_length=30,
|
||||||
validators=[
|
validators=[
|
||||||
validators.RegexValidator(
|
validators.RegexValidator(
|
||||||
r'^[A-z.+-]+$',
|
r'^[A-z.+-]+$',
|
||||||
_('Enter a valid page name. This value may contain only '
|
_('Enter a valid page name. This value may contain only '
|
||||||
'unaccented letters, numbers ' 'and ./+/-/_ characters.')
|
'unaccented letters, numbers ' 'and ./+/-/_ characters.')
|
||||||
),
|
), ],
|
||||||
],
|
blank=False)
|
||||||
blank=False)
|
|
||||||
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True, on_delete=models.SET_NULL)
|
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
# Attention: this field may not be valid until you call save(). It's made for fast query, but don't rely on it when
|
# Attention: this field may not be valid until you call save(). It's made for fast query, but don't rely on it when
|
||||||
# playing with a Page object, use get_full_name() instead!
|
# playing with a Page object, use get_full_name() instead!
|
||||||
@ -854,12 +873,13 @@ class Page(models.Model):
|
|||||||
Performs some needed actions before and after saving a page in database
|
Performs some needed actions before and after saving a page in database
|
||||||
"""
|
"""
|
||||||
locked = kwargs.pop('force_lock', False)
|
locked = kwargs.pop('force_lock', False)
|
||||||
if not locked: locked = self.is_locked()
|
if not locked:
|
||||||
|
locked = self.is_locked()
|
||||||
if not locked:
|
if not locked:
|
||||||
raise NotLocked("The page is not locked and thus can not be saved")
|
raise NotLocked("The page is not locked and thus can not be saved")
|
||||||
self.full_clean()
|
self.full_clean()
|
||||||
if not self.id:
|
if not self.id:
|
||||||
super(Page, self).save(*args, **kwargs) # Save a first time to correctly set _full_name
|
super(Page, self).save(*args, **kwargs) # Save a first time to correctly set _full_name
|
||||||
# This reset the _full_name just before saving to maintain a coherent field quicker for queries than the
|
# This reset the _full_name just before saving to maintain a coherent field quicker for queries than the
|
||||||
# recursive method
|
# recursive method
|
||||||
# It also update all the children to maintain correct names
|
# It also update all the children to maintain correct names
|
||||||
@ -973,7 +993,7 @@ class PageRev(models.Model):
|
|||||||
page = models.ForeignKey(Page, related_name='revisions')
|
page = models.ForeignKey(Page, related_name='revisions')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['date',]
|
ordering = ['date', ]
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""
|
"""
|
||||||
@ -1003,6 +1023,7 @@ class PageRev(models.Model):
|
|||||||
# Don't forget to unlock, otherwise, people will have to wait for the page's timeout
|
# Don't forget to unlock, otherwise, people will have to wait for the page's timeout
|
||||||
self.page.unset_lock()
|
self.page.unset_lock()
|
||||||
|
|
||||||
|
|
||||||
class Notification(models.Model):
|
class Notification(models.Model):
|
||||||
user = models.ForeignKey(User, related_name='notifications')
|
user = models.ForeignKey(User, related_name='notifications')
|
||||||
url = models.CharField(_("url"), max_length=255)
|
url = models.CharField(_("url"), max_length=255)
|
||||||
@ -1015,4 +1036,3 @@ class Notification(models.Model):
|
|||||||
if self.param:
|
if self.param:
|
||||||
return self.get_type_display() % self.param
|
return self.get_type_display() % self.param
|
||||||
return self.get_type_display()
|
return self.get_type_display()
|
||||||
|
|
||||||
|
@ -27,30 +27,32 @@ from django import template
|
|||||||
from django.template.defaultfilters import stringfilter
|
from django.template.defaultfilters import stringfilter
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from core.scss.processor import ScssProcessor
|
from core.scss.processor import ScssProcessor
|
||||||
from django.utils.html import escape
|
|
||||||
|
|
||||||
from core.markdown import markdown as md
|
from core.markdown import markdown as md
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(is_safe=False)
|
@register.filter(is_safe=False)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def markdown(text):
|
def markdown(text):
|
||||||
return mark_safe("<div class=\"markdown\">%s</div>" % md(text))
|
return mark_safe("<div class=\"markdown\">%s</div>" % md(text))
|
||||||
|
|
||||||
|
|
||||||
@register.filter()
|
@register.filter()
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def datetime_format_python_to_PHP(python_format_string):
|
def datetime_format_python_to_PHP(python_format_string):
|
||||||
"""
|
"""
|
||||||
Given a python datetime format string, attempts to convert it to the nearest PHP datetime format string possible.
|
Given a python datetime format string, attempts to convert it to the nearest PHP datetime format string possible.
|
||||||
"""
|
"""
|
||||||
python2PHP = {"%a": "D", "%a": "D", "%A": "l", "%b": "M", "%B": "F", "%c": "", "%d": "d", "%H": "H", "%I": "h", "%j": "z", "%m": "m", "%M": "i", "%p": "A", "%S": "s", "%U": "", "%w": "w", "%W": "W", "%x": "", "%X": "", "%y": "y", "%Y": "Y", "%Z": "e" }
|
python2PHP = {"%a": "D", "%a": "D", "%A": "l", "%b": "M", "%B": "F", "%c": "", "%d": "d", "%H": "H", "%I": "h", "%j": "z", "%m": "m", "%M": "i", "%p": "A", "%S": "s", "%U": "", "%w": "w", "%W": "W", "%x": "", "%X": "", "%y": "y", "%Y": "Y", "%Z": "e"}
|
||||||
|
|
||||||
php_format_string = python_format_string
|
php_format_string = python_format_string
|
||||||
for py, php in python2PHP.items():
|
for py, php in python2PHP.items():
|
||||||
php_format_string = php_format_string.replace(py, php)
|
php_format_string = php_format_string.replace(py, php)
|
||||||
return php_format_string
|
return php_format_string
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
def scss(path):
|
def scss(path):
|
||||||
"""
|
"""
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
|
||||||
from core.models import User, Group, Page
|
from core.models import User, Group, Page
|
||||||
@ -34,6 +33,7 @@ to run these tests :
|
|||||||
python3 manage.py test
|
python3 manage.py test
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class UserRegistrationTest(TestCase):
|
class UserRegistrationTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
try:
|
try:
|
||||||
@ -52,7 +52,7 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('TEST_REGISTER_USER_FORM_OK' in str(response.content))
|
self.assertTrue('TEST_REGISTER_USER_FORM_OK' in str(response.content))
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop2',
|
'password2': 'plop2',
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '',
|
'date_of_birth': '',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
||||||
|
|
||||||
@ -142,14 +142,14 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
response = c.post(reverse('core:register'), {'first_name': 'Bibou',
|
response = c.post(reverse('core:register'), {'first_name': 'Bibou',
|
||||||
'last_name': 'Carlier',
|
'last_name': 'Carlier',
|
||||||
'email': 'bibou@git.an',
|
'email': 'bibou@git.an',
|
||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
self.assertTrue('TEST_REGISTER_USER_FORM_FAIL' in str(response.content))
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
response = c.post(reverse('core:login'), {'username': 'gcarlier', 'password': 'plop'})
|
response = c.post(reverse('core:login'), {'username': 'gcarlier', 'password': 'plop'})
|
||||||
self.assertTrue(response.status_code == 302)
|
self.assertTrue(response.status_code == 302)
|
||||||
#self.assertTrue('Hello, world' in str(response.content))
|
#self.assertTrue('Hello, world' in str(response.content))
|
||||||
@ -180,11 +180,12 @@ class UserRegistrationTest(TestCase):
|
|||||||
'date_of_birth': '12/6/1942',
|
'date_of_birth': '12/6/1942',
|
||||||
'password1': 'plop',
|
'password1': 'plop',
|
||||||
'password2': 'plop',
|
'password2': 'plop',
|
||||||
})
|
})
|
||||||
response = c.post(reverse('core:login'), {'username': 'gcarlier', 'password': 'guy'})
|
response = c.post(reverse('core:login'), {'username': 'gcarlier', 'password': 'guy'})
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue("""<p>Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.</p>""" in str(response.content))
|
self.assertTrue("""<p>Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.</p>""" in str(response.content))
|
||||||
|
|
||||||
|
|
||||||
class PageHandlingTest(TestCase):
|
class PageHandlingTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
try:
|
try:
|
||||||
@ -207,7 +208,7 @@ class PageHandlingTest(TestCase):
|
|||||||
'parent': '',
|
'parent': '',
|
||||||
'name': 'guy',
|
'name': 'guy',
|
||||||
'owner_group': 1,
|
'owner_group': 1,
|
||||||
})
|
})
|
||||||
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy'}))
|
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy'}))
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('<a href="/page/guy/hist">' in str(response.content))
|
self.assertTrue('<a href="/page/guy/hist">' in str(response.content))
|
||||||
@ -220,12 +221,12 @@ class PageHandlingTest(TestCase):
|
|||||||
'parent': '',
|
'parent': '',
|
||||||
'name': 'guy',
|
'name': 'guy',
|
||||||
'owner_group': '1',
|
'owner_group': '1',
|
||||||
})
|
})
|
||||||
response = self.client.post(reverse('core:page_new'), {
|
response = self.client.post(reverse('core:page_new'), {
|
||||||
'parent': '1',
|
'parent': '1',
|
||||||
'name': 'bibou',
|
'name': 'bibou',
|
||||||
'owner_group': '1',
|
'owner_group': '1',
|
||||||
})
|
})
|
||||||
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy/bibou'}))
|
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy/bibou'}))
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('<a href="/page/guy/bibou/">' in str(response.content))
|
self.assertTrue('<a href="/page/guy/bibou/">' in str(response.content))
|
||||||
@ -259,11 +260,11 @@ class PageHandlingTest(TestCase):
|
|||||||
'parent': '',
|
'parent': '',
|
||||||
'name': 'guy',
|
'name': 'guy',
|
||||||
'owner_group': '1',
|
'owner_group': '1',
|
||||||
})
|
})
|
||||||
r = self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
|
self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
|
||||||
'title': 'Bibou',
|
'title': 'Bibou',
|
||||||
'content':
|
'content':
|
||||||
'''Guy *bibou*
|
'''Guy *bibou*
|
||||||
|
|
||||||
http://git.an
|
http://git.an
|
||||||
|
|
||||||
@ -273,18 +274,19 @@ http://git.an
|
|||||||
|
|
||||||
<script>alert('Guy');</script>
|
<script>alert('Guy');</script>
|
||||||
''',
|
''',
|
||||||
})
|
})
|
||||||
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy'}))
|
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy'}))
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue('<p>Guy <em>bibou</em></p>\\n<p><a href="http://git.an">http://git.an</a></p>\\n' +
|
self.assertTrue('<p>Guy <em>bibou</em></p>\\n<p><a href="http://git.an">http://git.an</a></p>\\n' +
|
||||||
'<h1>Swag</h1>\\n<guy>Bibou</guy>' +
|
'<h1>Swag</h1>\\n<guy>Bibou</guy>' +
|
||||||
"<script>alert(\\'Guy\\');</script>" in str(response.content))
|
"<script>alert(\\'Guy\\');</script>" in str(response.content))
|
||||||
|
|
||||||
#TODO: many tests on the pages:
|
# TODO: many tests on the pages:
|
||||||
# - renaming a page
|
# - renaming a page
|
||||||
# - changing a page's parent --> check that page's children's full_name
|
# - changing a page's parent --> check that page's children's full_name
|
||||||
# - changing the different groups of the page
|
# - changing the different groups of the page
|
||||||
|
|
||||||
|
|
||||||
class FileHandlingTest(TestCase):
|
class FileHandlingTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
try:
|
try:
|
||||||
@ -295,19 +297,18 @@ class FileHandlingTest(TestCase):
|
|||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def test_create_folder_home(self):
|
def test_create_folder_home(self):
|
||||||
response = self.client.post(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}),
|
response = self.client.post(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
|
||||||
{"folder_name": "GUY_folder_test"})
|
{"folder_name": "GUY_folder_test"})
|
||||||
self.assertTrue(response.status_code == 302)
|
self.assertTrue(response.status_code == 302)
|
||||||
response = self.client.get(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}))
|
response = self.client.get(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}))
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue("GUY_folder_test</a>" in str(response.content))
|
self.assertTrue("GUY_folder_test</a>" in str(response.content))
|
||||||
|
|
||||||
def test_upload_file_home(self):
|
def test_upload_file_home(self):
|
||||||
with open("/bin/ls", "rb") as f:
|
with open("/bin/ls", "rb") as f:
|
||||||
response = self.client.post(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}),
|
response = self.client.post(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
|
||||||
{"file_field": f})
|
{"file_field": f})
|
||||||
self.assertTrue(response.status_code == 302)
|
self.assertTrue(response.status_code == 302)
|
||||||
response = self.client.get(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}))
|
response = self.client.get(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}))
|
||||||
self.assertTrue(response.status_code == 200)
|
self.assertTrue(response.status_code == 200)
|
||||||
self.assertTrue("ls</a>" in str(response.content))
|
self.assertTrue("ls</a>" in str(response.content))
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from core.views import *
|
from core.views import *
|
||||||
|
|
||||||
|
117
core/utils.py
117
core/utils.py
@ -27,9 +27,10 @@ import re
|
|||||||
# Image utils
|
# Image utils
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from datetime import datetime, timezone, date
|
from datetime import date
|
||||||
|
|
||||||
from PIL import Image, ExifTags
|
from PIL import ExifTags
|
||||||
|
# from exceptions import IOError
|
||||||
import PIL
|
import PIL
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -49,16 +50,17 @@ def get_start_of_semester(d=date.today()):
|
|||||||
today = d
|
today = d
|
||||||
year = today.year
|
year = today.year
|
||||||
start = date(year, settings.SITH_START_DATE[0], settings.SITH_START_DATE[1])
|
start = date(year, settings.SITH_START_DATE[0], settings.SITH_START_DATE[1])
|
||||||
start2 = start.replace(month=(start.month+6)%12)
|
start2 = start.replace(month=(start.month + 6) % 12)
|
||||||
if start > start2:
|
if start > start2:
|
||||||
start, start2 = start2, start
|
start, start2 = start2, start
|
||||||
if today < start:
|
if today < start:
|
||||||
return start2.replace(year=year-1)
|
return start2.replace(year=year - 1)
|
||||||
elif today < start2:
|
elif today < start2:
|
||||||
return start
|
return start
|
||||||
else:
|
else:
|
||||||
return start2
|
return start2
|
||||||
|
|
||||||
|
|
||||||
def get_semester(d=date.today()):
|
def get_semester(d=date.today()):
|
||||||
start = get_start_of_semester(d)
|
start = get_start_of_semester(d)
|
||||||
if start.month <= 6:
|
if start.month <= 6:
|
||||||
@ -66,6 +68,7 @@ def get_semester(d=date.today()):
|
|||||||
else:
|
else:
|
||||||
return "A" + str(start.year)[-2:]
|
return "A" + str(start.year)[-2:]
|
||||||
|
|
||||||
|
|
||||||
def scale_dimension(width, height, long_edge):
|
def scale_dimension(width, height, long_edge):
|
||||||
if width > height:
|
if width > height:
|
||||||
ratio = long_edge * 1. / width
|
ratio = long_edge * 1. / width
|
||||||
@ -73,6 +76,7 @@ def scale_dimension(width, height, long_edge):
|
|||||||
ratio = long_edge * 1. / height
|
ratio = long_edge * 1. / height
|
||||||
return int(width * ratio), int(height * ratio)
|
return int(width * ratio), int(height * ratio)
|
||||||
|
|
||||||
|
|
||||||
def resize_image(im, edge, format):
|
def resize_image(im, edge, format):
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
(width, height) = scale_dimension(w, h, long_edge=edge)
|
(width, height) = scale_dimension(w, h, long_edge=edge)
|
||||||
@ -85,28 +89,31 @@ def resize_image(im, edge, format):
|
|||||||
im.save(fp=content, format=format.upper(), quality=90, optimize=True, progressive=True)
|
im.save(fp=content, format=format.upper(), quality=90, optimize=True, progressive=True)
|
||||||
return ContentFile(content.getvalue())
|
return ContentFile(content.getvalue())
|
||||||
|
|
||||||
def exif_auto_rotate(image):
|
|
||||||
for orientation in ExifTags.TAGS.keys() :
|
|
||||||
if ExifTags.TAGS[orientation]=='Orientation' : break
|
|
||||||
exif=dict(image._getexif().items())
|
|
||||||
|
|
||||||
if exif[orientation] == 3 :
|
def exif_auto_rotate(image):
|
||||||
image=image.rotate(180, expand=True)
|
for orientation in ExifTags.TAGS.keys():
|
||||||
elif exif[orientation] == 6 :
|
if ExifTags.TAGS[orientation] == 'Orientation':
|
||||||
image=image.rotate(270, expand=True)
|
break
|
||||||
elif exif[orientation] == 8 :
|
exif = dict(image._getexif().items())
|
||||||
image=image.rotate(90, expand=True)
|
|
||||||
|
if exif[orientation] == 3:
|
||||||
|
image = image.rotate(180, expand=True)
|
||||||
|
elif exif[orientation] == 6:
|
||||||
|
image = image.rotate(270, expand=True)
|
||||||
|
elif exif[orientation] == 8:
|
||||||
|
image = image.rotate(90, expand=True)
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
def doku_to_markdown(text):
|
def doku_to_markdown(text):
|
||||||
"""This is a quite correct doku translator"""
|
"""This is a quite correct doku translator"""
|
||||||
text = re.sub(r'([^:]|^)\/\/(.*?)\/\/', r'*\2*', text) # Italic (prevents protocol:// conflict)
|
text = re.sub(r'([^:]|^)\/\/(.*?)\/\/', r'*\2*', text) # Italic (prevents protocol:// conflict)
|
||||||
text = re.sub(r'<del>(.*?)<\/del>', r'~~\1~~', text, flags=re.DOTALL) # Strike (may be multiline)
|
text = re.sub(r'<del>(.*?)<\/del>', r'~~\1~~', text, flags=re.DOTALL) # Strike (may be multiline)
|
||||||
text = re.sub(r'<sup>(.*?)<\/sup>', r'^\1^', text) # Superscript (multiline not supported, because almost never used)
|
text = re.sub(r'<sup>(.*?)<\/sup>', r'^\1^', text) # Superscript (multiline not supported, because almost never used)
|
||||||
text = re.sub(r'<sub>(.*?)<\/sub>', r'_\1_', text) # Subscript (idem)
|
text = re.sub(r'<sub>(.*?)<\/sub>', r'_\1_', text) # Subscript (idem)
|
||||||
|
|
||||||
text = re.sub(r'^======(.*?)======', r'#\1', text, flags=re.MULTILINE) # Titles
|
text = re.sub(r'^======(.*?)======', r'#\1', text, flags=re.MULTILINE) # Titles
|
||||||
text = re.sub(r'^=====(.*?)=====', r'##\1', text, flags=re.MULTILINE)
|
text = re.sub(r'^=====(.*?)=====', r'##\1', text, flags=re.MULTILINE)
|
||||||
text = re.sub(r'^====(.*?)====', r'###\1', text, flags=re.MULTILINE)
|
text = re.sub(r'^====(.*?)====', r'###\1', text, flags=re.MULTILINE)
|
||||||
text = re.sub(r'^===(.*?)===', r'####\1', text, flags=re.MULTILINE)
|
text = re.sub(r'^===(.*?)===', r'####\1', text, flags=re.MULTILINE)
|
||||||
@ -121,93 +128,95 @@ def doku_to_markdown(text):
|
|||||||
text = re.sub(r'dfile://', r'file://', text)
|
text = re.sub(r'dfile://', r'file://', text)
|
||||||
|
|
||||||
i = 1
|
i = 1
|
||||||
for fn in re.findall(r'\(\((.*?)\)\)', text): # Footnotes
|
for fn in re.findall(r'\(\((.*?)\)\)', text): # Footnotes
|
||||||
text = re.sub(r'\(\((.*?)\)\)', r'[^%s]' % i, text, count=1)
|
text = re.sub(r'\(\((.*?)\)\)', r'[^%s]' % i, text, count=1)
|
||||||
text += "\n[^%s]: %s\n" % (i, fn)
|
text += "\n[^%s]: %s\n" % (i, fn)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
text = re.sub(r'\\{2,}[\s]', r' \n', text) # Carriage return
|
text = re.sub(r'\\{2,}[\s]', r' \n', text) # Carriage return
|
||||||
|
|
||||||
text = re.sub(r'\[\[(.*?)\|(.*?)\]\]', r'[\2](\1)', text) # Links
|
text = re.sub(r'\[\[(.*?)\|(.*?)\]\]', r'[\2](\1)', text) # Links
|
||||||
text = re.sub(r'\[\[(.*?)\]\]', r'[\1](\1)', text) # Links 2
|
text = re.sub(r'\[\[(.*?)\]\]', r'[\1](\1)', text) # Links 2
|
||||||
text = re.sub(r'{{(.*?)\|(.*?)}}', r'![\2](\1 "\2")', text) # Images
|
text = re.sub(r'{{(.*?)\|(.*?)}}', r'![\2](\1 "\2")', text) # Images
|
||||||
text = re.sub(r'{{(.*?)(\|(.*?))?}}', r'![\1](\1 "\1")', text) # Images 2
|
text = re.sub(r'{{(.*?)(\|(.*?))?}}', r'![\1](\1 "\1")', text) # Images 2
|
||||||
text = re.sub(r'{\[(.*?)(\|(.*?))?\]}', r'[\1](\1)', text) # Video (transform to classic links, since we can't integrate them)
|
text = re.sub(r'{\[(.*?)(\|(.*?))?\]}', r'[\1](\1)', text) # Video (transform to classic links, since we can't integrate them)
|
||||||
|
|
||||||
text = re.sub(r'###(\d*?)###', r'[[[\1]]]', text) # Progress bar
|
text = re.sub(r'###(\d*?)###', r'[[[\1]]]', text) # Progress bar
|
||||||
|
|
||||||
text = re.sub(r'(\n +[^* -][^\n]*(\n +[^* -][^\n]*)*)', r'```\1\n```', text, flags=re.DOTALL) # Block code without lists
|
text = re.sub(r'(\n +[^* -][^\n]*(\n +[^* -][^\n]*)*)', r'```\1\n```', text, flags=re.DOTALL) # Block code without lists
|
||||||
|
|
||||||
text = re.sub(r'( +)-(.*)', r'1.\2', text) # Ordered lists
|
text = re.sub(r'( +)-(.*)', r'1.\2', text) # Ordered lists
|
||||||
|
|
||||||
new_text = []
|
new_text = []
|
||||||
quote_level = 0
|
quote_level = 0
|
||||||
for line in text.splitlines(): # Tables and quotes
|
for line in text.splitlines(): # Tables and quotes
|
||||||
enter = re.finditer(r'\[quote(=(.+?))?\]', line)
|
enter = re.finditer(r'\[quote(=(.+?))?\]', line)
|
||||||
quit = re.finditer(r'\[/quote\]', line)
|
quit = re.finditer(r'\[/quote\]', line)
|
||||||
if re.search(r'\A\s*\^(([^\^]*?)\^)*', line): # Table part
|
if re.search(r'\A\s*\^(([^\^]*?)\^)*', line): # Table part
|
||||||
line = line.replace('^', '|')
|
line = line.replace('^', '|')
|
||||||
new_text.append("> " * quote_level + line)
|
new_text.append("> " * quote_level + line)
|
||||||
new_text.append("> " * quote_level + "|---|") # Don't keep the text alignement in tables it's really too complex for what it's worth
|
new_text.append("> " * quote_level + "|---|") # Don't keep the text alignement in tables it's really too complex for what it's worth
|
||||||
elif enter or quit: # Quote part
|
elif enter or quit: # Quote part
|
||||||
for quote in enter: # Enter quotes (support multiple at a time)
|
for quote in enter: # Enter quotes (support multiple at a time)
|
||||||
quote_level += 1
|
quote_level += 1
|
||||||
try:
|
try:
|
||||||
new_text.append("> " * quote_level + "##### " + quote.group(2))
|
new_text.append("> " * quote_level + "##### " + quote.group(2))
|
||||||
except:
|
except:
|
||||||
new_text.append("> " * quote_level)
|
new_text.append("> " * quote_level)
|
||||||
line = line.replace(quote.group(0), '')
|
line = line.replace(quote.group(0), '')
|
||||||
final_quote_level = quote_level # Store quote_level to use at the end, since it will be modified during quit iteration
|
final_quote_level = quote_level # Store quote_level to use at the end, since it will be modified during quit iteration
|
||||||
final_newline = False
|
final_newline = False
|
||||||
for quote in quit: # Quit quotes (support multiple at a time)
|
for quote in quit: # Quit quotes (support multiple at a time)
|
||||||
line = line.replace(quote.group(0), '')
|
line = line.replace(quote.group(0), '')
|
||||||
quote_level -= 1
|
quote_level -= 1
|
||||||
final_newline = True
|
final_newline = True
|
||||||
new_text.append("> " * final_quote_level + line) # Finally append the line
|
new_text.append("> " * final_quote_level + line) # Finally append the line
|
||||||
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
if final_newline:
|
||||||
|
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||||
else:
|
else:
|
||||||
new_text.append(line)
|
new_text.append(line)
|
||||||
|
|
||||||
return "\n".join(new_text)
|
return "\n".join(new_text)
|
||||||
|
|
||||||
|
|
||||||
def bbcode_to_markdown(text):
|
def bbcode_to_markdown(text):
|
||||||
"""This is a very basic BBcode translator"""
|
"""This is a very basic BBcode translator"""
|
||||||
text = re.sub(r'\[b\](.*?)\[\/b\]', r'**\1**', text, flags=re.DOTALL) # Bold
|
text = re.sub(r'\[b\](.*?)\[\/b\]', r'**\1**', text, flags=re.DOTALL) # Bold
|
||||||
text = re.sub(r'\[i\](.*?)\[\/i\]', r'*\1*', text, flags=re.DOTALL) # Italic
|
text = re.sub(r'\[i\](.*?)\[\/i\]', r'*\1*', text, flags=re.DOTALL) # Italic
|
||||||
text = re.sub(r'\[u\](.*?)\[\/u\]', r'__\1__', text, flags=re.DOTALL) # Underline
|
text = re.sub(r'\[u\](.*?)\[\/u\]', r'__\1__', text, flags=re.DOTALL) # Underline
|
||||||
text = re.sub(r'\[s\](.*?)\[\/s\]', r'~~\1~~', text, flags=re.DOTALL) # Strike (may be multiline)
|
text = re.sub(r'\[s\](.*?)\[\/s\]', r'~~\1~~', text, flags=re.DOTALL) # Strike (may be multiline)
|
||||||
text = re.sub(r'\[strike\](.*?)\[\/strike\]', r'~~\1~~', text, flags=re.DOTALL) # Strike 2
|
text = re.sub(r'\[strike\](.*?)\[\/strike\]', r'~~\1~~', text, flags=re.DOTALL) # Strike 2
|
||||||
|
|
||||||
text = re.sub(r'article://', r'page://', text)
|
text = re.sub(r'article://', r'page://', text)
|
||||||
text = re.sub(r'dfile://', r'file://', text)
|
text = re.sub(r'dfile://', r'file://', text)
|
||||||
|
|
||||||
text = re.sub(r'\[url=(.*?)\](.*)\[\/url\]', r'[\2](\1)', text) # Links
|
text = re.sub(r'\[url=(.*?)\](.*)\[\/url\]', r'[\2](\1)', text) # Links
|
||||||
text = re.sub(r'\[url\](.*)\[\/url\]', r'\1', text) # Links 2
|
text = re.sub(r'\[url\](.*)\[\/url\]', r'\1', text) # Links 2
|
||||||
text = re.sub(r'\[img\](.*)\[\/img\]', r'![\1](\1 "\1")', text) # Images
|
text = re.sub(r'\[img\](.*)\[\/img\]', r'![\1](\1 "\1")', text) # Images
|
||||||
|
|
||||||
new_text = []
|
new_text = []
|
||||||
quote_level = 0
|
quote_level = 0
|
||||||
for line in text.splitlines(): # Tables and quotes
|
for line in text.splitlines(): # Tables and quotes
|
||||||
enter = re.finditer(r'\[quote(=(.+?))?\]', line)
|
enter = re.finditer(r'\[quote(=(.+?))?\]', line)
|
||||||
quit = re.finditer(r'\[/quote\]', line)
|
quit = re.finditer(r'\[/quote\]', line)
|
||||||
if enter or quit: # Quote part
|
if enter or quit: # Quote part
|
||||||
for quote in enter: # Enter quotes (support multiple at a time)
|
for quote in enter: # Enter quotes (support multiple at a time)
|
||||||
quote_level += 1
|
quote_level += 1
|
||||||
try:
|
try:
|
||||||
new_text.append("> " * quote_level + "##### " + quote.group(2))
|
new_text.append("> " * quote_level + "##### " + quote.group(2))
|
||||||
except:
|
except:
|
||||||
new_text.append("> " * quote_level)
|
new_text.append("> " * quote_level)
|
||||||
line = line.replace(quote.group(0), '')
|
line = line.replace(quote.group(0), '')
|
||||||
final_quote_level = quote_level # Store quote_level to use at the end, since it will be modified during quit iteration
|
final_quote_level = quote_level # Store quote_level to use at the end, since it will be modified during quit iteration
|
||||||
final_newline = False
|
final_newline = False
|
||||||
for quote in quit: # Quit quotes (support multiple at a time)
|
for quote in quit: # Quit quotes (support multiple at a time)
|
||||||
line = line.replace(quote.group(0), '')
|
line = line.replace(quote.group(0), '')
|
||||||
quote_level -= 1
|
quote_level -= 1
|
||||||
final_newline = True
|
final_newline = True
|
||||||
new_text.append("> " * final_quote_level + line) # Finally append the line
|
new_text.append("> " * final_quote_level + line) # Finally append the line
|
||||||
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
if final_newline:
|
||||||
|
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||||
else:
|
else:
|
||||||
new_text.append(line)
|
new_text.append(line)
|
||||||
|
|
||||||
return "\n".join(new_text)
|
return "\n".join(new_text)
|
||||||
|
|
||||||
|
@ -23,29 +23,28 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
# This file contains all the views that concern the page model
|
# This file contains all the views that concern the page model
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import redirect
|
||||||
from django.views.generic import ListView, DetailView, TemplateView
|
from django.views.generic import ListView, DetailView, TemplateView
|
||||||
from django.views.generic.edit import UpdateView, CreateView, FormMixin, DeleteView
|
from django.views.generic.edit import UpdateView, FormMixin, DeleteView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.forms import CheckboxSelectMultiple
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.core.servers.basehttp import FileWrapper
|
from django.core.servers.basehttp import FileWrapper
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
|
from django.core.exceptions import PermissionDenied
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ajax_select import make_ajax_form, make_ajax_field
|
from ajax_select import make_ajax_field
|
||||||
|
|
||||||
from core.models import SithFile, RealGroup, Notification
|
from core.models import SithFile, RealGroup, Notification
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, can_view, not_found
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, can_view, not_found
|
||||||
from counter.models import Counter
|
from counter.models import Counter
|
||||||
|
|
||||||
|
|
||||||
def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
||||||
"""
|
"""
|
||||||
Send a file through Django without loading the whole file into
|
Send a file through Django without loading the whole file into
|
||||||
@ -57,7 +56,7 @@ def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
|||||||
return not_found(request)
|
return not_found(request)
|
||||||
if not (can_view(f, request.user) or
|
if not (can_view(f, request.user) or
|
||||||
('counter_token' in request.session.keys() and
|
('counter_token' in request.session.keys() and
|
||||||
request.session['counter_token'] and # check if not null for counters that have no token set
|
request.session['counter_token'] and # check if not null for counters that have no token set
|
||||||
Counter.objects.filter(token=request.session['counter_token']).exists())
|
Counter.objects.filter(token=request.session['counter_token']).exists())
|
||||||
):
|
):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
@ -70,10 +69,11 @@ def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
|||||||
response['Content-Disposition'] = ('inline; filename="%s"' % f.name).encode('utf-8')
|
response['Content-Disposition'] = ('inline; filename="%s"' % f.name).encode('utf-8')
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class AddFilesForm(forms.Form):
|
class AddFilesForm(forms.Form):
|
||||||
folder_name = forms.CharField(label=_("Add a new folder"), max_length=30, required=False)
|
folder_name = forms.CharField(label=_("Add a new folder"), max_length=30, required=False)
|
||||||
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Files"),
|
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Files"),
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
def process(self, parent, owner, files):
|
def process(self, parent, owner, files):
|
||||||
notif = False
|
notif = False
|
||||||
@ -85,10 +85,10 @@ class AddFilesForm(forms.Form):
|
|||||||
notif = True
|
notif = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.add_error(None, _("Error creating folder %(folder_name)s: %(msg)s") %
|
self.add_error(None, _("Error creating folder %(folder_name)s: %(msg)s") %
|
||||||
{'folder_name': self.cleaned_data['folder_name'], 'msg': repr(e)})
|
{'folder_name': self.cleaned_data['folder_name'], 'msg': repr(e)})
|
||||||
for f in files:
|
for f in files:
|
||||||
new_file = SithFile(parent=parent, name=f.name, file=f, owner=owner, is_folder=False,
|
new_file = SithFile(parent=parent, name=f.name, file=f, owner=owner, is_folder=False,
|
||||||
mime_type=f.content_type, size=f._size)
|
mime_type=f.content_type, size=f._size)
|
||||||
try:
|
try:
|
||||||
new_file.clean()
|
new_file.clean()
|
||||||
new_file.save()
|
new_file.save()
|
||||||
@ -100,6 +100,7 @@ class AddFilesForm(forms.Form):
|
|||||||
if not u.notifications.filter(type="FILE_MODERATION", viewed=False).exists():
|
if not u.notifications.filter(type="FILE_MODERATION", viewed=False).exists():
|
||||||
Notification(user=u, url=reverse("core:file_moderation"), type="FILE_MODERATION").save()
|
Notification(user=u, url=reverse("core:file_moderation"), type="FILE_MODERATION").save()
|
||||||
|
|
||||||
|
|
||||||
class FileListView(ListView):
|
class FileListView(ListView):
|
||||||
template_name = 'core/file_list.jinja'
|
template_name = 'core/file_list.jinja'
|
||||||
context_object_name = "file_list"
|
context_object_name = "file_list"
|
||||||
@ -114,6 +115,7 @@ class FileListView(ListView):
|
|||||||
kwargs['popup'] = 'popup'
|
kwargs['popup'] = 'popup'
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class FileEditView(CanEditMixin, UpdateView):
|
class FileEditView(CanEditMixin, UpdateView):
|
||||||
model = SithFile
|
model = SithFile
|
||||||
pk_url_kwarg = "file_id"
|
pk_url_kwarg = "file_id"
|
||||||
@ -138,6 +140,7 @@ class FileEditView(CanEditMixin, UpdateView):
|
|||||||
kwargs['popup'] = 'popup'
|
kwargs['popup'] = 'popup'
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class FileEditPropForm(forms.ModelForm):
|
class FileEditPropForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SithFile
|
model = SithFile
|
||||||
@ -147,6 +150,7 @@ class FileEditPropForm(forms.ModelForm):
|
|||||||
view_groups = make_ajax_field(SithFile, 'view_groups', 'groups', help_text="", label=_("view group"))
|
view_groups = make_ajax_field(SithFile, 'view_groups', 'groups', help_text="", label=_("view group"))
|
||||||
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
||||||
|
|
||||||
|
|
||||||
class FileEditPropView(CanEditPropMixin, UpdateView):
|
class FileEditPropView(CanEditPropMixin, UpdateView):
|
||||||
model = SithFile
|
model = SithFile
|
||||||
pk_url_kwarg = "file_id"
|
pk_url_kwarg = "file_id"
|
||||||
@ -175,6 +179,7 @@ class FileEditPropView(CanEditPropMixin, UpdateView):
|
|||||||
kwargs['popup'] = 'popup'
|
kwargs['popup'] = 'popup'
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class FileView(CanViewMixin, DetailView, FormMixin):
|
class FileView(CanViewMixin, DetailView, FormMixin):
|
||||||
"""This class handle the upload of new files into a folder"""
|
"""This class handle the upload of new files into a folder"""
|
||||||
model = SithFile
|
model = SithFile
|
||||||
@ -217,7 +222,7 @@ class FileView(CanViewMixin, DetailView, FormMixin):
|
|||||||
request.session['clipboard'] = []
|
request.session['clipboard'] = []
|
||||||
if request.user.can_edit(self.object):
|
if request.user.can_edit(self.object):
|
||||||
FileView.handle_clipboard(request, self.object)
|
FileView.handle_clipboard(request, self.object)
|
||||||
self.form = self.get_form() # The form handle only the file upload
|
self.form = self.get_form() # The form handle only the file upload
|
||||||
files = request.FILES.getlist('file_field')
|
files = request.FILES.getlist('file_field')
|
||||||
if request.user.is_authenticated() and request.user.can_edit(self.object) and self.form.is_valid():
|
if request.user.is_authenticated() and request.user.can_edit(self.object) and self.form.is_valid():
|
||||||
self.form.process(parent=self.object, owner=request.user, files=files)
|
self.form.process(parent=self.object, owner=request.user, files=files)
|
||||||
@ -237,6 +242,7 @@ class FileView(CanViewMixin, DetailView, FormMixin):
|
|||||||
kwargs['clipboard'] = SithFile.objects.filter(id__in=self.request.session['clipboard'])
|
kwargs['clipboard'] = SithFile.objects.filter(id__in=self.request.session['clipboard'])
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class FileDeleteView(CanEditPropMixin, DeleteView):
|
class FileDeleteView(CanEditPropMixin, DeleteView):
|
||||||
model = SithFile
|
model = SithFile
|
||||||
pk_url_kwarg = "file_id"
|
pk_url_kwarg = "file_id"
|
||||||
@ -244,7 +250,7 @@ class FileDeleteView(CanEditPropMixin, DeleteView):
|
|||||||
context_object_name = "file"
|
context_object_name = "file"
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
self.object.file.delete() # Doing it here or overloading delete() is the same, so let's do it here
|
self.object.file.delete() # Doing it here or overloading delete() is the same, so let's do it here
|
||||||
if 'next' in self.request.GET.keys():
|
if 'next' in self.request.GET.keys():
|
||||||
return self.request.GET['next']
|
return self.request.GET['next']
|
||||||
if self.object.parent is None:
|
if self.object.parent is None:
|
||||||
@ -258,6 +264,7 @@ class FileDeleteView(CanEditPropMixin, DeleteView):
|
|||||||
kwargs['popup'] = 'popup'
|
kwargs['popup'] = 'popup'
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class FileModerationView(TemplateView):
|
class FileModerationView(TemplateView):
|
||||||
template_name = "core/file_moderation.jinja"
|
template_name = "core/file_moderation.jinja"
|
||||||
|
|
||||||
@ -266,6 +273,7 @@ class FileModerationView(TemplateView):
|
|||||||
kwargs['files'] = SithFile.objects.filter(is_moderated=False)[:100]
|
kwargs['files'] = SithFile.objects.filter(is_moderated=False)[:100]
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
||||||
model = SithFile
|
model = SithFile
|
||||||
pk_url_kwarg = "file_id"
|
pk_url_kwarg = "file_id"
|
||||||
@ -278,4 +286,3 @@ class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
|||||||
if 'next' in self.request.GET.keys():
|
if 'next' in self.request.GET.keys():
|
||||||
return redirect(self.request.GET['next'])
|
return redirect(self.request.GET['next'])
|
||||||
return redirect('core:file_moderation')
|
return redirect('core:file_moderation')
|
||||||
|
|
||||||
|
@ -22,22 +22,24 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, UserChangeForm
|
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.contrib.auth import logout, login, authenticate
|
|
||||||
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput, DateTimeInput, Textarea
|
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput, DateTimeInput, Textarea
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext
|
from django.utils.translation import ugettext
|
||||||
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
|
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
|
||||||
from ajax_select.fields import AutoCompleteSelectField
|
from ajax_select.fields import AutoCompleteSelectField
|
||||||
|
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from core.models import User, Page, RealGroup, SithFile
|
from core.models import User, Page, SithFile
|
||||||
|
|
||||||
|
from core.utils import resize_image
|
||||||
|
from io import BytesIO
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
# Widgets
|
# Widgets
|
||||||
@ -50,6 +52,7 @@ class SelectSingle(Select):
|
|||||||
attrs = {'class': "select_single"}
|
attrs = {'class': "select_single"}
|
||||||
return super(SelectSingle, self).render(name, value, attrs)
|
return super(SelectSingle, self).render(name, value, attrs)
|
||||||
|
|
||||||
|
|
||||||
class SelectMultiple(Select):
|
class SelectMultiple(Select):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if attrs:
|
if attrs:
|
||||||
@ -58,6 +61,7 @@ class SelectMultiple(Select):
|
|||||||
attrs = {'class': "select_multiple"}
|
attrs = {'class': "select_multiple"}
|
||||||
return super(SelectMultiple, self).render(name, value, attrs)
|
return super(SelectMultiple, self).render(name, value, attrs)
|
||||||
|
|
||||||
|
|
||||||
class SelectDateTime(DateTimeInput):
|
class SelectDateTime(DateTimeInput):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if attrs:
|
if attrs:
|
||||||
@ -66,6 +70,7 @@ class SelectDateTime(DateTimeInput):
|
|||||||
attrs = {'class': "select_datetime"}
|
attrs = {'class': "select_datetime"}
|
||||||
return super(SelectDateTime, self).render(name, value, attrs)
|
return super(SelectDateTime, self).render(name, value, attrs)
|
||||||
|
|
||||||
|
|
||||||
class SelectDate(DateInput):
|
class SelectDate(DateInput):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if attrs:
|
if attrs:
|
||||||
@ -74,16 +79,18 @@ class SelectDate(DateInput):
|
|||||||
attrs = {'class': "select_date"}
|
attrs = {'class': "select_date"}
|
||||||
return super(SelectDate, self).render(name, value, attrs)
|
return super(SelectDate, self).render(name, value, attrs)
|
||||||
|
|
||||||
|
|
||||||
class MarkdownInput(Textarea):
|
class MarkdownInput(Textarea):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
output = '<p><a href="%(syntax_url)s">%(help_text)s</a></p>'\
|
output = '<p><a href="%(syntax_url)s">%(help_text)s</a></p>'\
|
||||||
'<div class="markdown_editor">%(content)s</div>' % {
|
'<div class="markdown_editor">%(content)s</div>' % {
|
||||||
'syntax_url': Page.get_page_by_full_name(settings.SITH_CORE_PAGE_SYNTAX).get_absolute_url(),
|
'syntax_url': Page.get_page_by_full_name(settings.SITH_CORE_PAGE_SYNTAX).get_absolute_url(),
|
||||||
'help_text': _("Help on the syntax"),
|
'help_text': _("Help on the syntax"),
|
||||||
'content': super(MarkdownInput, self).render(name, value, attrs),
|
'content': super(MarkdownInput, self).render(name, value, attrs),
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
class SelectFile(TextInput):
|
class SelectFile(TextInput):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if attrs:
|
if attrs:
|
||||||
@ -91,13 +98,14 @@ class SelectFile(TextInput):
|
|||||||
else:
|
else:
|
||||||
attrs = {'class': "select_file"}
|
attrs = {'class': "select_file"}
|
||||||
output = '%(content)s<div name="%(name)s" class="choose_file_widget" title="%(title)s"></div>' % {
|
output = '%(content)s<div name="%(name)s" class="choose_file_widget" title="%(title)s"></div>' % {
|
||||||
'content': super(SelectFile, self).render(name, value, attrs),
|
'content': super(SelectFile, self).render(name, value, attrs),
|
||||||
'title': _("Choose file"),
|
'title': _("Choose file"),
|
||||||
'name': name,
|
'name': name,
|
||||||
}
|
}
|
||||||
output += '<span name="' + name + '" class="choose_file_button">' + ugettext("Choose file") + '</span>'
|
output += '<span name="' + name + '" class="choose_file_button">' + ugettext("Choose file") + '</span>'
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
class SelectUser(TextInput):
|
class SelectUser(TextInput):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if attrs:
|
if attrs:
|
||||||
@ -105,15 +113,16 @@ class SelectUser(TextInput):
|
|||||||
else:
|
else:
|
||||||
attrs = {'class': "select_user"}
|
attrs = {'class': "select_user"}
|
||||||
output = '%(content)s<div name="%(name)s" class="choose_user_widget" title="%(title)s"></div>' % {
|
output = '%(content)s<div name="%(name)s" class="choose_user_widget" title="%(title)s"></div>' % {
|
||||||
'content': super(SelectUser, self).render(name, value, attrs),
|
'content': super(SelectUser, self).render(name, value, attrs),
|
||||||
'title': _("Choose user"),
|
'title': _("Choose user"),
|
||||||
'name': name,
|
'name': name,
|
||||||
}
|
}
|
||||||
output += '<span name="' + name + '" class="choose_user_button">' + ugettext("Choose user") + '</span>'
|
output += '<span name="' + name + '" class="choose_user_button">' + ugettext("Choose user") + '</span>'
|
||||||
return output
|
return output
|
||||||
|
|
||||||
# Forms
|
# Forms
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(AuthenticationForm):
|
class LoginForm(AuthenticationForm):
|
||||||
def __init__(self, *arg, **kwargs):
|
def __init__(self, *arg, **kwargs):
|
||||||
if 'data' in kwargs.keys():
|
if 'data' in kwargs.keys():
|
||||||
@ -128,14 +137,17 @@ class LoginForm(AuthenticationForm):
|
|||||||
else:
|
else:
|
||||||
user = User.objects.filter(username=data['username']).first()
|
user = User.objects.filter(username=data['username']).first()
|
||||||
data['username'] = user.username
|
data['username'] = user.username
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
kwargs['data'] = data
|
kwargs['data'] = data
|
||||||
super(LoginForm, self).__init__(*arg, **kwargs)
|
super(LoginForm, self).__init__(*arg, **kwargs)
|
||||||
self.fields['username'].label = _("Username, email, or account number")
|
self.fields['username'].label = _("Username, email, or account number")
|
||||||
|
|
||||||
|
|
||||||
class RegisteringForm(UserCreationForm):
|
class RegisteringForm(UserCreationForm):
|
||||||
error_css_class = 'error'
|
error_css_class = 'error'
|
||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ('first_name', 'last_name', 'email')
|
fields = ('first_name', 'last_name', 'email')
|
||||||
@ -148,9 +160,6 @@ class RegisteringForm(UserCreationForm):
|
|||||||
user.save()
|
user.save()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
from core.utils import resize_image
|
|
||||||
from io import BytesIO
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
class UserProfileForm(forms.ModelForm):
|
class UserProfileForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
@ -161,22 +170,22 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ['first_name', 'last_name', 'nick_name', 'email', 'date_of_birth', 'profile_pict', 'avatar_pict',
|
fields = ['first_name', 'last_name', 'nick_name', 'email', 'date_of_birth', 'profile_pict', 'avatar_pict',
|
||||||
'scrub_pict', 'sex', 'second_email', 'address', 'parent_address', 'phone', 'parent_phone',
|
'scrub_pict', 'sex', 'second_email', 'address', 'parent_address', 'phone', 'parent_phone',
|
||||||
'tshirt_size', 'role', 'department', 'dpt_option', 'semester', 'quote', 'school', 'promo',
|
'tshirt_size', 'role', 'department', 'dpt_option', 'semester', 'quote', 'school', 'promo',
|
||||||
'forum_signature', 'is_subscriber_viewable']
|
'forum_signature', 'is_subscriber_viewable']
|
||||||
widgets = {
|
widgets = {
|
||||||
'date_of_birth': SelectDate,
|
'date_of_birth': SelectDate,
|
||||||
'profile_pict': forms.ClearableFileInput,
|
'profile_pict': forms.ClearableFileInput,
|
||||||
'avatar_pict': forms.ClearableFileInput,
|
'avatar_pict': forms.ClearableFileInput,
|
||||||
'scrub_pict': forms.ClearableFileInput,
|
'scrub_pict': forms.ClearableFileInput,
|
||||||
'phone': PhoneNumberInternationalFallbackWidget,
|
'phone': PhoneNumberInternationalFallbackWidget,
|
||||||
'parent_phone': PhoneNumberInternationalFallbackWidget,
|
'parent_phone': PhoneNumberInternationalFallbackWidget,
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
'profile_pict': _("Profile: you need to be visible on the picture, in order to be recognized (e.g. by the barmen)"),
|
'profile_pict': _("Profile: you need to be visible on the picture, in order to be recognized (e.g. by the barmen)"),
|
||||||
'avatar_pict': _("Avatar: used on the forum"),
|
'avatar_pict': _("Avatar: used on the forum"),
|
||||||
'scrub_pict': _("Scrub: let other know how your scrub looks like!"),
|
'scrub_pict': _("Scrub: let other know how your scrub looks like!"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *arg, **kwargs):
|
def __init__(self, *arg, **kwargs):
|
||||||
super(UserProfileForm, self).__init__(*arg, **kwargs)
|
super(UserProfileForm, self).__init__(*arg, **kwargs)
|
||||||
@ -197,14 +206,14 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
self.cleaned_data['profile_pict'] = profile
|
self.cleaned_data['profile_pict'] = profile
|
||||||
self.cleaned_data['scrub_pict'] = scrub
|
self.cleaned_data['scrub_pict'] = scrub
|
||||||
parent = SithFile.objects.filter(parent=None, name="profiles").first()
|
parent = SithFile.objects.filter(parent=None, name="profiles").first()
|
||||||
for field,f in files:
|
for field, f in files:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
try:
|
try:
|
||||||
im = Image.open(BytesIO(f.read()))
|
im = Image.open(BytesIO(f.read()))
|
||||||
new_file = SithFile(parent=parent, name=self.generate_name(field, f),
|
new_file = SithFile(parent=parent, name=self.generate_name(field, f),
|
||||||
file=resize_image(im, 400, f.content_type.split('/')[-1]),
|
file=resize_image(im, 400, f.content_type.split('/')[-1]),
|
||||||
owner=self.instance, is_folder=False, mime_type=f.content_type, size=f._size,
|
owner=self.instance, is_folder=False, mime_type=f.content_type, size=f._size,
|
||||||
moderator=self.instance, is_moderated=True)
|
moderator=self.instance, is_moderated=True)
|
||||||
new_file.file.name = new_file.name
|
new_file.file.name = new_file.name
|
||||||
old = SithFile.objects.filter(parent=parent, name=new_file.name).first()
|
old = SithFile.objects.filter(parent=parent, name=new_file.name).first()
|
||||||
if old:
|
if old:
|
||||||
@ -216,16 +225,18 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self._errors.pop(field, None)
|
self._errors.pop(field, None)
|
||||||
self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
|
self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
|
||||||
{'file_name': f, 'msg': str(e.message)})
|
{'file_name': f, 'msg': str(e.message)})
|
||||||
except IOError:
|
except IOError:
|
||||||
self._errors.pop(field, None)
|
self._errors.pop(field, None)
|
||||||
self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
|
self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
|
||||||
{'file_name': f, 'msg': _("Bad image format, only jpeg, png, and gif are accepted")})
|
{'file_name': f, 'msg': _("Bad image format, only jpeg, png, and gif are accepted")})
|
||||||
self._post_clean()
|
self._post_clean()
|
||||||
|
|
||||||
|
|
||||||
class UserPropForm(forms.ModelForm):
|
class UserPropForm(forms.ModelForm):
|
||||||
error_css_class = 'error'
|
error_css_class = 'error'
|
||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ['groups']
|
fields = ['groups']
|
||||||
@ -236,13 +247,16 @@ class UserPropForm(forms.ModelForm):
|
|||||||
'groups': CheckboxSelectMultiple,
|
'groups': CheckboxSelectMultiple,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UserGodfathersForm(forms.Form):
|
class UserGodfathersForm(forms.Form):
|
||||||
type = forms.ChoiceField(choices=[('godfather', _("Godfather")), ('godchild', _("Godchild"))], label=_("Add"))
|
type = forms.ChoiceField(choices=[('godfather', _("Godfather")), ('godchild', _("Godchild"))], label=_("Add"))
|
||||||
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
|
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
|
||||||
|
|
||||||
|
|
||||||
class PagePropForm(forms.ModelForm):
|
class PagePropForm(forms.ModelForm):
|
||||||
error_css_class = 'error'
|
error_css_class = 'error'
|
||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Page
|
model = Page
|
||||||
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ]
|
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ]
|
||||||
@ -255,4 +269,3 @@ class PagePropForm(forms.ModelForm):
|
|||||||
super(PagePropForm, self).__init__(*arg, **kwargs)
|
super(PagePropForm, self).__init__(*arg, **kwargs)
|
||||||
self.fields['edit_groups'].required = False
|
self.fields['edit_groups'].required = False
|
||||||
self.fields['view_groups'].required = False
|
self.fields['view_groups'].required = False
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ from django.core.urlresolvers import reverse_lazy
|
|||||||
from core.models import RealGroup
|
from core.models import RealGroup
|
||||||
from core.views import CanEditMixin
|
from core.views import CanEditMixin
|
||||||
|
|
||||||
|
|
||||||
class GroupListView(CanEditMixin, ListView):
|
class GroupListView(CanEditMixin, ListView):
|
||||||
"""
|
"""
|
||||||
Displays the group list
|
Displays the group list
|
||||||
@ -36,17 +37,20 @@ class GroupListView(CanEditMixin, ListView):
|
|||||||
model = RealGroup
|
model = RealGroup
|
||||||
template_name = "core/group_list.jinja"
|
template_name = "core/group_list.jinja"
|
||||||
|
|
||||||
|
|
||||||
class GroupEditView(CanEditMixin, UpdateView):
|
class GroupEditView(CanEditMixin, UpdateView):
|
||||||
model = RealGroup
|
model = RealGroup
|
||||||
pk_url_kwarg = "group_id"
|
pk_url_kwarg = "group_id"
|
||||||
template_name = "core/group_edit.jinja"
|
template_name = "core/group_edit.jinja"
|
||||||
fields = ['name', 'description']
|
fields = ['name', 'description']
|
||||||
|
|
||||||
|
|
||||||
class GroupCreateView(CanEditMixin, CreateView):
|
class GroupCreateView(CanEditMixin, CreateView):
|
||||||
model = RealGroup
|
model = RealGroup
|
||||||
template_name = "core/group_edit.jinja"
|
template_name = "core/group_edit.jinja"
|
||||||
fields = ['name', 'description']
|
fields = ['name', 'description']
|
||||||
|
|
||||||
|
|
||||||
class GroupDeleteView(CanEditMixin, DeleteView):
|
class GroupDeleteView(CanEditMixin, DeleteView):
|
||||||
model = RealGroup
|
model = RealGroup
|
||||||
pk_url_kwarg = "group_id"
|
pk_url_kwarg = "group_id"
|
||||||
|
@ -23,23 +23,22 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
# This file contains all the views that concern the page model
|
# This file contains all the views that concern the page model
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView
|
||||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
|
||||||
from django.utils.decorators import method_decorator
|
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.forms import CheckboxSelectMultiple, modelform_factory
|
from django.forms import CheckboxSelectMultiple
|
||||||
|
|
||||||
from core.models import Page, PageRev, LockError
|
from core.models import Page, PageRev, LockError
|
||||||
from core.views.forms import PagePropForm, MarkdownInput
|
from core.views.forms import MarkdownInput
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
||||||
|
|
||||||
|
|
||||||
class PageListView(CanViewMixin, ListView):
|
class PageListView(CanViewMixin, ListView):
|
||||||
model = Page
|
model = Page
|
||||||
template_name = 'core/page_list.jinja'
|
template_name = 'core/page_list.jinja'
|
||||||
|
|
||||||
|
|
||||||
class PageView(CanViewMixin, DetailView):
|
class PageView(CanViewMixin, DetailView):
|
||||||
model = Page
|
model = Page
|
||||||
template_name = 'core/page_detail.jinja'
|
template_name = 'core/page_detail.jinja'
|
||||||
@ -54,6 +53,7 @@ class PageView(CanViewMixin, DetailView):
|
|||||||
context['new_page'] = self.kwargs['page_name']
|
context['new_page'] = self.kwargs['page_name']
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class PageHistView(CanViewMixin, DetailView):
|
class PageHistView(CanViewMixin, DetailView):
|
||||||
model = Page
|
model = Page
|
||||||
template_name = 'core/page_hist.jinja'
|
template_name = 'core/page_hist.jinja'
|
||||||
@ -62,6 +62,7 @@ class PageHistView(CanViewMixin, DetailView):
|
|||||||
self.page = Page.get_page_by_full_name(self.kwargs['page_name'])
|
self.page = Page.get_page_by_full_name(self.kwargs['page_name'])
|
||||||
return self.page
|
return self.page
|
||||||
|
|
||||||
|
|
||||||
class PageRevView(CanViewMixin, DetailView):
|
class PageRevView(CanViewMixin, DetailView):
|
||||||
model = Page
|
model = Page
|
||||||
template_name = 'core/page_detail.jinja'
|
template_name = 'core/page_detail.jinja'
|
||||||
@ -78,20 +79,21 @@ class PageRevView(CanViewMixin, DetailView):
|
|||||||
rev = self.page.revisions.get(id=self.kwargs['rev'])
|
rev = self.page.revisions.get(id=self.kwargs['rev'])
|
||||||
context['rev'] = rev
|
context['rev'] = rev
|
||||||
except:
|
except:
|
||||||
# By passing, the template will just display the normal page without taking revision into account
|
# By passing, the template will just display the normal page without taking revision into account
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
context['new_page'] = self.kwargs['page_name']
|
context['new_page'] = self.kwargs['page_name']
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class PageCreateView(CanCreateMixin, CreateView):
|
class PageCreateView(CanCreateMixin, CreateView):
|
||||||
model = Page
|
model = Page
|
||||||
form_class = modelform_factory(Page,
|
form_class = modelform_factory(Page,
|
||||||
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
fields=['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
||||||
widgets={
|
widgets={
|
||||||
'edit_groups':CheckboxSelectMultiple,
|
'edit_groups': CheckboxSelectMultiple,
|
||||||
'view_groups':CheckboxSelectMultiple,
|
'view_groups': CheckboxSelectMultiple,
|
||||||
})
|
})
|
||||||
template_name = 'core/page_prop.jinja'
|
template_name = 'core/page_prop.jinja'
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
@ -115,14 +117,15 @@ class PageCreateView(CanCreateMixin, CreateView):
|
|||||||
ret = super(PageCreateView, self).form_valid(form)
|
ret = super(PageCreateView, self).form_valid(form)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class PagePropView(CanEditPropMixin, UpdateView):
|
class PagePropView(CanEditPropMixin, UpdateView):
|
||||||
model = Page
|
model = Page
|
||||||
form_class = modelform_factory(Page,
|
form_class = modelform_factory(Page,
|
||||||
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
fields=['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
||||||
widgets={
|
widgets={
|
||||||
'edit_groups':CheckboxSelectMultiple,
|
'edit_groups': CheckboxSelectMultiple,
|
||||||
'view_groups':CheckboxSelectMultiple,
|
'view_groups': CheckboxSelectMultiple,
|
||||||
})
|
})
|
||||||
template_name = 'core/page_prop.jinja'
|
template_name = 'core/page_prop.jinja'
|
||||||
slug_field = '_full_name'
|
slug_field = '_full_name'
|
||||||
slug_url_kwarg = 'page_name'
|
slug_url_kwarg = 'page_name'
|
||||||
@ -130,7 +133,7 @@ class PagePropView(CanEditPropMixin, UpdateView):
|
|||||||
def get_object(self):
|
def get_object(self):
|
||||||
o = super(PagePropView, self).get_object()
|
o = super(PagePropView, self).get_object()
|
||||||
# Create the page if it does not exists
|
# Create the page if it does not exists
|
||||||
#if p == None:
|
# if p == None:
|
||||||
# parent_name = '/'.join(page_name.split('/')[:-1])
|
# parent_name = '/'.join(page_name.split('/')[:-1])
|
||||||
# name = page_name.split('/')[-1]
|
# name = page_name.split('/')[-1]
|
||||||
# if parent_name == "":
|
# if parent_name == "":
|
||||||
@ -145,9 +148,10 @@ class PagePropView(CanEditPropMixin, UpdateView):
|
|||||||
raise e
|
raise e
|
||||||
return self.page
|
return self.page
|
||||||
|
|
||||||
|
|
||||||
class PageEditView(CanEditMixin, UpdateView):
|
class PageEditView(CanEditMixin, UpdateView):
|
||||||
model = PageRev
|
model = PageRev
|
||||||
form_class = modelform_factory(model=PageRev, fields=['title', 'content',], widgets={'content': MarkdownInput})
|
form_class = modelform_factory(model=PageRev, fields=['title', 'content', ], widgets={'content': MarkdownInput})
|
||||||
template_name = 'core/pagerev_edit.jinja'
|
template_name = 'core/pagerev_edit.jinja'
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
@ -22,17 +22,13 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect
|
||||||
from django.db import models
|
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.db.models import Q
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.views.generic import ListView, TemplateView
|
from django.views.generic import ListView, TemplateView
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
import json
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
from haystack.query import SearchQuerySet
|
from haystack.query import SearchQuerySet
|
||||||
|
|
||||||
@ -40,9 +36,11 @@ from core.models import User, Notification
|
|||||||
from core.utils import doku_to_markdown, bbcode_to_markdown
|
from core.utils import doku_to_markdown, bbcode_to_markdown
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
|
|
||||||
|
|
||||||
def index(request, context=None):
|
def index(request, context=None):
|
||||||
return render(request, "core/index.jinja")
|
return render(request, "core/index.jinja")
|
||||||
|
|
||||||
|
|
||||||
class NotificationList(ListView):
|
class NotificationList(ListView):
|
||||||
model = Notification
|
model = Notification
|
||||||
template_name = "core/notification_list.jinja"
|
template_name = "core/notification_list.jinja"
|
||||||
@ -52,6 +50,7 @@ class NotificationList(ListView):
|
|||||||
self.request.user.notifications.update(viewed=True)
|
self.request.user.notifications.update(viewed=True)
|
||||||
return self.request.user.notifications.order_by('-id')[:20]
|
return self.request.user.notifications.order_by('-id')[:20]
|
||||||
|
|
||||||
|
|
||||||
def notification(request, notif_id):
|
def notification(request, notif_id):
|
||||||
notif = Notification.objects.filter(id=notif_id).first()
|
notif = Notification.objects.filter(id=notif_id).first()
|
||||||
if notif:
|
if notif:
|
||||||
@ -60,44 +59,50 @@ def notification(request, notif_id):
|
|||||||
return redirect(notif.url)
|
return redirect(notif.url)
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
def search_user(query, as_json=False):
|
def search_user(query, as_json=False):
|
||||||
res = SearchQuerySet().models(User).filter(text=query).filter_or(text__contains=query)[:20]
|
res = SearchQuerySet().models(User).filter(text=query).filter_or(text__contains=query)[:20]
|
||||||
return [r.object for r in res]
|
return [r.object for r in res]
|
||||||
|
|
||||||
|
|
||||||
def search_club(query, as_json=False):
|
def search_club(query, as_json=False):
|
||||||
clubs = []
|
clubs = []
|
||||||
if query:
|
if query:
|
||||||
clubs = Club.objects.filter(name__icontains=query).all()
|
clubs = Club.objects.filter(name__icontains=query).all()
|
||||||
clubs = clubs[:5]
|
clubs = clubs[:5]
|
||||||
if as_json: # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
|
if as_json: # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
|
||||||
clubs = json.loads(serializers.serialize('json', clubs, fields=('name')))
|
clubs = json.loads(serializers.serialize('json', clubs, fields=('name')))
|
||||||
else:
|
else:
|
||||||
clubs = list(clubs)
|
clubs = list(clubs)
|
||||||
return clubs
|
return clubs
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def search_view(request):
|
def search_view(request):
|
||||||
result = {
|
result = {
|
||||||
'users': search_user(request.GET.get('query', '')),
|
'users': search_user(request.GET.get('query', '')),
|
||||||
'clubs': search_club(request.GET.get('query', '')),
|
'clubs': search_club(request.GET.get('query', '')),
|
||||||
}
|
}
|
||||||
return render(request, "core/search.jinja", context={'result': result})
|
return render(request, "core/search.jinja", context={'result': result})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def search_user_json(request):
|
def search_user_json(request):
|
||||||
result = {
|
result = {
|
||||||
'users': search_user(request.GET.get('query', ''), True),
|
'users': search_user(request.GET.get('query', ''), True),
|
||||||
}
|
}
|
||||||
return JsonResponse(result)
|
return JsonResponse(result)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def search_json(request):
|
def search_json(request):
|
||||||
result = {
|
result = {
|
||||||
'users': search_user(request.GET.get('query', ''), True),
|
'users': search_user(request.GET.get('query', ''), True),
|
||||||
'clubs': search_club(request.GET.get('query', ''), True),
|
'clubs': search_club(request.GET.get('query', ''), True),
|
||||||
}
|
}
|
||||||
return JsonResponse(result)
|
return JsonResponse(result)
|
||||||
|
|
||||||
|
|
||||||
class ToMarkdownView(TemplateView):
|
class ToMarkdownView(TemplateView):
|
||||||
template_name = "core/to_markdown.jinja"
|
template_name = "core/to_markdown.jinja"
|
||||||
|
|
||||||
@ -119,4 +124,3 @@ class ToMarkdownView(TemplateView):
|
|||||||
kwargs['text'] = ""
|
kwargs['text'] = ""
|
||||||
kwargs['text_md'] = ""
|
kwargs['text_md'] = ""
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
@ -24,30 +24,29 @@
|
|||||||
|
|
||||||
# This file contains all the views that concern the user model
|
# This file contains all the views that concern the user model
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.contrib.auth import logout as auth_logout, views
|
from django.contrib.auth import views
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.views.generic.edit import UpdateView
|
from django.views.generic.edit import UpdateView
|
||||||
from django.views.generic import ListView, DetailView, TemplateView, DeleteView
|
from django.views.generic import ListView, DetailView, TemplateView
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.forms import CheckboxSelectMultiple
|
from django.forms import CheckboxSelectMultiple
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.views.generic.dates import YearMixin, MonthMixin
|
from django.views.generic.dates import YearMixin, MonthMixin
|
||||||
|
|
||||||
from django.utils import timezone
|
from datetime import timedelta, date
|
||||||
from datetime import timedelta, datetime, date
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
|
||||||
from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm, LoginForm, UserGodfathersForm
|
from core.views.forms import RegisteringForm, UserProfileForm, LoginForm, UserGodfathersForm
|
||||||
from core.models import User, SithFile, Preferences
|
from core.models import User, SithFile, Preferences
|
||||||
from club.models import Club
|
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
from trombi.views import UserTrombiForm
|
from trombi.views import UserTrombiForm
|
||||||
|
|
||||||
|
|
||||||
def login(request):
|
def login(request):
|
||||||
"""
|
"""
|
||||||
The login view
|
The login view
|
||||||
@ -56,24 +55,28 @@ def login(request):
|
|||||||
"""
|
"""
|
||||||
return views.login(request, template_name="core/login.jinja", authentication_form=LoginForm)
|
return views.login(request, template_name="core/login.jinja", authentication_form=LoginForm)
|
||||||
|
|
||||||
|
|
||||||
def logout(request):
|
def logout(request):
|
||||||
"""
|
"""
|
||||||
The logout view
|
The logout view
|
||||||
"""
|
"""
|
||||||
return views.logout_then_login(request)
|
return views.logout_then_login(request)
|
||||||
|
|
||||||
|
|
||||||
def password_change(request):
|
def password_change(request):
|
||||||
"""
|
"""
|
||||||
Allows a user to change its password
|
Allows a user to change its password
|
||||||
"""
|
"""
|
||||||
return views.password_change(request, template_name="core/password_change.jinja", post_change_redirect=reverse("core:password_change_done"))
|
return views.password_change(request, template_name="core/password_change.jinja", post_change_redirect=reverse("core:password_change_done"))
|
||||||
|
|
||||||
|
|
||||||
def password_change_done(request):
|
def password_change_done(request):
|
||||||
"""
|
"""
|
||||||
Allows a user to change its password
|
Allows a user to change its password
|
||||||
"""
|
"""
|
||||||
return views.password_change_done(request, template_name="core/password_change_done.jinja")
|
return views.password_change_done(request, template_name="core/password_change_done.jinja")
|
||||||
|
|
||||||
|
|
||||||
def password_root_change(request, user_id):
|
def password_root_change(request, user_id):
|
||||||
"""
|
"""
|
||||||
Allows a root user to change someone's password
|
Allows a root user to change someone's password
|
||||||
@ -92,6 +95,7 @@ def password_root_change(request, user_id):
|
|||||||
form = views.SetPasswordForm(user=user)
|
form = views.SetPasswordForm(user=user)
|
||||||
return TemplateResponse(request, "core/password_change.jinja", {'form': form, 'target': user})
|
return TemplateResponse(request, "core/password_change.jinja", {'form': form, 'target': user})
|
||||||
|
|
||||||
|
|
||||||
def password_reset(request):
|
def password_reset(request):
|
||||||
"""
|
"""
|
||||||
Allows someone to enter an email adresse for resetting password
|
Allows someone to enter an email adresse for resetting password
|
||||||
@ -100,7 +104,8 @@ def password_reset(request):
|
|||||||
template_name="core/password_reset.jinja",
|
template_name="core/password_reset.jinja",
|
||||||
email_template_name="core/password_reset_email.jinja",
|
email_template_name="core/password_reset_email.jinja",
|
||||||
post_reset_redirect="core:password_reset_done",
|
post_reset_redirect="core:password_reset_done",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def password_reset_done(request):
|
def password_reset_done(request):
|
||||||
"""
|
"""
|
||||||
@ -108,6 +113,7 @@ def password_reset_done(request):
|
|||||||
"""
|
"""
|
||||||
return views.password_reset_done(request, template_name="core/password_reset_done.jinja")
|
return views.password_reset_done(request, template_name="core/password_reset_done.jinja")
|
||||||
|
|
||||||
|
|
||||||
def password_reset_confirm(request, uidb64=None, token=None):
|
def password_reset_confirm(request, uidb64=None, token=None):
|
||||||
"""
|
"""
|
||||||
Provide a reset password formular
|
Provide a reset password formular
|
||||||
@ -115,7 +121,8 @@ def password_reset_confirm(request, uidb64=None, token=None):
|
|||||||
return views.password_reset_confirm(request, uidb64=uidb64, token=token,
|
return views.password_reset_confirm(request, uidb64=uidb64, token=token,
|
||||||
post_reset_redirect="core:password_reset_complete",
|
post_reset_redirect="core:password_reset_complete",
|
||||||
template_name="core/password_reset_confirm.jinja",
|
template_name="core/password_reset_confirm.jinja",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def password_reset_complete(request):
|
def password_reset_complete(request):
|
||||||
"""
|
"""
|
||||||
@ -123,14 +130,15 @@ def password_reset_complete(request):
|
|||||||
"""
|
"""
|
||||||
return views.password_reset_complete(request,
|
return views.password_reset_complete(request,
|
||||||
template_name="core/password_reset_complete.jinja",
|
template_name="core/password_reset_complete.jinja",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register(request):
|
def register(request):
|
||||||
context = {}
|
context = {}
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = RegisteringForm(request.POST)
|
form = RegisteringForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
logging.debug("Registering "+form.cleaned_data['first_name']+form.cleaned_data['last_name'])
|
logging.debug("Registering " + form.cleaned_data['first_name'] + form.cleaned_data['last_name'])
|
||||||
u = form.save()
|
u = form.save()
|
||||||
context['user_registered'] = u
|
context['user_registered'] = u
|
||||||
context['tests'] = 'TEST_REGISTER_USER_FORM_OK'
|
context['tests'] = 'TEST_REGISTER_USER_FORM_OK'
|
||||||
@ -143,6 +151,7 @@ def register(request):
|
|||||||
context['form'] = form.as_p()
|
context['form'] = form.as_p()
|
||||||
return render(request, "core/register.jinja", context)
|
return render(request, "core/register.jinja", context)
|
||||||
|
|
||||||
|
|
||||||
class UserTabsMixin(TabedViewMixin):
|
class UserTabsMixin(TabedViewMixin):
|
||||||
def get_tabs_title(self):
|
def get_tabs_title(self):
|
||||||
return self.object.get_display_name()
|
return self.object.get_display_name()
|
||||||
@ -150,67 +159,69 @@ class UserTabsMixin(TabedViewMixin):
|
|||||||
def get_list_of_tabs(self):
|
def get_list_of_tabs(self):
|
||||||
tab_list = []
|
tab_list = []
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_profile', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_profile', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'infos',
|
'slug': 'infos',
|
||||||
'name': _("Infos"),
|
'name': _("Infos"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_godfathers', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_godfathers', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'godfathers',
|
'slug': 'godfathers',
|
||||||
'name': _("Godfathers"),
|
'name': _("Godfathers"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_pictures', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_pictures', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'pictures',
|
'slug': 'pictures',
|
||||||
'name': _("Pictures"),
|
'name': _("Pictures"),
|
||||||
})
|
})
|
||||||
if self.request.user == self.object:
|
if self.request.user == self.object:
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_tools'),
|
'url': reverse('core:user_tools'),
|
||||||
'slug': 'tools',
|
'slug': 'tools',
|
||||||
'name': _("Tools"),
|
'name': _("Tools"),
|
||||||
})
|
})
|
||||||
if self.request.user.can_edit(self.object):
|
if self.request.user.can_edit(self.object):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_edit', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_edit', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'edit',
|
'slug': 'edit',
|
||||||
'name': _("Edit"),
|
'name': _("Edit"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_prefs', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_prefs', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'prefs',
|
'slug': 'prefs',
|
||||||
'name': _("Preferences"),
|
'name': _("Preferences"),
|
||||||
})
|
})
|
||||||
if self.request.user.can_view(self.object):
|
if self.request.user.can_view(self.object):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_clubs', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_clubs', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'clubs',
|
'slug': 'clubs',
|
||||||
'name': _("Clubs"),
|
'name': _("Clubs"),
|
||||||
})
|
})
|
||||||
if self.request.user.is_owner(self.object):
|
if self.request.user.is_owner(self.object):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_groups', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_groups', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'groups',
|
'slug': 'groups',
|
||||||
'name': _("Groups"),
|
'name': _("Groups"),
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
if (self.object.customer and (self.object == self.request.user
|
if (self.object.customer and (self.object == self.request.user
|
||||||
or self.request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
or self.request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||||
or self.request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
|
or self.request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name'] + settings.SITH_BOARD_SUFFIX)
|
||||||
or self.request.user.is_root)):
|
or self.request.user.is_root)):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_stats', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_stats', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'stats',
|
'slug': 'stats',
|
||||||
'name': _("Stats"),
|
'name': _("Stats"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('core:user_account', kwargs={'user_id': self.object.id}),
|
'url': reverse('core:user_account', kwargs={'user_id': self.object.id}),
|
||||||
'slug': 'account',
|
'slug': 'account',
|
||||||
'name': _("Account")+" (%s €)" % self.object.customer.amount,
|
'name': _("Account") + " (%s €)" % self.object.customer.amount,
|
||||||
})
|
})
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return tab_list
|
return tab_list
|
||||||
|
|
||||||
|
|
||||||
class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display a user's profile
|
Display a user's profile
|
||||||
@ -225,8 +236,8 @@ class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
||||||
user = User.objects.get(id=user_id)
|
user = User.objects.get(id=user_id)
|
||||||
if ((user == request.user) or
|
if ((user == request.user) or
|
||||||
request.user.is_root or
|
request.user.is_root or
|
||||||
request.user.is_board_member):
|
request.user.is_board_member):
|
||||||
ud = get_object_or_404(User, id=godfather_id)
|
ud = get_object_or_404(User, id=godfather_id)
|
||||||
if is_father == "True":
|
if is_father == "True":
|
||||||
user.godfathers.remove(ud)
|
user.godfathers.remove(ud)
|
||||||
@ -236,6 +247,7 @@ def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
|||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
return redirect('core:user_godfathers', user_id=user_id)
|
return redirect('core:user_godfathers', user_id=user_id)
|
||||||
|
|
||||||
|
|
||||||
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display a user's pictures
|
Display a user's pictures
|
||||||
@ -246,6 +258,7 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
template_name = "core/user_pictures.jinja"
|
template_name = "core/user_pictures.jinja"
|
||||||
current_tab = 'pictures'
|
current_tab = 'pictures'
|
||||||
|
|
||||||
|
|
||||||
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display a user's godfathers
|
Display a user's godfathers
|
||||||
@ -277,6 +290,7 @@ class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['form'] = UserGodfathersForm()
|
kwargs['form'] = UserGodfathersForm()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display a user's stats
|
Display a user's stats
|
||||||
@ -295,7 +309,7 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
|
|
||||||
if not (profile == request.user
|
if not (profile == request.user
|
||||||
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||||
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
|
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name'] + settings.SITH_BOARD_SUFFIX)
|
||||||
or request.user.is_root):
|
or request.user.is_root):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
@ -303,26 +317,27 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs = super(UserStatsView, self).get_context_data(**kwargs)
|
kwargs = super(UserStatsView, self).get_context_data(**kwargs)
|
||||||
from counter.models import Counter, Product, Selling
|
from counter.models import Counter
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
foyer = Counter.objects.filter(name="Foyer").first()
|
foyer = Counter.objects.filter(name="Foyer").first()
|
||||||
mde = Counter.objects.filter(name="MDE").first()
|
mde = Counter.objects.filter(name="MDE").first()
|
||||||
gommette = Counter.objects.filter(name="La Gommette").first()
|
gommette = Counter.objects.filter(name="La Gommette").first()
|
||||||
semester_start=Subscription.compute_start(d=date.today(), duration=3)
|
semester_start = Subscription.compute_start(d=date.today(), duration=3)
|
||||||
kwargs['total_perm_time'] = sum([p.end-p.start for p in self.object.permanencies.exclude(end=None)], timedelta())
|
kwargs['total_perm_time'] = sum([p.end - p.start for p in self.object.permanencies.exclude(end=None)], timedelta())
|
||||||
kwargs['total_foyer_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=foyer).exclude(end=None)], timedelta())
|
kwargs['total_foyer_time'] = sum([p.end - p.start for p in self.object.permanencies.filter(counter=foyer).exclude(end=None)], timedelta())
|
||||||
kwargs['total_mde_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=mde).exclude(end=None)], timedelta())
|
kwargs['total_mde_time'] = sum([p.end - p.start for p in self.object.permanencies.filter(counter=mde).exclude(end=None)], timedelta())
|
||||||
kwargs['total_gommette_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=gommette).exclude(end=None)], timedelta())
|
kwargs['total_gommette_time'] = sum([p.end - p.start for p in self.object.permanencies.filter(counter=gommette).exclude(end=None)], timedelta())
|
||||||
kwargs['total_foyer_buyings'] = sum([b.unit_price*b.quantity for b in
|
kwargs['total_foyer_buyings'] = sum([b.unit_price * b.quantity for b in
|
||||||
self.object.customer.buyings.filter(counter=foyer, date__gte=semester_start)])
|
self.object.customer.buyings.filter(counter=foyer, date__gte=semester_start)])
|
||||||
kwargs['total_mde_buyings'] = sum([b.unit_price*b.quantity for b in self.object.customer.buyings.filter(counter=mde,
|
kwargs['total_mde_buyings'] = sum([b.unit_price * b.quantity for b in self.object.customer.buyings.filter(counter=mde,
|
||||||
date__gte=semester_start)])
|
date__gte=semester_start)])
|
||||||
kwargs['total_gommette_buyings'] = sum([b.unit_price*b.quantity for b in
|
kwargs['total_gommette_buyings'] = sum([b.unit_price * b.quantity for b in
|
||||||
self.object.customer.buyings.filter(counter=gommette, date__gte=semester_start)])
|
self.object.customer.buyings.filter(counter=gommette, date__gte=semester_start)])
|
||||||
kwargs['top_product'] = self.object.customer.buyings.values('product__name').annotate(
|
kwargs['top_product'] = self.object.customer.buyings.values('product__name').annotate(
|
||||||
product_sum=Sum('quantity')).exclude(product_sum=None).order_by('-product_sum').all()[:10]
|
product_sum=Sum('quantity')).exclude(product_sum=None).order_by('-product_sum').all()[:10]
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class UserMiniView(CanViewMixin, DetailView):
|
class UserMiniView(CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display a user's profile
|
Display a user's profile
|
||||||
@ -332,6 +347,7 @@ class UserMiniView(CanViewMixin, DetailView):
|
|||||||
context_object_name = "profile"
|
context_object_name = "profile"
|
||||||
template_name = "core/user_mini.jinja"
|
template_name = "core/user_mini.jinja"
|
||||||
|
|
||||||
|
|
||||||
class UserListView(ListView, CanEditPropMixin):
|
class UserListView(ListView, CanEditPropMixin):
|
||||||
"""
|
"""
|
||||||
Displays the user list
|
Displays the user list
|
||||||
@ -339,6 +355,7 @@ class UserListView(ListView, CanEditPropMixin):
|
|||||||
model = User
|
model = User
|
||||||
template_name = "core/user_list.jinja"
|
template_name = "core/user_list.jinja"
|
||||||
|
|
||||||
|
|
||||||
class UserUploadProfilePictView(CanEditMixin, DetailView):
|
class UserUploadProfilePictView(CanEditMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Handle the upload of the profile picture taken with webcam in navigator
|
Handle the upload of the profile picture taken with webcam in navigator
|
||||||
@ -356,17 +373,18 @@ class UserUploadProfilePictView(CanEditMixin, DetailView):
|
|||||||
raise ValidationError(_("User already has a profile picture"))
|
raise ValidationError(_("User already has a profile picture"))
|
||||||
f = request.FILES['new_profile_pict']
|
f = request.FILES['new_profile_pict']
|
||||||
parent = SithFile.objects.filter(parent=None, name="profiles").first()
|
parent = SithFile.objects.filter(parent=None, name="profiles").first()
|
||||||
name = str(self.object.id) + "_profile.jpg" # Webcamejs uploads JPGs
|
name = str(self.object.id) + "_profile.jpg" # Webcamejs uploads JPGs
|
||||||
im = Image.open(BytesIO(f.read()))
|
im = Image.open(BytesIO(f.read()))
|
||||||
new_file = SithFile(parent=parent, name=name,
|
new_file = SithFile(parent=parent, name=name,
|
||||||
file=resize_image(im, 400, f.content_type.split('/')[-1]),
|
file=resize_image(im, 400, f.content_type.split('/')[-1]),
|
||||||
owner=self.object, is_folder=False, mime_type=f.content_type, size=f._size)
|
owner=self.object, is_folder=False, mime_type=f.content_type, size=f._size)
|
||||||
new_file.file.name = name
|
new_file.file.name = name
|
||||||
new_file.save()
|
new_file.save()
|
||||||
self.object.profile_pict = new_file
|
self.object.profile_pict = new_file
|
||||||
self.object.save()
|
self.object.save()
|
||||||
return redirect("core:user_edit", user_id=self.object.id)
|
return redirect("core:user_edit", user_id=self.object.id)
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit a user's profile
|
Edit a user's profile
|
||||||
@ -412,6 +430,7 @@ class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
|||||||
kwargs['form'] = self.form
|
kwargs['form'] = self.form
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display the user's club(s)
|
Display the user's club(s)
|
||||||
@ -422,6 +441,7 @@ class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
template_name = "core/user_clubs.jinja"
|
template_name = "core/user_clubs.jinja"
|
||||||
current_tab = "clubs"
|
current_tab = "clubs"
|
||||||
|
|
||||||
|
|
||||||
class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit a user's preferences
|
Edit a user's preferences
|
||||||
@ -453,6 +473,7 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
|||||||
kwargs['trombi_form'] = UserTrombiForm()
|
kwargs['trombi_form'] = UserTrombiForm()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit a user's groups
|
Edit a user's groups
|
||||||
@ -461,10 +482,11 @@ class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
|||||||
pk_url_kwarg = "user_id"
|
pk_url_kwarg = "user_id"
|
||||||
template_name = "core/user_group.jinja"
|
template_name = "core/user_group.jinja"
|
||||||
form_class = modelform_factory(User, fields=['groups'],
|
form_class = modelform_factory(User, fields=['groups'],
|
||||||
widgets={'groups':CheckboxSelectMultiple})
|
widgets={'groups': CheckboxSelectMultiple})
|
||||||
context_object_name = "profile"
|
context_object_name = "profile"
|
||||||
current_tab = "groups"
|
current_tab = "groups"
|
||||||
|
|
||||||
|
|
||||||
class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
|
class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
|
||||||
"""
|
"""
|
||||||
Displays the logged user's tools
|
Displays the logged user's tools
|
||||||
@ -481,6 +503,7 @@ class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
|
|||||||
kwargs['object'] = self.request.user
|
kwargs['object'] = self.request.user
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class UserAccountBase(UserTabsMixin, DetailView):
|
class UserAccountBase(UserTabsMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Base class for UserAccount
|
Base class for UserAccount
|
||||||
@ -489,15 +512,16 @@ class UserAccountBase(UserTabsMixin, DetailView):
|
|||||||
pk_url_kwarg = "user_id"
|
pk_url_kwarg = "user_id"
|
||||||
current_tab = "account"
|
current_tab = "account"
|
||||||
|
|
||||||
def dispatch(self, request, *arg, **kwargs): # Manually validates the rights
|
def dispatch(self, request, *arg, **kwargs): # Manually validates the rights
|
||||||
res = super(UserAccountBase, self).dispatch(request, *arg, **kwargs)
|
res = super(UserAccountBase, self).dispatch(request, *arg, **kwargs)
|
||||||
if (self.object == request.user
|
if (self.object == request.user
|
||||||
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||||
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
|
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name'] + settings.SITH_BOARD_SUFFIX)
|
||||||
or request.user.is_root):
|
or request.user.is_root):
|
||||||
return res
|
return res
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
class UserAccountView(UserAccountBase):
|
class UserAccountView(UserAccountBase):
|
||||||
"""
|
"""
|
||||||
Display a user's account
|
Display a user's account
|
||||||
@ -511,14 +535,14 @@ class UserAccountView(UserAccountBase):
|
|||||||
stats.append([])
|
stats.append([])
|
||||||
i = 0
|
i = 0
|
||||||
for month in obj.filter(date__year=year.year).datetimes(
|
for month in obj.filter(date__year=year.year).datetimes(
|
||||||
'date', 'month', order='DESC'):
|
'date', 'month', order='DESC'):
|
||||||
q = obj.filter(
|
q = obj.filter(
|
||||||
date__year=month.year,
|
date__year=month.year,
|
||||||
date__month=month.month
|
date__month=month.month
|
||||||
)
|
)
|
||||||
stats[i].append({
|
stats[i].append({
|
||||||
'sum':sum([calc(p) for p in q]),
|
'sum': sum([calc(p) for p in q]),
|
||||||
'date':month
|
'date': month
|
||||||
})
|
})
|
||||||
i += 1
|
i += 1
|
||||||
return stats
|
return stats
|
||||||
@ -551,6 +575,7 @@ class UserAccountView(UserAccountBase):
|
|||||||
print(repr(e))
|
print(repr(e))
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
|
class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
|
||||||
"""
|
"""
|
||||||
Display a user's account for month
|
Display a user's account for month
|
||||||
@ -568,4 +593,3 @@ class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
|
|||||||
pass
|
pass
|
||||||
kwargs['tab'] = "account"
|
kwargs['tab'] = "account"
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
@ -36,4 +36,3 @@ admin.site.register(Selling)
|
|||||||
admin.site.register(Permanency)
|
admin.site.register(Permanency)
|
||||||
admin.site.register(CashRegisterSummary)
|
admin.site.register(CashRegisterSummary)
|
||||||
admin.site.register(Eticket)
|
admin.site.register(Eticket)
|
||||||
|
|
||||||
|
@ -22,13 +22,12 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.db import models, DataError
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.contrib.sites.shortcuts import get_current_site
|
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from datetime import timedelta, date
|
from datetime import timedelta, date
|
||||||
@ -43,6 +42,7 @@ from accounting.models import CurrencyField
|
|||||||
from core.models import Group, User, Notification
|
from core.models import Group, User, Notification
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
|
|
||||||
|
|
||||||
class Customer(models.Model):
|
class Customer(models.Model):
|
||||||
"""
|
"""
|
||||||
This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
|
This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
|
||||||
@ -55,7 +55,7 @@ class Customer(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('customer')
|
verbose_name = _('customer')
|
||||||
verbose_name_plural = _('customers')
|
verbose_name_plural = _('customers')
|
||||||
ordering = ['account_id',]
|
ordering = ['account_id', ]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s - %s" % (self.user.username, self.account_id)
|
return "%s - %s" % (self.user.username, self.account_id)
|
||||||
@ -68,9 +68,9 @@ class Customer(models.Model):
|
|||||||
def generate_account_id(number):
|
def generate_account_id(number):
|
||||||
number = str(number)
|
number = str(number)
|
||||||
letter = random.choice(string.ascii_lowercase)
|
letter = random.choice(string.ascii_lowercase)
|
||||||
while Customer.objects.filter(account_id=number+letter).exists():
|
while Customer.objects.filter(account_id=number + letter).exists():
|
||||||
letter = random.choice(string.ascii_lowercase)
|
letter = random.choice(string.ascii_lowercase)
|
||||||
return number+letter
|
return number + letter
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.amount < 0:
|
if self.amount < 0:
|
||||||
@ -118,6 +118,7 @@ class ProductType(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('counter:producttype_list')
|
return reverse('counter:producttype_list')
|
||||||
|
|
||||||
|
|
||||||
class Product(models.Model):
|
class Product(models.Model):
|
||||||
"""
|
"""
|
||||||
This describes a product, with all its related informations
|
This describes a product, with all its related informations
|
||||||
@ -125,7 +126,7 @@ class Product(models.Model):
|
|||||||
name = models.CharField(_('name'), max_length=64)
|
name = models.CharField(_('name'), max_length=64)
|
||||||
description = models.TextField(_('description'), blank=True)
|
description = models.TextField(_('description'), blank=True)
|
||||||
product_type = models.ForeignKey(ProductType, related_name='products', verbose_name=_("product type"), null=True, blank=True,
|
product_type = models.ForeignKey(ProductType, related_name='products', verbose_name=_("product type"), null=True, blank=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
code = models.CharField(_('code'), max_length=16, blank=True)
|
code = models.CharField(_('code'), max_length=16, blank=True)
|
||||||
purchase_price = CurrencyField(_('purchase price'))
|
purchase_price = CurrencyField(_('purchase price'))
|
||||||
selling_price = CurrencyField(_('selling price'))
|
selling_price = CurrencyField(_('selling price'))
|
||||||
@ -135,7 +136,7 @@ class Product(models.Model):
|
|||||||
limit_age = models.IntegerField(_('limit age'), default=0)
|
limit_age = models.IntegerField(_('limit age'), default=0)
|
||||||
tray = models.BooleanField(_('tray price'), default=False)
|
tray = models.BooleanField(_('tray price'), default=False)
|
||||||
parent_product = models.ForeignKey('self', related_name='children_products', verbose_name=_("parent product"), null=True,
|
parent_product = models.ForeignKey('self', related_name='children_products', verbose_name=_("parent product"), null=True,
|
||||||
blank=True, on_delete=models.SET_NULL)
|
blank=True, on_delete=models.SET_NULL)
|
||||||
buying_groups = models.ManyToManyField(Group, related_name='products', verbose_name=_("buying groups"), blank=True)
|
buying_groups = models.ManyToManyField(Group, related_name='products', verbose_name=_("buying groups"), blank=True)
|
||||||
archived = models.BooleanField(_("archived"), default=False)
|
archived = models.BooleanField(_("archived"), default=False)
|
||||||
|
|
||||||
@ -156,13 +157,14 @@ class Product(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('counter:product_list')
|
return reverse('counter:product_list')
|
||||||
|
|
||||||
|
|
||||||
class Counter(models.Model):
|
class Counter(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=30)
|
name = models.CharField(_('name'), max_length=30)
|
||||||
club = models.ForeignKey(Club, related_name="counters", verbose_name=_("club"))
|
club = models.ForeignKey(Club, related_name="counters", verbose_name=_("club"))
|
||||||
products = models.ManyToManyField(Product, related_name="counters", verbose_name=_("products"), blank=True)
|
products = models.ManyToManyField(Product, related_name="counters", verbose_name=_("products"), blank=True)
|
||||||
type = models.CharField(_('counter type'),
|
type = models.CharField(_('counter type'),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
choices=[('BAR',_('Bar')), ('OFFICE',_('Office')), ('EBOUTIC',_('Eboutic'))])
|
choices=[('BAR', _('Bar')), ('OFFICE', _('Office')), ('EBOUTIC', _('Eboutic'))])
|
||||||
sellers = models.ManyToManyField(User, verbose_name=_('sellers'), related_name='counters', blank=True)
|
sellers = models.ManyToManyField(User, verbose_name=_('sellers'), related_name='counters', blank=True)
|
||||||
edit_groups = models.ManyToManyField(Group, related_name="editable_counters", blank=True)
|
edit_groups = models.ManyToManyField(Group, related_name="editable_counters", blank=True)
|
||||||
view_groups = models.ManyToManyField(Group, related_name="viewable_counters", blank=True)
|
view_groups = models.ManyToManyField(Group, related_name="viewable_counters", blank=True)
|
||||||
@ -173,7 +175,7 @@ class Counter(models.Model):
|
|||||||
|
|
||||||
def __getattribute__(self, name):
|
def __getattribute__(self, name):
|
||||||
if name == "edit_groups":
|
if name == "edit_groups":
|
||||||
return Group.objects.filter(name=self.club.unix_name+settings.SITH_BOARD_SUFFIX).all()
|
return Group.objects.filter(name=self.club.unix_name + settings.SITH_BOARD_SUFFIX).all()
|
||||||
return object.__getattribute__(self, name)
|
return object.__getattribute__(self, name)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -248,7 +250,7 @@ class Counter(models.Model):
|
|||||||
Update the barman activity to prevent timeout
|
Update the barman activity to prevent timeout
|
||||||
"""
|
"""
|
||||||
for p in Permanency.objects.filter(counter=self, end=None).all():
|
for p in Permanency.objects.filter(counter=self, end=None).all():
|
||||||
p.save() # Update activity
|
p.save() # Update activity
|
||||||
|
|
||||||
def is_open(self):
|
def is_open(self):
|
||||||
return len(self.barmen_list) > 0
|
return len(self.barmen_list) > 0
|
||||||
@ -265,6 +267,7 @@ class Counter(models.Model):
|
|||||||
"""
|
"""
|
||||||
return [b.id for b in self.get_barmen_list()]
|
return [b.id for b in self.get_barmen_list()]
|
||||||
|
|
||||||
|
|
||||||
class Refilling(models.Model):
|
class Refilling(models.Model):
|
||||||
"""
|
"""
|
||||||
Handle the refilling
|
Handle the refilling
|
||||||
@ -275,9 +278,9 @@ class Refilling(models.Model):
|
|||||||
customer = models.ForeignKey(Customer, related_name="refillings", blank=False)
|
customer = models.ForeignKey(Customer, related_name="refillings", blank=False)
|
||||||
date = models.DateTimeField(_('date'))
|
date = models.DateTimeField(_('date'))
|
||||||
payment_method = models.CharField(_('payment method'), max_length=255,
|
payment_method = models.CharField(_('payment method'), max_length=255,
|
||||||
choices=settings.SITH_COUNTER_PAYMENT_METHOD, default='CASH')
|
choices=settings.SITH_COUNTER_PAYMENT_METHOD, default='CASH')
|
||||||
bank = models.CharField(_('bank'), max_length=255,
|
bank = models.CharField(_('bank'), max_length=255,
|
||||||
choices=settings.SITH_COUNTER_BANK, default='OTHER')
|
choices=settings.SITH_COUNTER_BANK, default='OTHER')
|
||||||
is_validated = models.BooleanField(_('is validated'), default=False)
|
is_validated = models.BooleanField(_('is validated'), default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -303,12 +306,13 @@ class Refilling(models.Model):
|
|||||||
self.customer.save()
|
self.customer.save()
|
||||||
self.is_validated = True
|
self.is_validated = True
|
||||||
Notification(user=self.customer.user, url=reverse('core:user_account_detail',
|
Notification(user=self.customer.user, url=reverse('core:user_account_detail',
|
||||||
kwargs={'user_id': self.customer.user.id, 'year': self.date.year, 'month': self.date.month}),
|
kwargs={'user_id': self.customer.user.id, 'year': self.date.year, 'month': self.date.month}),
|
||||||
param=str(self.amount),
|
param=str(self.amount),
|
||||||
type="REFILLING",
|
type="REFILLING",
|
||||||
).save()
|
).save()
|
||||||
super(Refilling, self).save(*args, **kwargs)
|
super(Refilling, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Selling(models.Model):
|
class Selling(models.Model):
|
||||||
"""
|
"""
|
||||||
Handle the sellings
|
Handle the sellings
|
||||||
@ -323,7 +327,7 @@ class Selling(models.Model):
|
|||||||
customer = models.ForeignKey(Customer, related_name="buyings", null=True, blank=False, on_delete=models.SET_NULL)
|
customer = models.ForeignKey(Customer, related_name="buyings", null=True, blank=False, on_delete=models.SET_NULL)
|
||||||
date = models.DateTimeField(_('date'))
|
date = models.DateTimeField(_('date'))
|
||||||
payment_method = models.CharField(_('payment method'), max_length=255,
|
payment_method = models.CharField(_('payment method'), max_length=255,
|
||||||
choices=[('SITH_ACCOUNT', _('Sith account')), ('CARD', _('Credit card'))], default='SITH_ACCOUNT')
|
choices=[('SITH_ACCOUNT', _('Sith account')), ('CARD', _('Credit card'))], default='SITH_ACCOUNT')
|
||||||
is_validated = models.BooleanField(_('is validated'), default=False)
|
is_validated = models.BooleanField(_('is validated'), default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -331,7 +335,7 @@ class Selling(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
|
return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
|
||||||
self.quantity*self.unit_price, self.customer.user.get_display_name())
|
self.quantity * self.unit_price, self.customer.user.get_display_name())
|
||||||
|
|
||||||
def is_owned_by(self, user):
|
def is_owned_by(self, user):
|
||||||
return user.is_owner(self.counter) and self.payment_method != "CARD"
|
return user.is_owner(self.counter) and self.payment_method != "CARD"
|
||||||
@ -352,13 +356,13 @@ class Selling(models.Model):
|
|||||||
"You bought an eticket for the event %(event)s.\nYou can download it on this page %(url)s."
|
"You bought an eticket for the event %(event)s.\nYou can download it on this page %(url)s."
|
||||||
) % {
|
) % {
|
||||||
'event': event,
|
'event': event,
|
||||||
'url':''.join((
|
'url': ''.join((
|
||||||
'<a href="',
|
'<a href="',
|
||||||
self.customer.get_full_url(),
|
self.customer.get_full_url(),
|
||||||
'">',
|
'">',
|
||||||
self.customer.get_full_url(),
|
self.customer.get_full_url(),
|
||||||
'</a>'
|
'</a>'
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
message_txt = _(
|
message_txt = _(
|
||||||
"You bought an eticket for the event %(event)s.\nYou can download it on this page %(url)s."
|
"You bought an eticket for the event %(event)s.\nYou can download it on this page %(url)s."
|
||||||
@ -384,46 +388,48 @@ class Selling(models.Model):
|
|||||||
if u.was_subscribed:
|
if u.was_subscribed:
|
||||||
if self.product and self.product.id == settings.SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER:
|
if self.product and self.product.id == settings.SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER:
|
||||||
sub = Subscription(
|
sub = Subscription(
|
||||||
member=u,
|
member=u,
|
||||||
subscription_type='un-semestre',
|
subscription_type='un-semestre',
|
||||||
payment_method="EBOUTIC",
|
payment_method="EBOUTIC",
|
||||||
location="EBOUTIC",
|
location="EBOUTIC",
|
||||||
)
|
)
|
||||||
sub.subscription_start = Subscription.compute_start()
|
sub.subscription_start = Subscription.compute_start()
|
||||||
sub.subscription_start = Subscription.compute_start(
|
sub.subscription_start = Subscription.compute_start(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'])
|
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'])
|
||||||
sub.subscription_end = Subscription.compute_end(
|
sub.subscription_end = Subscription.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'],
|
||||||
start=sub.subscription_start)
|
start=sub.subscription_start)
|
||||||
sub.save()
|
sub.save()
|
||||||
elif self.product and self.product.id == settings.SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS:
|
elif self.product and self.product.id == settings.SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS:
|
||||||
u = User.objects.filter(id=self.customer.user.id).first()
|
u = User.objects.filter(id=self.customer.user.id).first()
|
||||||
sub = Subscription(
|
sub = Subscription(
|
||||||
member=u,
|
member=u,
|
||||||
subscription_type='deux-semestres',
|
subscription_type='deux-semestres',
|
||||||
payment_method="EBOUTIC",
|
payment_method="EBOUTIC",
|
||||||
location="EBOUTIC",
|
location="EBOUTIC",
|
||||||
)
|
)
|
||||||
sub.subscription_start = Subscription.compute_start()
|
sub.subscription_start = Subscription.compute_start()
|
||||||
sub.subscription_start = Subscription.compute_start(
|
sub.subscription_start = Subscription.compute_start(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'])
|
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'])
|
||||||
sub.subscription_end = Subscription.compute_end(
|
sub.subscription_end = Subscription.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[sub.subscription_type]['duration'],
|
||||||
start=sub.subscription_start)
|
start=sub.subscription_start)
|
||||||
sub.save()
|
sub.save()
|
||||||
try:
|
try:
|
||||||
if self.product.eticket:
|
if self.product.eticket:
|
||||||
self.send_mail_customer()
|
self.send_mail_customer()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
Notification(
|
Notification(
|
||||||
user=self.customer.user,
|
user=self.customer.user,
|
||||||
url=reverse('core:user_account_detail',
|
url=reverse('core:user_account_detail',
|
||||||
kwargs={'user_id': self.customer.user.id, 'year': self.date.year, 'month': self.date.month}),
|
kwargs={'user_id': self.customer.user.id, 'year': self.date.year, 'month': self.date.month}),
|
||||||
param="%d x %s" % (self.quantity, self.label),
|
param="%d x %s" % (self.quantity, self.label),
|
||||||
type="SELLING",
|
type="SELLING",
|
||||||
).save()
|
).save()
|
||||||
super(Selling, self).save(*args, **kwargs)
|
super(Selling, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Permanency(models.Model):
|
class Permanency(models.Model):
|
||||||
"""
|
"""
|
||||||
This class aims at storing a traceability of who was barman where and when
|
This class aims at storing a traceability of who was barman where and when
|
||||||
@ -439,10 +445,11 @@ class Permanency(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s in %s from %s (last activity: %s) to %s" % (self.user, self.counter,
|
return "%s in %s from %s (last activity: %s) to %s" % (self.user, self.counter,
|
||||||
self.start.strftime("%Y-%m-%d %H:%M:%S"),
|
self.start.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
self.activity.strftime("%Y-%m-%d %H:%M:%S"),
|
self.activity.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
self.end.strftime("%Y-%m-%d %H:%M:%S") if self.end else "",
|
self.end.strftime("%Y-%m-%d %H:%M:%S") if self.end else "",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CashRegisterSummary(models.Model):
|
class CashRegisterSummary(models.Model):
|
||||||
user = models.ForeignKey(User, related_name="cash_summaries", verbose_name=_("user"))
|
user = models.ForeignKey(User, related_name="cash_summaries", verbose_name=_("user"))
|
||||||
@ -515,6 +522,7 @@ class CashRegisterSummary(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('counter:cash_summary_list')
|
return reverse('counter:cash_summary_list')
|
||||||
|
|
||||||
|
|
||||||
class CashRegisterSummaryItem(models.Model):
|
class CashRegisterSummaryItem(models.Model):
|
||||||
cash_summary = models.ForeignKey(CashRegisterSummary, related_name="items", verbose_name=_("cash summary"))
|
cash_summary = models.ForeignKey(CashRegisterSummary, related_name="items", verbose_name=_("cash summary"))
|
||||||
value = CurrencyField(_("value"))
|
value = CurrencyField(_("value"))
|
||||||
@ -524,6 +532,7 @@ class CashRegisterSummaryItem(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("cash register summary item")
|
verbose_name = _("cash register summary item")
|
||||||
|
|
||||||
|
|
||||||
class Eticket(models.Model):
|
class Eticket(models.Model):
|
||||||
"""
|
"""
|
||||||
Eticket can be linked to a product an allows PDF generation
|
Eticket can be linked to a product an allows PDF generation
|
||||||
@ -552,6 +561,6 @@ class Eticket(models.Model):
|
|||||||
return user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
return user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
||||||
|
|
||||||
def get_hash(self, string):
|
def get_hash(self, string):
|
||||||
import hashlib, hmac
|
import hashlib
|
||||||
|
import hmac
|
||||||
return hmac.new(bytes(self.secret, 'utf-8'), bytes(string, 'utf-8'), hashlib.sha1).hexdigest()
|
return hmac.new(bytes(self.secret, 'utf-8'), bytes(string, 'utf-8'), hashlib.sha1).hexdigest()
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from pprint import pprint
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from counter.views import *
|
from counter.views import *
|
||||||
|
|
||||||
|
398
counter/views.py
398
counter/views.py
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||||
@ -31,7 +31,6 @@ from django.views.generic.edit import UpdateView, CreateView, DeleteView, Proces
|
|||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.forms import CheckboxSelectMultiple
|
from django.forms import CheckboxSelectMultiple
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
from django.core.exceptions import PermissionDenied
|
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django import forms
|
from django import forms
|
||||||
@ -43,16 +42,17 @@ import re
|
|||||||
import pytz
|
import pytz
|
||||||
from datetime import date, timedelta, datetime
|
from datetime import date, timedelta, datetime
|
||||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||||
from ajax_select import make_ajax_form, make_ajax_field
|
from ajax_select import make_ajax_field
|
||||||
|
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
from core.views import CanViewMixin, TabedViewMixin
|
||||||
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
|
from core.views.forms import LoginForm, SelectDate, SelectDateTime
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType, \
|
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType, \
|
||||||
CashRegisterSummary, CashRegisterSummaryItem, Eticket, Permanency
|
CashRegisterSummary, CashRegisterSummaryItem, Eticket, Permanency
|
||||||
from accounting.models import CurrencyField
|
from accounting.models import CurrencyField
|
||||||
|
|
||||||
|
|
||||||
class CounterAdminMixin(View):
|
class CounterAdminMixin(View):
|
||||||
"""
|
"""
|
||||||
This view is made to protect counter admin section
|
This view is made to protect counter admin section
|
||||||
@ -72,13 +72,13 @@ class CounterAdminMixin(View):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not (request.user.is_root or self._test_group(request.user)
|
if not (request.user.is_root or self._test_group(request.user)
|
||||||
or self._test_club(request.user)):
|
or self._test_club(request.user)):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
|
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GetUserForm(forms.Form):
|
class GetUserForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
||||||
@ -107,51 +107,57 @@ class GetUserForm(forms.Form):
|
|||||||
cleaned_data['user'] = cus.user
|
cleaned_data['user'] = cus.user
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class RefillForm(forms.ModelForm):
|
class RefillForm(forms.ModelForm):
|
||||||
error_css_class = 'error'
|
error_css_class = 'error'
|
||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class':'focus'}))
|
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class': 'focus'}))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Refilling
|
model = Refilling
|
||||||
fields = ['amount', 'payment_method', 'bank']
|
fields = ['amount', 'payment_method', 'bank']
|
||||||
|
|
||||||
|
|
||||||
class CounterTabsMixin(TabedViewMixin):
|
class CounterTabsMixin(TabedViewMixin):
|
||||||
def get_tabs_title(self):
|
def get_tabs_title(self):
|
||||||
if hasattr(self.object, 'stock_owner') :
|
if hasattr(self.object, 'stock_owner'):
|
||||||
return self.object.stock_owner.counter
|
return self.object.stock_owner.counter
|
||||||
else:
|
else:
|
||||||
return self.object
|
return self.object
|
||||||
|
|
||||||
def get_list_of_tabs(self):
|
def get_list_of_tabs(self):
|
||||||
tab_list = []
|
tab_list = []
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse_lazy('counter:details',
|
'url': reverse_lazy('counter:details',
|
||||||
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id }),
|
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id}),
|
||||||
'slug': 'counter',
|
'slug': 'counter',
|
||||||
'name': _("Counter"),
|
'name': _("Counter"),
|
||||||
})
|
})
|
||||||
if self.object.stock_owner.counter.type if hasattr(self.object, 'stock_owner') else self.object.type == "BAR":
|
if self.object.stock_owner.counter.type if hasattr(self.object, 'stock_owner') else self.object.type == "BAR":
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse_lazy('counter:cash_summary',
|
'url': reverse_lazy('counter:cash_summary',
|
||||||
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id}),
|
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id}),
|
||||||
'slug': 'cash_summary',
|
'slug': 'cash_summary',
|
||||||
'name': _("Cash summary"),
|
'name': _("Cash summary"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse_lazy('counter:last_ops',
|
'url': reverse_lazy('counter:last_ops',
|
||||||
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id}),
|
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id}),
|
||||||
'slug': 'last_ops',
|
'slug': 'last_ops',
|
||||||
'name': _("Last operations"),
|
'name': _("Last operations"),
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse_lazy('stock:take_items',
|
'url': reverse_lazy('stock:take_items',
|
||||||
kwargs={'stock_id': self.object.stock.id if hasattr(self.object, 'stock') else self.object.stock_owner.id}),
|
kwargs={'stock_id': self.object.stock.id if hasattr(self.object, 'stock') else self.object.stock_owner.id}),
|
||||||
'slug': 'take_items_from_stock',
|
'slug': 'take_items_from_stock',
|
||||||
'name': _("Take items from stock"),
|
'name': _("Take items from stock"),
|
||||||
})
|
})
|
||||||
except: pass # The counter just have no stock
|
except:
|
||||||
|
pass # The counter just have no stock
|
||||||
return tab_list
|
return tab_list
|
||||||
|
|
||||||
|
|
||||||
class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, FormMixin):
|
class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, FormMixin):
|
||||||
"""
|
"""
|
||||||
The public (barman) view
|
The public (barman) view
|
||||||
@ -159,15 +165,15 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
|
|||||||
model = Counter
|
model = Counter
|
||||||
template_name = 'counter/counter_main.jinja'
|
template_name = 'counter/counter_main.jinja'
|
||||||
pk_url_kwarg = "counter_id"
|
pk_url_kwarg = "counter_id"
|
||||||
form_class = GetUserForm # Form to enter a client code and get the corresponding user id
|
form_class = GetUserForm # Form to enter a client code and get the corresponding user id
|
||||||
current_tab = "counter"
|
current_tab = "counter"
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
|
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
|
||||||
self.request.session['counter_token'] == self.object.token): # Check the token to avoid the bar to be stolen
|
self.request.session['counter_token'] == self.object.token): # Check the token to avoid the bar to be stolen
|
||||||
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
|
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
|
||||||
kwargs={'counter_id': self.object.id})+'?bad_location')
|
kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||||
return super(CounterMain, self).post(request, *args, **kwargs)
|
return super(CounterMain, self).post(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
@ -180,13 +186,13 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
|
|||||||
kwargs = super(CounterMain, self).get_context_data(**kwargs)
|
kwargs = super(CounterMain, self).get_context_data(**kwargs)
|
||||||
kwargs['login_form'] = LoginForm()
|
kwargs['login_form'] = LoginForm()
|
||||||
kwargs['login_form'].fields['username'].widget.attrs['autofocus'] = True
|
kwargs['login_form'].fields['username'].widget.attrs['autofocus'] = True
|
||||||
kwargs['login_form'].cleaned_data = {} # add_error fails if there are no cleaned_data
|
kwargs['login_form'].cleaned_data = {} # add_error fails if there are no cleaned_data
|
||||||
if "credentials" in self.request.GET:
|
if "credentials" in self.request.GET:
|
||||||
kwargs['login_form'].add_error(None, _("Bad credentials"))
|
kwargs['login_form'].add_error(None, _("Bad credentials"))
|
||||||
if "sellers" in self.request.GET:
|
if "sellers" in self.request.GET:
|
||||||
kwargs['login_form'].add_error(None, _("User is not barman"))
|
kwargs['login_form'].add_error(None, _("User is not barman"))
|
||||||
kwargs['form'] = self.get_form()
|
kwargs['form'] = self.get_form()
|
||||||
kwargs['form'].cleaned_data = {} # same as above
|
kwargs['form'].cleaned_data = {} # same as above
|
||||||
if "bad_location" in self.request.GET:
|
if "bad_location" in self.request.GET:
|
||||||
kwargs['form'].add_error(None, _("Bad location, someone is already logged in somewhere else"))
|
kwargs['form'].add_error(None, _("Bad location, someone is already logged in somewhere else"))
|
||||||
if self.object.type == 'BAR':
|
if self.object.type == 'BAR':
|
||||||
@ -210,6 +216,7 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('counter:click', args=self.args, kwargs=self.kwargs)
|
return reverse_lazy('counter:click', args=self.args, kwargs=self.kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
The click view
|
The click view
|
||||||
@ -228,7 +235,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
raise Http404
|
raise Http404
|
||||||
if obj.type == "BAR":
|
if obj.type == "BAR":
|
||||||
if not ('counter_token' in request.session.keys() and
|
if not ('counter_token' in request.session.keys() and
|
||||||
request.session['counter_token'] == obj.token) or len(obj.get_barmen_list())<1:
|
request.session['counter_token'] == obj.token) or len(obj.get_barmen_list()) < 1:
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
else:
|
else:
|
||||||
if not request.user.is_authenticated():
|
if not request.user.is_authenticated():
|
||||||
@ -237,10 +244,10 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
"""Simple get view"""
|
"""Simple get view"""
|
||||||
if 'basket' not in request.session.keys(): # Init the basket session entry
|
if 'basket' not in request.session.keys(): # Init the basket session entry
|
||||||
request.session['basket'] = {}
|
request.session['basket'] = {}
|
||||||
request.session['basket_total'] = 0
|
request.session['basket_total'] = 0
|
||||||
request.session['not_enough'] = False # Reset every variable
|
request.session['not_enough'] = False # Reset every variable
|
||||||
request.session['too_young'] = False
|
request.session['too_young'] = False
|
||||||
request.session['not_allowed'] = False
|
request.session['not_allowed'] = False
|
||||||
request.session['no_age'] = False
|
request.session['no_age'] = False
|
||||||
@ -248,8 +255,8 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
ret = super(CounterClick, self).get(request, *args, **kwargs)
|
ret = super(CounterClick, self).get(request, *args, **kwargs)
|
||||||
if ((self.object.type != "BAR" and not request.user.is_authenticated()) or
|
if ((self.object.type != "BAR" and not request.user.is_authenticated()) or
|
||||||
(self.object.type == "BAR" and
|
(self.object.type == "BAR" and
|
||||||
len(self.object.get_barmen_list()) < 1)): # Check that at least one barman is logged in
|
len(self.object.get_barmen_list()) < 1)): # Check that at least one barman is logged in
|
||||||
ret = self.cancel(request) # Otherwise, go to main view
|
ret = self.cancel(request) # Otherwise, go to main view
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
@ -258,16 +265,16 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
self.refill_form = None
|
self.refill_form = None
|
||||||
if ((self.object.type != "BAR" and not request.user.is_authenticated()) or
|
if ((self.object.type != "BAR" and not request.user.is_authenticated()) or
|
||||||
(self.object.type == "BAR" and
|
(self.object.type == "BAR" and
|
||||||
len(self.object.get_barmen_list()) < 1)): # Check that at least one barman is logged in
|
len(self.object.get_barmen_list()) < 1)): # Check that at least one barman is logged in
|
||||||
return self.cancel(request)
|
return self.cancel(request)
|
||||||
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
|
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
|
||||||
self.request.session['counter_token'] == self.object.token): # Also check the token to avoid the bar to be stolen
|
self.request.session['counter_token'] == self.object.token): # Also check the token to avoid the bar to be stolen
|
||||||
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
|
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
|
||||||
kwargs={'counter_id': self.object.id})+'?bad_location')
|
kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||||
if 'basket' not in request.session.keys():
|
if 'basket' not in request.session.keys():
|
||||||
request.session['basket'] = {}
|
request.session['basket'] = {}
|
||||||
request.session['basket_total'] = 0
|
request.session['basket_total'] = 0
|
||||||
request.session['not_enough'] = False # Reset every variable
|
request.session['not_enough'] = False # Reset every variable
|
||||||
request.session['too_young'] = False
|
request.session['too_young'] = False
|
||||||
request.session['not_allowed'] = False
|
request.session['not_allowed'] = False
|
||||||
request.session['no_age'] = False
|
request.session['no_age'] = False
|
||||||
@ -312,7 +319,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
|
|
||||||
def sum_basket(self, request):
|
def sum_basket(self, request):
|
||||||
total = 0
|
total = 0
|
||||||
for pid,infos in request.session['basket'].items():
|
for pid, infos in request.session['basket'].items():
|
||||||
total += infos['price'] * infos['qty']
|
total += infos['price'] * infos['qty']
|
||||||
return total / 100
|
return total / 100
|
||||||
|
|
||||||
@ -323,7 +330,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
except:
|
except:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def add_product(self, request, q = 1, p=None):
|
def add_product(self, request, q=1, p=None):
|
||||||
"""
|
"""
|
||||||
Add a product to the basket
|
Add a product to the basket
|
||||||
q is the quantity passed as integer
|
q is the quantity passed as integer
|
||||||
@ -344,12 +351,12 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
if not can_buy:
|
if not can_buy:
|
||||||
request.session['not_allowed'] = True
|
request.session['not_allowed'] = True
|
||||||
return False
|
return False
|
||||||
bq = 0 # Bonus quantity, for trays
|
bq = 0 # Bonus quantity, for trays
|
||||||
if product.tray: # Handle the tray to adjust the quantity q to add and the bonus quantity bq
|
if product.tray: # Handle the tray to adjust the quantity q to add and the bonus quantity bq
|
||||||
total_qty_mod_6 = self.get_total_quantity_for_pid(request, pid) % 6
|
total_qty_mod_6 = self.get_total_quantity_for_pid(request, pid) % 6
|
||||||
bq = int((total_qty_mod_6 + q) / 6) # Integer division
|
bq = int((total_qty_mod_6 + q) / 6) # Integer division
|
||||||
q -= bq
|
q -= bq
|
||||||
if self.customer.amount < (total + round(q*float(price),2)): # Check for enough money
|
if self.customer.amount < (total + round(q * float(price), 2)): # Check for enough money
|
||||||
request.session['not_enough'] = True
|
request.session['not_enough'] = True
|
||||||
return False
|
return False
|
||||||
if product.limit_age >= 18 and not self.customer.user.date_of_birth:
|
if product.limit_age >= 18 and not self.customer.user.date_of_birth:
|
||||||
@ -361,14 +368,14 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
if self.customer.user.is_banned_counter:
|
if self.customer.user.is_banned_counter:
|
||||||
request.session['not_allowed'] = True
|
request.session['not_allowed'] = True
|
||||||
return False
|
return False
|
||||||
if self.customer.user.date_of_birth and self.customer.user.get_age() < product.limit_age: # Check if affordable
|
if self.customer.user.date_of_birth and self.customer.user.get_age() < product.limit_age: # Check if affordable
|
||||||
request.session['too_young'] = True
|
request.session['too_young'] = True
|
||||||
return False
|
return False
|
||||||
if pid in request.session['basket']: # Add if already in basket
|
if pid in request.session['basket']: # Add if already in basket
|
||||||
request.session['basket'][pid]['qty'] += q
|
request.session['basket'][pid]['qty'] += q
|
||||||
request.session['basket'][pid]['bonus_qty'] += bq
|
request.session['basket'][pid]['bonus_qty'] += bq
|
||||||
else: # or create if not
|
else: # or create if not
|
||||||
request.session['basket'][pid] = {'qty': q, 'price': int(price*100), 'bonus_qty': bq}
|
request.session['basket'][pid] = {'qty': q, 'price': int(price * 100), 'bonus_qty': bq}
|
||||||
request.session.modified = True
|
request.session.modified = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -414,7 +421,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
""" Finish the click session, and validate the basket """
|
""" Finish the click session, and validate the basket """
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
request.session['last_basket'] = []
|
request.session['last_basket'] = []
|
||||||
for pid,infos in request.session['basket'].items():
|
for pid, infos in request.session['basket'].items():
|
||||||
# This duplicates code for DB optimization (prevent to load many times the same object)
|
# This duplicates code for DB optimization (prevent to load many times the same object)
|
||||||
p = Product.objects.filter(pk=pid).first()
|
p = Product.objects.filter(pk=pid).first()
|
||||||
if self.is_barman_price():
|
if self.is_barman_price():
|
||||||
@ -423,13 +430,13 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
uprice = p.selling_price
|
uprice = p.selling_price
|
||||||
if uprice * infos['qty'] > self.customer.amount:
|
if uprice * infos['qty'] > self.customer.amount:
|
||||||
raise DataError(_("You have not enough money to buy all the basket"))
|
raise DataError(_("You have not enough money to buy all the basket"))
|
||||||
request.session['last_basket'].append("%d x %s" % (infos['qty']+infos['bonus_qty'], p.name))
|
request.session['last_basket'].append("%d x %s" % (infos['qty'] + infos['bonus_qty'], p.name))
|
||||||
s = Selling(label=p.name, product=p, club=p.club, counter=self.object, unit_price=uprice,
|
s = Selling(label=p.name, product=p, club=p.club, counter=self.object, unit_price=uprice,
|
||||||
quantity=infos['qty'], seller=self.operator, customer=self.customer)
|
quantity=infos['qty'], seller=self.operator, customer=self.customer)
|
||||||
s.save()
|
s.save()
|
||||||
if infos['bonus_qty']:
|
if infos['bonus_qty']:
|
||||||
s = Selling(label=p.name + " (Plateau)", product=p, club=p.club, counter=self.object, unit_price=0,
|
s = Selling(label=p.name + " (Plateau)", product=p, club=p.club, counter=self.object, unit_price=0,
|
||||||
quantity=infos['bonus_qty'], seller=self.operator, customer=self.customer)
|
quantity=infos['bonus_qty'], seller=self.operator, customer=self.customer)
|
||||||
s.save()
|
s.save()
|
||||||
request.session['last_customer'] = self.customer.user.get_display_name()
|
request.session['last_customer'] = self.customer.user.get_display_name()
|
||||||
request.session['last_total'] = "%0.2f" % self.sum_basket(request)
|
request.session['last_total'] = "%0.2f" % self.sum_basket(request)
|
||||||
@ -437,8 +444,8 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
del request.session['basket']
|
del request.session['basket']
|
||||||
request.session.modified = True
|
request.session.modified = True
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'counter_id': self.object.id,
|
'counter_id': self.object.id,
|
||||||
}
|
}
|
||||||
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args, kwargs=kwargs))
|
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args, kwargs=kwargs))
|
||||||
|
|
||||||
def cancel(self, request):
|
def cancel(self, request):
|
||||||
@ -470,6 +477,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['categories'] = ProductType.objects.all()
|
kwargs['categories'] = ProductType.objects.all()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class CounterLogin(RedirectView):
|
class CounterLogin(RedirectView):
|
||||||
"""
|
"""
|
||||||
Handle the login of a barman
|
Handle the login of a barman
|
||||||
@ -477,6 +485,7 @@ class CounterLogin(RedirectView):
|
|||||||
Logged barmen are stored in the Permanency model
|
Logged barmen are stored in the Permanency model
|
||||||
"""
|
"""
|
||||||
permanent = False
|
permanent = False
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Register the logged user as barman for this counter
|
Register the logged user as barman for this counter
|
||||||
@ -499,10 +508,12 @@ class CounterLogin(RedirectView):
|
|||||||
return super(CounterLogin, self).post(request, *args, **kwargs)
|
return super(CounterLogin, self).post(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
return reverse_lazy('counter:details', args=args, kwargs=kwargs)+"?"+'&'.join(self.errors)
|
return reverse_lazy('counter:details', args=args, kwargs=kwargs) + "?" + '&'.join(self.errors)
|
||||||
|
|
||||||
|
|
||||||
class CounterLogout(RedirectView):
|
class CounterLogout(RedirectView):
|
||||||
permanent = False
|
permanent = False
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Unregister the user from the barman
|
Unregister the user from the barman
|
||||||
@ -515,52 +526,54 @@ class CounterLogout(RedirectView):
|
|||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
return reverse_lazy('counter:details', args=args, kwargs=kwargs)
|
return reverse_lazy('counter:details', args=args, kwargs=kwargs)
|
||||||
|
|
||||||
## Counter admin views
|
# Counter admin views
|
||||||
|
|
||||||
|
|
||||||
class CounterAdminTabsMixin(TabedViewMixin):
|
class CounterAdminTabsMixin(TabedViewMixin):
|
||||||
tabs_title = _("Counter administration")
|
tabs_title = _("Counter administration")
|
||||||
list_of_tabs = [
|
list_of_tabs = [
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('stock:list'),
|
'url': reverse_lazy('stock:list'),
|
||||||
'slug': 'stocks',
|
'slug': 'stocks',
|
||||||
'name': _("Stocks"),
|
'name': _("Stocks"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('counter:admin_list'),
|
'url': reverse_lazy('counter:admin_list'),
|
||||||
'slug': 'counters',
|
'slug': 'counters',
|
||||||
'name': _("Counters"),
|
'name': _("Counters"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('counter:product_list'),
|
'url': reverse_lazy('counter:product_list'),
|
||||||
'slug': 'products',
|
'slug': 'products',
|
||||||
'name': _("Products"),
|
'name': _("Products"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('counter:product_list_archived'),
|
'url': reverse_lazy('counter:product_list_archived'),
|
||||||
'slug': 'archive',
|
'slug': 'archive',
|
||||||
'name': _("Archived products"),
|
'name': _("Archived products"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('counter:producttype_list'),
|
'url': reverse_lazy('counter:producttype_list'),
|
||||||
'slug': 'product_types',
|
'slug': 'product_types',
|
||||||
'name': _("Product types"),
|
'name': _("Product types"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('counter:cash_summary_list'),
|
'url': reverse_lazy('counter:cash_summary_list'),
|
||||||
'slug': 'cash_summary',
|
'slug': 'cash_summary',
|
||||||
'name': _("Cash register summaries"),
|
'name': _("Cash register summaries"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('counter:invoices_call'),
|
'url': reverse_lazy('counter:invoices_call'),
|
||||||
'slug': 'invoices_call',
|
'slug': 'invoices_call',
|
||||||
'name': _("Invoices call"),
|
'name': _("Invoices call"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': reverse_lazy('counter:eticket_list'),
|
'url': reverse_lazy('counter:eticket_list'),
|
||||||
'slug': 'etickets',
|
'slug': 'etickets',
|
||||||
'name': _("Etickets"),
|
'name': _("Etickets"),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
||||||
"""
|
"""
|
||||||
@ -570,6 +583,7 @@ class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
|||||||
template_name = 'counter/counter_list.jinja'
|
template_name = 'counter/counter_list.jinja'
|
||||||
current_tab = "counters"
|
current_tab = "counters"
|
||||||
|
|
||||||
|
|
||||||
class CounterEditForm(forms.ModelForm):
|
class CounterEditForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Counter
|
model = Counter
|
||||||
@ -577,6 +591,7 @@ class CounterEditForm(forms.ModelForm):
|
|||||||
sellers = make_ajax_field(Counter, 'sellers', 'users', help_text="")
|
sellers = make_ajax_field(Counter, 'sellers', 'users', help_text="")
|
||||||
products = make_ajax_field(Counter, 'products', 'products', help_text="")
|
products = make_ajax_field(Counter, 'products', 'products', help_text="")
|
||||||
|
|
||||||
|
|
||||||
class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit a counter's main informations (for the counter's manager)
|
Edit a counter's main informations (for the counter's manager)
|
||||||
@ -595,6 +610,7 @@ class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('counter:admin', kwargs={'counter_id': self.object.id})
|
return reverse_lazy('counter:admin', kwargs={'counter_id': self.object.id})
|
||||||
|
|
||||||
|
|
||||||
class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit a counter's main informations (for the counter's admin)
|
Edit a counter's main informations (for the counter's admin)
|
||||||
@ -605,16 +621,18 @@ class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
|||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
current_tab = "counters"
|
current_tab = "counters"
|
||||||
|
|
||||||
|
|
||||||
class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create a counter (for the admins)
|
Create a counter (for the admins)
|
||||||
"""
|
"""
|
||||||
model = Counter
|
model = Counter
|
||||||
form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'products'],
|
form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'products'],
|
||||||
widgets={'products':CheckboxSelectMultiple})
|
widgets={'products': CheckboxSelectMultiple})
|
||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
current_tab = "counters"
|
current_tab = "counters"
|
||||||
|
|
||||||
|
|
||||||
class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
||||||
"""
|
"""
|
||||||
Delete a counter (for the admins)
|
Delete a counter (for the admins)
|
||||||
@ -627,6 +645,7 @@ class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
|||||||
|
|
||||||
# Product management
|
# Product management
|
||||||
|
|
||||||
|
|
||||||
class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||||
"""
|
"""
|
||||||
A list view for the admins
|
A list view for the admins
|
||||||
@ -635,6 +654,7 @@ class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
|||||||
template_name = 'counter/producttype_list.jinja'
|
template_name = 'counter/producttype_list.jinja'
|
||||||
current_tab = "product_types"
|
current_tab = "product_types"
|
||||||
|
|
||||||
|
|
||||||
class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
A create view for the admins
|
A create view for the admins
|
||||||
@ -644,6 +664,7 @@ class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView
|
|||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
current_tab = "products"
|
current_tab = "products"
|
||||||
|
|
||||||
|
|
||||||
class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
An edit view for the admins
|
An edit view for the admins
|
||||||
@ -654,6 +675,7 @@ class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
|||||||
pk_url_kwarg = "type_id"
|
pk_url_kwarg = "type_id"
|
||||||
current_tab = "products"
|
current_tab = "products"
|
||||||
|
|
||||||
|
|
||||||
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||||
"""
|
"""
|
||||||
A list view for the admins
|
A list view for the admins
|
||||||
@ -664,6 +686,7 @@ class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView
|
|||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
current_tab = "archive"
|
current_tab = "archive"
|
||||||
|
|
||||||
|
|
||||||
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||||
"""
|
"""
|
||||||
A list view for the admins
|
A list view for the admins
|
||||||
@ -674,11 +697,12 @@ class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
|||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
current_tab = "products"
|
current_tab = "products"
|
||||||
|
|
||||||
|
|
||||||
class ProductEditForm(forms.ModelForm):
|
class ProductEditForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Product
|
model = Product
|
||||||
fields = ['name', 'description', 'product_type', 'code', 'parent_product', 'buying_groups', 'purchase_price',
|
fields = ['name', 'description', 'product_type', 'code', 'parent_product', 'buying_groups', 'purchase_price',
|
||||||
'selling_price', 'special_selling_price', 'icon', 'club', 'limit_age', 'tray', 'archived']
|
'selling_price', 'special_selling_price', 'icon', 'club', 'limit_age', 'tray', 'archived']
|
||||||
parent_product = AutoCompleteSelectField('products', show_help_text=False, label=_("Parent product"), required=False)
|
parent_product = AutoCompleteSelectField('products', show_help_text=False, label=_("Parent product"), required=False)
|
||||||
buying_groups = AutoCompleteSelectMultipleField('groups', show_help_text=False, help_text="", label=_("Buying groups"), required=False)
|
buying_groups = AutoCompleteSelectMultipleField('groups', show_help_text=False, help_text="", label=_("Buying groups"), required=False)
|
||||||
club = AutoCompleteSelectField('clubs', show_help_text=False)
|
club = AutoCompleteSelectField('clubs', show_help_text=False)
|
||||||
@ -702,6 +726,7 @@ class ProductEditForm(forms.ModelForm):
|
|||||||
c.save()
|
c.save()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
A create view for the admins
|
A create view for the admins
|
||||||
@ -711,6 +736,7 @@ class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
|||||||
template_name = 'core/create.jinja'
|
template_name = 'core/create.jinja'
|
||||||
current_tab = "products"
|
current_tab = "products"
|
||||||
|
|
||||||
|
|
||||||
class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
An edit view for the admins
|
An edit view for the admins
|
||||||
@ -721,6 +747,7 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
|||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
current_tab = "products"
|
current_tab = "products"
|
||||||
|
|
||||||
|
|
||||||
class RefillingDeleteView(DeleteView):
|
class RefillingDeleteView(DeleteView):
|
||||||
"""
|
"""
|
||||||
Delete a refilling (for the admins)
|
Delete a refilling (for the admins)
|
||||||
@ -736,7 +763,7 @@ class RefillingDeleteView(DeleteView):
|
|||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
if (timezone.now() - self.object.date <= timedelta(minutes=settings.SITH_LAST_OPERATIONS_LIMIT) and
|
if (timezone.now() - self.object.date <= timedelta(minutes=settings.SITH_LAST_OPERATIONS_LIMIT) and
|
||||||
'counter_token' in request.session.keys() and
|
'counter_token' in request.session.keys() and
|
||||||
request.session['counter_token'] and # check if not null for counters that have no token set
|
request.session['counter_token'] and # check if not null for counters that have no token set
|
||||||
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
||||||
self.success_url = reverse('counter:details', kwargs={'counter_id': self.object.counter.id})
|
self.success_url = reverse('counter:details', kwargs={'counter_id': self.object.counter.id})
|
||||||
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
|
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
|
||||||
@ -745,6 +772,7 @@ class RefillingDeleteView(DeleteView):
|
|||||||
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
|
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
class SellingDeleteView(DeleteView):
|
class SellingDeleteView(DeleteView):
|
||||||
"""
|
"""
|
||||||
Delete a selling (for the admins)
|
Delete a selling (for the admins)
|
||||||
@ -760,7 +788,7 @@ class SellingDeleteView(DeleteView):
|
|||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
if (timezone.now() - self.object.date <= timedelta(minutes=settings.SITH_LAST_OPERATIONS_LIMIT) and
|
if (timezone.now() - self.object.date <= timedelta(minutes=settings.SITH_LAST_OPERATIONS_LIMIT) and
|
||||||
'counter_token' in request.session.keys() and
|
'counter_token' in request.session.keys() and
|
||||||
request.session['counter_token'] and # check if not null for counters that have no token set
|
request.session['counter_token'] and # check if not null for counters that have no token set
|
||||||
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
||||||
self.success_url = reverse('counter:details', kwargs={'counter_id': self.object.counter.id})
|
self.success_url = reverse('counter:details', kwargs={'counter_id': self.object.counter.id})
|
||||||
return super(SellingDeleteView, self).dispatch(request, *args, **kwargs)
|
return super(SellingDeleteView, self).dispatch(request, *args, **kwargs)
|
||||||
@ -771,6 +799,7 @@ class SellingDeleteView(DeleteView):
|
|||||||
|
|
||||||
# Cash register summaries
|
# Cash register summaries
|
||||||
|
|
||||||
|
|
||||||
class CashRegisterSummaryForm(forms.Form):
|
class CashRegisterSummaryForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
Provide the cash summary form
|
Provide the cash summary form
|
||||||
@ -831,38 +860,54 @@ class CashRegisterSummaryForm(forms.Form):
|
|||||||
def save(self, counter=None):
|
def save(self, counter=None):
|
||||||
cd = self.cleaned_data
|
cd = self.cleaned_data
|
||||||
summary = self.instance or CashRegisterSummary(
|
summary = self.instance or CashRegisterSummary(
|
||||||
counter=counter,
|
counter=counter,
|
||||||
user=counter.get_random_barman(),
|
user=counter.get_random_barman(),
|
||||||
)
|
)
|
||||||
summary.comment = cd['comment']
|
summary.comment = cd['comment']
|
||||||
summary.emptied = cd['emptied']
|
summary.emptied = cd['emptied']
|
||||||
summary.save()
|
summary.save()
|
||||||
summary.items.all().delete()
|
summary.items.all().delete()
|
||||||
# Cash
|
# Cash
|
||||||
if cd['ten_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
|
if cd['ten_cents']:
|
||||||
if cd['twenty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
|
CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
|
||||||
if cd['fifty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
|
if cd['twenty_cents']:
|
||||||
if cd['one_euro']: CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
|
CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
|
||||||
if cd['two_euros']: CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
|
if cd['fifty_cents']:
|
||||||
if cd['five_euros']: CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
|
CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
|
||||||
if cd['ten_euros']: CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
|
if cd['one_euro']:
|
||||||
if cd['twenty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
|
CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
|
||||||
if cd['fifty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
|
if cd['two_euros']:
|
||||||
if cd['hundred_euros']: CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
|
CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
|
||||||
|
if cd['five_euros']:
|
||||||
|
CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
|
||||||
|
if cd['ten_euros']:
|
||||||
|
CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
|
||||||
|
if cd['twenty_euros']:
|
||||||
|
CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
|
||||||
|
if cd['fifty_euros']:
|
||||||
|
CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
|
||||||
|
if cd['hundred_euros']:
|
||||||
|
CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
|
||||||
# Checks
|
# Checks
|
||||||
if cd['check_1_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
|
if cd['check_1_quantity']:
|
||||||
quantity=cd['check_1_quantity'], check=True).save()
|
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
|
||||||
if cd['check_2_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
|
quantity=cd['check_1_quantity'], check=True).save()
|
||||||
quantity=cd['check_2_quantity'], check=True).save()
|
if cd['check_2_quantity']:
|
||||||
if cd['check_3_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
|
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
|
||||||
quantity=cd['check_3_quantity'], check=True).save()
|
quantity=cd['check_2_quantity'], check=True).save()
|
||||||
if cd['check_4_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
|
if cd['check_3_quantity']:
|
||||||
quantity=cd['check_4_quantity'], check=True).save()
|
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
|
||||||
if cd['check_5_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
|
quantity=cd['check_3_quantity'], check=True).save()
|
||||||
quantity=cd['check_5_quantity'], check=True).save()
|
if cd['check_4_quantity']:
|
||||||
|
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
|
||||||
|
quantity=cd['check_4_quantity'], check=True).save()
|
||||||
|
if cd['check_5_quantity']:
|
||||||
|
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
|
||||||
|
quantity=cd['check_5_quantity'], check=True).save()
|
||||||
if summary.items.count() < 1:
|
if summary.items.count() < 1:
|
||||||
summary.delete()
|
summary.delete()
|
||||||
|
|
||||||
|
|
||||||
class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Provide the last operations to allow barmen to delete them
|
Provide the last operations to allow barmen to delete them
|
||||||
@ -878,10 +923,10 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
"""
|
"""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
if (self.object.get_barmen_list() and 'counter_token' in request.session.keys() and
|
if (self.object.get_barmen_list() and 'counter_token' in request.session.keys() and
|
||||||
request.session['counter_token'] and # check if not null for counters that have no token set
|
request.session['counter_token'] and # check if not null for counters that have no token set
|
||||||
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
||||||
return super(CounterLastOperationsView, self).dispatch(request, *args, **kwargs)
|
return super(CounterLastOperationsView, self).dispatch(request, *args, **kwargs)
|
||||||
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id})+'?bad_location')
|
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""Add form to the context """
|
"""Add form to the context """
|
||||||
@ -891,6 +936,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['last_sellings'] = self.object.sellings.filter(date__gte=threshold).order_by('-id')[:20]
|
kwargs['last_sellings'] = self.object.sellings.filter(date__gte=threshold).order_by('-id')[:20]
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Provide the cash summary form
|
Provide the cash summary form
|
||||||
@ -906,10 +952,10 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
"""
|
"""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
if (self.object.get_barmen_list() and 'counter_token' in request.session.keys() and
|
if (self.object.get_barmen_list() and 'counter_token' in request.session.keys() and
|
||||||
request.session['counter_token'] and # check if not null for counters that have no token set
|
request.session['counter_token'] and # check if not null for counters that have no token set
|
||||||
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
||||||
return super(CounterCashSummaryView, self).dispatch(request, *args, **kwargs)
|
return super(CounterCashSummaryView, self).dispatch(request, *args, **kwargs)
|
||||||
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id})+'?bad_location')
|
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
@ -933,6 +979,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
kwargs['form'] = self.form
|
kwargs['form'] = self.form
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class CounterActivityView(DetailView):
|
class CounterActivityView(DetailView):
|
||||||
"""
|
"""
|
||||||
Show the bar activity
|
Show the bar activity
|
||||||
@ -941,6 +988,7 @@ class CounterActivityView(DetailView):
|
|||||||
pk_url_kwarg = "counter_id"
|
pk_url_kwarg = "counter_id"
|
||||||
template_name = 'counter/activity.jinja'
|
template_name = 'counter/activity.jinja'
|
||||||
|
|
||||||
|
|
||||||
class CounterStatView(DetailView, CounterAdminMixin):
|
class CounterStatView(DetailView, CounterAdminMixin):
|
||||||
"""
|
"""
|
||||||
Show the bar stats
|
Show the bar stats
|
||||||
@ -957,40 +1005,40 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
|||||||
kwargs['User'] = User
|
kwargs['User'] = User
|
||||||
semester_start = Subscription.compute_start(d=date.today(), duration=3)
|
semester_start = Subscription.compute_start(d=date.today(), duration=3)
|
||||||
kwargs['total_sellings'] = Selling.objects.filter(date__gte=semester_start,
|
kwargs['total_sellings'] = Selling.objects.filter(date__gte=semester_start,
|
||||||
counter=self.object).aggregate(total_sellings=Sum(F('quantity')*F('unit_price'),
|
counter=self.object).aggregate(total_sellings=Sum(F('quantity') * F('unit_price'),
|
||||||
output_field=CurrencyField()))['total_sellings']
|
output_field=CurrencyField()))['total_sellings']
|
||||||
kwargs['top'] = Selling.objects.values('customer__user').annotate(
|
kwargs['top'] = Selling.objects.values('customer__user').annotate(
|
||||||
selling_sum=Sum(
|
selling_sum=Sum(
|
||||||
Case(When(counter=self.object,
|
Case(When(counter=self.object,
|
||||||
date__gte=semester_start,
|
date__gte=semester_start,
|
||||||
unit_price__gt=0,
|
unit_price__gt=0,
|
||||||
then=F('unit_price')*F('quantity')),
|
then=F('unit_price') * F('quantity')),
|
||||||
output_field=CurrencyField()
|
output_field=CurrencyField()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).exclude(selling_sum=None).order_by('-selling_sum').all()[:100]
|
).exclude(selling_sum=None).order_by('-selling_sum').all()[:100]
|
||||||
kwargs['top_barman'] = Permanency.objects.values('user').annotate(
|
kwargs['top_barman'] = Permanency.objects.values('user').annotate(
|
||||||
perm_sum=Sum(
|
perm_sum=Sum(
|
||||||
Case(When(counter=self.object,
|
Case(When(counter=self.object,
|
||||||
end__gt=datetime(year=1999, month=1, day=1),
|
end__gt=datetime(year=1999, month=1, day=1),
|
||||||
then=F('end')-F('start')),
|
then=F('end') - F('start')),
|
||||||
output_field=models.DateTimeField()
|
output_field=models.DateTimeField()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).exclude(perm_sum=None).order_by('-perm_sum').all()[:100]
|
).exclude(perm_sum=None).order_by('-perm_sum').all()[:100]
|
||||||
kwargs['top_barman_semester'] = Permanency.objects.values('user').annotate(
|
kwargs['top_barman_semester'] = Permanency.objects.values('user').annotate(
|
||||||
perm_sum=Sum(
|
perm_sum=Sum(
|
||||||
Case(When(counter=self.object,
|
Case(When(counter=self.object,
|
||||||
start__gt=semester_start,
|
start__gt=semester_start,
|
||||||
end__gt=datetime(year=1999, month=1, day=1),
|
end__gt=datetime(year=1999, month=1, day=1),
|
||||||
then=F('end')-F('start')),
|
then=F('end') - F('start')),
|
||||||
output_field=models.DateTimeField()
|
output_field=models.DateTimeField()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).exclude(perm_sum=None).order_by('-perm_sum').all()[:100]
|
).exclude(perm_sum=None).order_by('-perm_sum').all()[:100]
|
||||||
|
|
||||||
kwargs['sith_date']=settings.SITH_START_DATE[0]
|
kwargs['sith_date'] = settings.SITH_START_DATE[0]
|
||||||
kwargs['semester_start']=semester_start
|
kwargs['semester_start'] = semester_start
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
@ -999,11 +1047,12 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
|||||||
except:
|
except:
|
||||||
if (request.user.is_root
|
if (request.user.is_root
|
||||||
or request.user.is_board_member
|
or request.user.is_board_member
|
||||||
or self.object.is_owned_by(request.user)):
|
or self.object.is_owned_by(request.user)):
|
||||||
return super(CanEditMixin, self).dispatch(request, *args, **kwargs)
|
return super(CanEditMixin, self).dispatch(request, *args, **kwargs)
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
|
||||||
|
class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||||
"""Edit cash summaries"""
|
"""Edit cash summaries"""
|
||||||
model = CashRegisterSummary
|
model = CashRegisterSummary
|
||||||
template_name = 'counter/cash_register_summary.jinja'
|
template_name = 'counter/cash_register_summary.jinja'
|
||||||
@ -1015,10 +1064,12 @@ class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView)
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('counter:cash_summary_list')
|
return reverse('counter:cash_summary_list')
|
||||||
|
|
||||||
|
|
||||||
class CashSummaryFormBase(forms.Form):
|
class CashSummaryFormBase(forms.Form):
|
||||||
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
||||||
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
||||||
|
|
||||||
|
|
||||||
class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||||
"""Display a list of cash summaries"""
|
"""Display a list of cash summaries"""
|
||||||
model = CashRegisterSummary
|
model = CashRegisterSummary
|
||||||
@ -1047,7 +1098,7 @@ class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
|||||||
refillings = refillings.filter(date__gt=last_summary.date)
|
refillings = refillings.filter(date__gt=last_summary.date)
|
||||||
cashredistersummaries = cashredistersummaries.filter(date__gt=last_summary.date)
|
cashredistersummaries = cashredistersummaries.filter(date__gt=last_summary.date)
|
||||||
else:
|
else:
|
||||||
refillings = refillings.filter(date__gte=datetime(year=1994, month=5, day=17, tzinfo=pytz.UTC)) # My birth date should be old enough
|
refillings = refillings.filter(date__gte=datetime(year=1994, month=5, day=17, tzinfo=pytz.UTC)) # My birth date should be old enough
|
||||||
cashredistersummaries = cashredistersummaries.filter(date__gte=datetime(year=1994, month=5, day=17, tzinfo=pytz.UTC))
|
cashredistersummaries = cashredistersummaries.filter(date__gte=datetime(year=1994, month=5, day=17, tzinfo=pytz.UTC))
|
||||||
if form.is_valid() and form.cleaned_data['end_date']:
|
if form.is_valid() and form.cleaned_data['end_date']:
|
||||||
refillings = refillings.filter(date__lte=form.cleaned_data['end_date'])
|
refillings = refillings.filter(date__lte=form.cleaned_data['end_date'])
|
||||||
@ -1056,6 +1107,7 @@ class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
|||||||
kwargs['refilling_sums'][c.name] = sum([s.amount for s in refillings.all()])
|
kwargs['refilling_sums'][c.name] = sum([s.amount for s in refillings.all()])
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
||||||
template_name = 'counter/invoices_call.jinja'
|
template_name = 'counter/invoices_call.jinja'
|
||||||
current_tab = 'invoices_call'
|
current_tab = 'invoices_call'
|
||||||
@ -1069,24 +1121,25 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
|||||||
try:
|
try:
|
||||||
start_date = datetime.strptime(self.request.GET['month'], '%Y-%m')
|
start_date = datetime.strptime(self.request.GET['month'], '%Y-%m')
|
||||||
except:
|
except:
|
||||||
start_date = datetime(year=timezone.now().year, month=(timezone.now().month+10)%12+1, day=1)
|
start_date = datetime(year=timezone.now().year, month=(timezone.now().month + 10) % 12 + 1, day=1)
|
||||||
start_date = start_date.replace(tzinfo=pytz.UTC)
|
start_date = start_date.replace(tzinfo=pytz.UTC)
|
||||||
end_date = (start_date + timedelta(days=32)).replace(day=1, hour=0, minute=0, microsecond=0)
|
end_date = (start_date + timedelta(days=32)).replace(day=1, hour=0, minute=0, microsecond=0)
|
||||||
from django.db.models import Sum, Case, When, F, DecimalField
|
from django.db.models import Sum, Case, When, F, DecimalField
|
||||||
kwargs['sum_cb'] = sum([r.amount for r in Refilling.objects.filter(payment_method='CARD', is_validated=True,
|
kwargs['sum_cb'] = sum([r.amount for r in Refilling.objects.filter(payment_method='CARD', is_validated=True,
|
||||||
date__gte=start_date, date__lte=end_date)])
|
date__gte=start_date, date__lte=end_date)])
|
||||||
kwargs['sum_cb'] += sum([s.quantity*s.unit_price for s in Selling.objects.filter(payment_method='CARD', is_validated=True,
|
kwargs['sum_cb'] += sum([s.quantity * s.unit_price for s in Selling.objects.filter(payment_method='CARD', is_validated=True,
|
||||||
date__gte=start_date, date__lte=end_date)])
|
date__gte=start_date, date__lte=end_date)])
|
||||||
kwargs['start_date'] = start_date
|
kwargs['start_date'] = start_date
|
||||||
kwargs['sums'] = Selling.objects.values('club__name').annotate(selling_sum=Sum(
|
kwargs['sums'] = Selling.objects.values('club__name').annotate(selling_sum=Sum(
|
||||||
Case(When(date__gte=start_date,
|
Case(When(date__gte=start_date,
|
||||||
date__lt=end_date,
|
date__lt=end_date,
|
||||||
then=F('unit_price')*F('quantity')),
|
then=F('unit_price') * F('quantity')),
|
||||||
output_field=CurrencyField()
|
output_field=CurrencyField()
|
||||||
)
|
)
|
||||||
)).exclude(selling_sum=None).order_by('-selling_sum')
|
)).exclude(selling_sum=None).order_by('-selling_sum')
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||||
"""
|
"""
|
||||||
A list view for the admins
|
A list view for the admins
|
||||||
@ -1096,15 +1149,17 @@ class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
|||||||
ordering = ['id']
|
ordering = ['id']
|
||||||
current_tab = "etickets"
|
current_tab = "etickets"
|
||||||
|
|
||||||
|
|
||||||
class EticketForm(forms.ModelForm):
|
class EticketForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Eticket
|
model = Eticket
|
||||||
fields = ['product', 'banner', 'event_title', 'event_date']
|
fields = ['product', 'banner', 'event_title', 'event_date']
|
||||||
widgets = {
|
widgets = {
|
||||||
'event_date': SelectDate,
|
'event_date': SelectDate,
|
||||||
}
|
}
|
||||||
product = AutoCompleteSelectField('products', show_help_text=False, label=_("Product"), required=True)
|
product = AutoCompleteSelectField('products', show_help_text=False, label=_("Product"), required=True)
|
||||||
|
|
||||||
|
|
||||||
class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create an eticket
|
Create an eticket
|
||||||
@ -1114,6 +1169,7 @@ class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
|||||||
form_class = EticketForm
|
form_class = EticketForm
|
||||||
current_tab = "etickets"
|
current_tab = "etickets"
|
||||||
|
|
||||||
|
|
||||||
class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
Edit an eticket
|
Edit an eticket
|
||||||
@ -1124,6 +1180,7 @@ class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
|||||||
pk_url_kwarg = "eticket_id"
|
pk_url_kwarg = "eticket_id"
|
||||||
current_tab = "etickets"
|
current_tab = "etickets"
|
||||||
|
|
||||||
|
|
||||||
class EticketPDFView(CanViewMixin, DetailView):
|
class EticketPDFView(CanViewMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Display the PDF of an eticket
|
Display the PDF of an eticket
|
||||||
@ -1172,7 +1229,7 @@ class EticketPDFView(CanViewMixin, DetailView):
|
|||||||
p.drawCentredString(10.5 * cm, 23.6 * cm, eticket.event_title)
|
p.drawCentredString(10.5 * cm, 23.6 * cm, eticket.event_title)
|
||||||
if eticket.event_date:
|
if eticket.event_date:
|
||||||
p.setFont("Helvetica-Bold", 16)
|
p.setFont("Helvetica-Bold", 16)
|
||||||
p.drawCentredString(10.5 * cm, 22.6 * cm, eticket.event_date.strftime("%d %b %Y")) # FIXME with a locale
|
p.drawCentredString(10.5 * cm, 22.6 * cm, eticket.event_date.strftime("%d %b %Y")) # FIXME with a locale
|
||||||
p.setFont("Helvetica-Bold", 14)
|
p.setFont("Helvetica-Bold", 14)
|
||||||
p.drawCentredString(10.5 * cm, 15 * cm, "%s : %d %s" % (user.get_display_name(), self.object.quantity, str(_("people(s)"))))
|
p.drawCentredString(10.5 * cm, 15 * cm, "%s : %d %s" % (user.get_display_name(), self.object.quantity, str(_("people(s)"))))
|
||||||
p.setFont("Courier-Bold", 14)
|
p.setFont("Courier-Bold", 14)
|
||||||
@ -1180,7 +1237,7 @@ class EticketPDFView(CanViewMixin, DetailView):
|
|||||||
bounds = qrcode.getBounds()
|
bounds = qrcode.getBounds()
|
||||||
width = bounds[2] - bounds[0]
|
width = bounds[2] - bounds[0]
|
||||||
height = bounds[3] - bounds[1]
|
height = bounds[3] - bounds[1]
|
||||||
d = Drawing(260, 260, transform=[260./width, 0, 0, 260./height, 0, 0])
|
d = Drawing(260, 260, transform=[260. / width, 0, 0, 260. / height, 0, 0])
|
||||||
d.add(qrcode)
|
d.add(qrcode)
|
||||||
renderPDF.draw(d, p, 10.5 * cm - 130, 6.1 * cm)
|
renderPDF.draw(d, p, 10.5 * cm - 130, 6.1 * cm)
|
||||||
p.drawCentredString(10.5 * cm, 6 * cm, code)
|
p.drawCentredString(10.5 * cm, 6 * cm, code)
|
||||||
@ -1195,4 +1252,3 @@ class EticketPDFView(CanViewMixin, DetailView):
|
|||||||
p.showPage()
|
p.showPage()
|
||||||
p.save()
|
p.save()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from accounting.models import CurrencyField
|
from accounting.models import CurrencyField
|
||||||
from counter.models import Counter, Product, Customer, Selling, Refilling
|
from counter.models import Counter, Product, Selling, Refilling
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from subscription.models import Subscription
|
|
||||||
|
|
||||||
class Basket(models.Model):
|
class Basket(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -38,16 +38,16 @@ class Basket(models.Model):
|
|||||||
user = models.ForeignKey(User, related_name='baskets', verbose_name=_('user'), blank=False)
|
user = models.ForeignKey(User, related_name='baskets', verbose_name=_('user'), blank=False)
|
||||||
date = models.DateTimeField(_('date'), auto_now=True)
|
date = models.DateTimeField(_('date'), auto_now=True)
|
||||||
|
|
||||||
def add_product(self, p, q = 1):
|
def add_product(self, p, q=1):
|
||||||
item = self.items.filter(product_id=p.id).first()
|
item = self.items.filter(product_id=p.id).first()
|
||||||
if item is None:
|
if item is None:
|
||||||
BasketItem(basket=self, product_id=p.id, product_name=p.name, type_id=p.product_type.id,
|
BasketItem(basket=self, product_id=p.id, product_name=p.name, type_id=p.product_type.id,
|
||||||
quantity=q, product_unit_price=p.selling_price).save()
|
quantity=q, product_unit_price=p.selling_price).save()
|
||||||
else:
|
else:
|
||||||
item.quantity += q
|
item.quantity += q
|
||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
def del_product(self, p, q = 1):
|
def del_product(self, p, q=1):
|
||||||
item = self.items.filter(product_id=p.id).first()
|
item = self.items.filter(product_id=p.id).first()
|
||||||
if item is not None:
|
if item is not None:
|
||||||
item.quantity -= q
|
item.quantity -= q
|
||||||
@ -64,6 +64,7 @@ class Basket(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s's basket (%d items)" % (self.user, self.items.all().count())
|
return "%s's basket (%d items)" % (self.user, self.items.all().count())
|
||||||
|
|
||||||
|
|
||||||
class Invoice(models.Model):
|
class Invoice(models.Model):
|
||||||
"""
|
"""
|
||||||
Invoices are generated once the payment has been validated
|
Invoices are generated once the payment has been validated
|
||||||
@ -92,34 +93,35 @@ class Invoice(models.Model):
|
|||||||
for i in self.items.all():
|
for i in self.items.all():
|
||||||
if i.type_id == settings.SITH_COUNTER_PRODUCTTYPE_REFILLING:
|
if i.type_id == settings.SITH_COUNTER_PRODUCTTYPE_REFILLING:
|
||||||
new = Refilling(
|
new = Refilling(
|
||||||
counter=eboutic,
|
counter=eboutic,
|
||||||
customer=self.user.customer,
|
customer=self.user.customer,
|
||||||
operator=self.user,
|
operator=self.user,
|
||||||
amount=i.product_unit_price * i.quantity,
|
amount=i.product_unit_price * i.quantity,
|
||||||
payment_method="CARD",
|
payment_method="CARD",
|
||||||
bank="OTHER",
|
bank="OTHER",
|
||||||
date=self.date,
|
date=self.date,
|
||||||
)
|
)
|
||||||
new.save()
|
new.save()
|
||||||
else:
|
else:
|
||||||
product = Product.objects.filter(id=i.product_id).first()
|
product = Product.objects.filter(id=i.product_id).first()
|
||||||
new = Selling(
|
new = Selling(
|
||||||
label=i.product_name,
|
label=i.product_name,
|
||||||
counter=eboutic,
|
counter=eboutic,
|
||||||
club=product.club,
|
club=product.club,
|
||||||
product=product,
|
product=product,
|
||||||
seller=self.user,
|
seller=self.user,
|
||||||
customer=self.user.customer,
|
customer=self.user.customer,
|
||||||
unit_price=i.product_unit_price,
|
unit_price=i.product_unit_price,
|
||||||
quantity=i.quantity,
|
quantity=i.quantity,
|
||||||
payment_method="CARD",
|
payment_method="CARD",
|
||||||
is_validated=True,
|
is_validated=True,
|
||||||
date=self.date,
|
date=self.date,
|
||||||
)
|
)
|
||||||
new.save()
|
new.save()
|
||||||
self.validated = True
|
self.validated = True
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
class AbstractBaseItem(models.Model):
|
class AbstractBaseItem(models.Model):
|
||||||
product_id = models.IntegerField(_('product id'))
|
product_id = models.IntegerField(_('product id'))
|
||||||
product_name = models.CharField(_('product name'), max_length=255)
|
product_name = models.CharField(_('product name'), max_length=255)
|
||||||
@ -133,8 +135,10 @@ class AbstractBaseItem(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Item: %s (%s) x%d" % (self.product_name, self.product_unit_price, self.quantity)
|
return "Item: %s (%s) x%d" % (self.product_name, self.product_unit_price, self.quantity)
|
||||||
|
|
||||||
|
|
||||||
class BasketItem(AbstractBaseItem):
|
class BasketItem(AbstractBaseItem):
|
||||||
basket = models.ForeignKey(Basket, related_name='items', verbose_name=_('basket'))
|
basket = models.ForeignKey(Basket, related_name='items', verbose_name=_('basket'))
|
||||||
|
|
||||||
|
|
||||||
class InvoiceItem(AbstractBaseItem):
|
class InvoiceItem(AbstractBaseItem):
|
||||||
invoice = models.ForeignKey(Invoice, related_name='items', verbose_name=_('invoice'))
|
invoice = models.ForeignKey(Invoice, related_name='items', verbose_name=_('invoice'))
|
||||||
|
@ -34,7 +34,8 @@ from django.core.management import call_command
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from counter.models import Customer, ProductType, Product, Counter, Refilling
|
from counter.models import Product, Counter, Refilling
|
||||||
|
|
||||||
|
|
||||||
class EbouticTest(TestCase):
|
class EbouticTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -74,25 +75,24 @@ class EbouticTest(TestCase):
|
|||||||
"action": "add_product",
|
"action": "add_product",
|
||||||
"product_id": self.barbar.id})
|
"product_id": self.barbar.id})
|
||||||
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
||||||
" <button type=\"submit\" name=\"product_id\" value=\"4\"> + </button>\\n"
|
" <button type=\"submit\" name=\"product_id\" value=\"4\"> + </button>\\n"
|
||||||
"</form>\\n Barbar: 1.70 \\xe2\\x82\\xac</li>" in str(response.content))
|
"</form>\\n Barbar: 1.70 \\xe2\\x82\\xac</li>" in str(response.content))
|
||||||
response = self.client.post(reverse("eboutic:command"))
|
response = self.client.post(reverse("eboutic:command"))
|
||||||
self.assertTrue("<tr>\\n <td>Barbar</td>\\n <td>1</td>\\n"
|
self.assertTrue("<tr>\\n <td>Barbar</td>\\n <td>1</td>\\n"
|
||||||
" <td>1.70 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
" <td>1.70 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
||||||
response = self.client.post(reverse("eboutic:pay_with_sith"), {
|
response = self.client.post(reverse("eboutic:pay_with_sith"), {
|
||||||
"action": "pay_with_sith_account"
|
"action": "pay_with_sith_account"
|
||||||
})
|
})
|
||||||
self.assertTrue("Le paiement a \\xc3\\xa9t\\xc3\\xa9 effectu\\xc3\\xa9\\n" in str(response.content))
|
self.assertTrue("Le paiement a \\xc3\\xa9t\\xc3\\xa9 effectu\\xc3\\xa9\\n" in str(response.content))
|
||||||
response = self.client.get(reverse("core:user_account_detail", kwargs={
|
response = self.client.get(reverse("core:user_account_detail", kwargs={
|
||||||
"user_id": self.subscriber.id,
|
"user_id": self.subscriber.id,
|
||||||
"year": datetime.now().year,
|
"year": datetime.now().year,
|
||||||
"month": datetime.now().month,
|
"month": datetime.now().month,
|
||||||
}))
|
}))
|
||||||
self.assertTrue("class=\"selected_tab\">Compte (8.30 \\xe2\\x82\\xac)</a>" in str(response.content))
|
self.assertTrue("class=\"selected_tab\">Compte (8.30 \\xe2\\x82\\xac)</a>" in str(response.content))
|
||||||
self.assertTrue("<td>Eboutic</td>\\n <td><a href=\"/user/3/\">Subscribed User</a></td>\\n"
|
self.assertTrue("<td>Eboutic</td>\\n <td><a href=\"/user/3/\">Subscribed User</a></td>\\n"
|
||||||
" <td>Barbar</td>\\n <td>1</td>\\n <td>1.70 \\xe2\\x82\\xac</td>\\n"
|
" <td>Barbar</td>\\n <td>1</td>\\n <td>1.70 \\xe2\\x82\\xac</td>\\n"
|
||||||
" <td>Compte utilisateur</td>" in str(response.content))
|
" <td>Compte utilisateur</td>" in str(response.content))
|
||||||
|
|
||||||
|
|
||||||
def test_buy_simple_product_with_credit_card(self):
|
def test_buy_simple_product_with_credit_card(self):
|
||||||
self.client.login(username='subscriber', password='plop')
|
self.client.login(username='subscriber', password='plop')
|
||||||
@ -100,11 +100,11 @@ class EbouticTest(TestCase):
|
|||||||
"action": "add_product",
|
"action": "add_product",
|
||||||
"product_id": self.barbar.id})
|
"product_id": self.barbar.id})
|
||||||
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
||||||
" <button type=\"submit\" name=\"product_id\" value=\"4\"> + </button>\\n"
|
" <button type=\"submit\" name=\"product_id\" value=\"4\"> + </button>\\n"
|
||||||
"</form>\\n Barbar: 1.70 \\xe2\\x82\\xac</li>" in str(response.content))
|
"</form>\\n Barbar: 1.70 \\xe2\\x82\\xac</li>" in str(response.content))
|
||||||
response = self.client.post(reverse("eboutic:command"))
|
response = self.client.post(reverse("eboutic:command"))
|
||||||
self.assertTrue("<tr>\\n <td>Barbar</td>\\n <td>1</td>\\n"
|
self.assertTrue("<tr>\\n <td>Barbar</td>\\n <td>1</td>\\n"
|
||||||
" <td>1.70 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
" <td>1.70 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
||||||
|
|
||||||
response = self.generate_bank_valid_answer_from_page_content(response.content)
|
response = self.generate_bank_valid_answer_from_page_content(response.content)
|
||||||
|
|
||||||
@ -112,11 +112,11 @@ class EbouticTest(TestCase):
|
|||||||
"user_id": self.subscriber.id,
|
"user_id": self.subscriber.id,
|
||||||
"year": datetime.now().year,
|
"year": datetime.now().year,
|
||||||
"month": datetime.now().month,
|
"month": datetime.now().month,
|
||||||
}))
|
}))
|
||||||
self.assertTrue("class=\"selected_tab\">Compte (0.00 \\xe2\\x82\\xac)</a>" in str(response.content))
|
self.assertTrue("class=\"selected_tab\">Compte (0.00 \\xe2\\x82\\xac)</a>" in str(response.content))
|
||||||
self.assertTrue("<td>Eboutic</td>\\n <td><a href=\"/user/3/\">Subscribed User</a></td>\\n"
|
self.assertTrue("<td>Eboutic</td>\\n <td><a href=\"/user/3/\">Subscribed User</a></td>\\n"
|
||||||
" <td>Barbar</td>\\n <td>1</td>\\n <td>1.70 \\xe2\\x82\\xac</td>\\n"
|
" <td>Barbar</td>\\n <td>1</td>\\n <td>1.70 \\xe2\\x82\\xac</td>\\n"
|
||||||
" <td>Carte bancaire</td>" in str(response.content))
|
" <td>Carte bancaire</td>" in str(response.content))
|
||||||
|
|
||||||
def test_buy_refill_product_with_credit_card(self):
|
def test_buy_refill_product_with_credit_card(self):
|
||||||
self.client.login(username='subscriber', password='plop')
|
self.client.login(username='subscriber', password='plop')
|
||||||
@ -124,11 +124,11 @@ class EbouticTest(TestCase):
|
|||||||
"action": "add_product",
|
"action": "add_product",
|
||||||
"product_id": self.refill.id})
|
"product_id": self.refill.id})
|
||||||
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
||||||
" <button type=\"submit\" name=\"product_id\" value=\"3\"> + </button>\\n"
|
" <button type=\"submit\" name=\"product_id\" value=\"3\"> + </button>\\n"
|
||||||
"</form>\\n Rechargement 15 \\xe2\\x82\\xac: 15.00 \\xe2\\x82\\xac</li>" in str(response.content))
|
"</form>\\n Rechargement 15 \\xe2\\x82\\xac: 15.00 \\xe2\\x82\\xac</li>" in str(response.content))
|
||||||
response = self.client.post(reverse("eboutic:command"))
|
response = self.client.post(reverse("eboutic:command"))
|
||||||
self.assertTrue("<tr>\\n <td>Rechargement 15 \\xe2\\x82\\xac</td>\\n <td>1</td>\\n"
|
self.assertTrue("<tr>\\n <td>Rechargement 15 \\xe2\\x82\\xac</td>\\n <td>1</td>\\n"
|
||||||
" <td>15.00 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
" <td>15.00 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
||||||
|
|
||||||
response = self.generate_bank_valid_answer_from_page_content(response.content)
|
response = self.generate_bank_valid_answer_from_page_content(response.content)
|
||||||
|
|
||||||
@ -136,12 +136,12 @@ class EbouticTest(TestCase):
|
|||||||
"user_id": self.subscriber.id,
|
"user_id": self.subscriber.id,
|
||||||
"year": datetime.now().year,
|
"year": datetime.now().year,
|
||||||
"month": datetime.now().month,
|
"month": datetime.now().month,
|
||||||
}))
|
}))
|
||||||
self.assertTrue("class=\"selected_tab\">Compte (15.00 \\xe2\\x82\\xac)</a>" in str(response.content))
|
self.assertTrue("class=\"selected_tab\">Compte (15.00 \\xe2\\x82\\xac)</a>" in str(response.content))
|
||||||
self.assertTrue("<td>\\n <ul>\\n \\n "
|
self.assertTrue("<td>\\n <ul>\\n \\n "
|
||||||
"<li>1 x Rechargement 15 \\xe2\\x82\\xac - 15.00 \\xe2\\x82\\xac</li>\\n"
|
"<li>1 x Rechargement 15 \\xe2\\x82\\xac - 15.00 \\xe2\\x82\\xac</li>\\n"
|
||||||
" \\n </ul>\\n </td>\\n"
|
" \\n </ul>\\n </td>\\n"
|
||||||
" <td>15.00 \\xe2\\x82\\xac</td>" in str(response.content))
|
" <td>15.00 \\xe2\\x82\\xac</td>" in str(response.content))
|
||||||
|
|
||||||
def test_buy_subscribe_product_with_credit_card(self):
|
def test_buy_subscribe_product_with_credit_card(self):
|
||||||
self.client.login(username='old_subscriber', password='plop')
|
self.client.login(username='old_subscriber', password='plop')
|
||||||
@ -151,11 +151,11 @@ class EbouticTest(TestCase):
|
|||||||
"action": "add_product",
|
"action": "add_product",
|
||||||
"product_id": self.cotis.id})
|
"product_id": self.cotis.id})
|
||||||
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
self.assertTrue("<input type=\"hidden\" name=\"action\" value=\"add_product\">\\n"
|
||||||
" <button type=\"submit\" name=\"product_id\" value=\"1\"> + </button>\\n"
|
" <button type=\"submit\" name=\"product_id\" value=\"1\"> + </button>\\n"
|
||||||
"</form>\\n Cotis 1 semestre: 15.00 \\xe2\\x82\\xac</li>" in str(response.content))
|
"</form>\\n Cotis 1 semestre: 15.00 \\xe2\\x82\\xac</li>" in str(response.content))
|
||||||
response = self.client.post(reverse("eboutic:command"))
|
response = self.client.post(reverse("eboutic:command"))
|
||||||
self.assertTrue("<tr>\\n <td>Cotis 1 semestre</td>\\n <td>1</td>\\n"
|
self.assertTrue("<tr>\\n <td>Cotis 1 semestre</td>\\n <td>1</td>\\n"
|
||||||
" <td>15.00 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
" <td>15.00 \\xe2\\x82\\xac</td>\\n </tr>" in str(response.content))
|
||||||
|
|
||||||
response = self.generate_bank_valid_answer_from_page_content(response.content)
|
response = self.generate_bank_valid_answer_from_page_content(response.content)
|
||||||
|
|
||||||
@ -163,14 +163,11 @@ class EbouticTest(TestCase):
|
|||||||
"user_id": self.old_subscriber.id,
|
"user_id": self.old_subscriber.id,
|
||||||
"year": datetime.now().year,
|
"year": datetime.now().year,
|
||||||
"month": datetime.now().month,
|
"month": datetime.now().month,
|
||||||
}))
|
}))
|
||||||
self.assertTrue("class=\"selected_tab\">Compte (0.00 \\xe2\\x82\\xac)</a>" in str(response.content))
|
self.assertTrue("class=\"selected_tab\">Compte (0.00 \\xe2\\x82\\xac)</a>" in str(response.content))
|
||||||
self.assertTrue("<td>\\n <ul>\\n \\n "
|
self.assertTrue("<td>\\n <ul>\\n \\n "
|
||||||
"<li>1 x Cotis 1 semestre - 15.00 \\xe2\\x82\\xac</li>\\n"
|
"<li>1 x Cotis 1 semestre - 15.00 \\xe2\\x82\\xac</li>\\n"
|
||||||
" \\n </ul>\\n </td>\\n"
|
" \\n </ul>\\n </td>\\n"
|
||||||
" <td>15.00 \\xe2\\x82\\xac</td>" in str(response.content))
|
" <td>15.00 \\xe2\\x82\\xac</td>" in str(response.content))
|
||||||
response = self.client.get(reverse("core:user_profile", kwargs={"user_id": self.old_subscriber.id}))
|
response = self.client.get(reverse("core:user_profile", kwargs={"user_id": self.old_subscriber.id}))
|
||||||
self.assertTrue("Cotisant jusqu\\'au" in str(response.content))
|
self.assertTrue("Cotisant jusqu\\'au" in str(response.content))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from eboutic.views import *
|
from eboutic.views import *
|
||||||
|
|
||||||
@ -33,6 +33,3 @@ urlpatterns = [
|
|||||||
url(r'^pay$', EbouticPayWithSith.as_view(), name='pay_with_sith'),
|
url(r'^pay$', EbouticPayWithSith.as_view(), name='pay_with_sith'),
|
||||||
url(r'^et_autoanswer$', EtransactionAutoAnswer.as_view(), name='etransation_autoanswer'),
|
url(r'^et_autoanswer$', EtransactionAutoAnswer.as_view(), name='etransation_autoanswer'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,29 +24,27 @@
|
|||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pytz
|
|
||||||
import hmac
|
import hmac
|
||||||
import base64
|
import base64
|
||||||
from OpenSSL import crypto
|
from OpenSSL import crypto
|
||||||
|
|
||||||
from django.shortcuts import render
|
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.views.generic import TemplateView, View
|
from django.views.generic import TemplateView, View
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.shortcuts import render
|
|
||||||
from django.db import transaction, DataError
|
from django.db import transaction, DataError
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from counter.models import Customer, Counter, ProductType, Selling
|
from counter.models import Customer, Counter, ProductType, Selling
|
||||||
from eboutic.models import Basket, Invoice, BasketItem, InvoiceItem
|
from eboutic.models import Basket, Invoice, InvoiceItem
|
||||||
|
|
||||||
|
|
||||||
class EbouticMain(TemplateView):
|
class EbouticMain(TemplateView):
|
||||||
template_name = 'eboutic/eboutic_main.jinja'
|
template_name = 'eboutic/eboutic_main.jinja'
|
||||||
|
|
||||||
def make_basket(self, request):
|
def make_basket(self, request):
|
||||||
if 'basket_id' not in request.session.keys(): # Init the basket session entry
|
if 'basket_id' not in request.session.keys(): # Init the basket session entry
|
||||||
self.basket = Basket(user=request.user)
|
self.basket = Basket(user=request.user)
|
||||||
self.basket.save()
|
self.basket.save()
|
||||||
else:
|
else:
|
||||||
@ -60,7 +58,7 @@ class EbouticMain(TemplateView):
|
|||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if not request.user.is_authenticated():
|
if not request.user.is_authenticated():
|
||||||
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
||||||
request.path)
|
request.path)
|
||||||
self.object = Counter.objects.filter(type="EBOUTIC").first()
|
self.object = Counter.objects.filter(type="EBOUTIC").first()
|
||||||
self.make_basket(request)
|
self.make_basket(request)
|
||||||
return super(EbouticMain, self).get(request, *args, **kwargs)
|
return super(EbouticMain, self).get(request, *args, **kwargs)
|
||||||
@ -68,7 +66,7 @@ class EbouticMain(TemplateView):
|
|||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if not request.user.is_authenticated():
|
if not request.user.is_authenticated():
|
||||||
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
||||||
request.path)
|
request.path)
|
||||||
self.object = Counter.objects.filter(type="EBOUTIC").first()
|
self.object = Counter.objects.filter(type="EBOUTIC").first()
|
||||||
self.make_basket(request)
|
self.make_basket(request)
|
||||||
if 'add_product' in request.POST['action']:
|
if 'add_product' in request.POST['action']:
|
||||||
@ -77,7 +75,6 @@ class EbouticMain(TemplateView):
|
|||||||
self.del_product(request)
|
self.del_product(request)
|
||||||
return self.render_to_response(self.get_context_data(**kwargs))
|
return self.render_to_response(self.get_context_data(**kwargs))
|
||||||
|
|
||||||
|
|
||||||
def add_product(self, request):
|
def add_product(self, request):
|
||||||
""" Add a product to the basket """
|
""" Add a product to the basket """
|
||||||
try:
|
try:
|
||||||
@ -108,19 +105,20 @@ class EbouticMain(TemplateView):
|
|||||||
kwargs['categories'] = kwargs['categories'].exclude(id=settings.SITH_PRODUCTTYPE_SUBSCRIPTION)
|
kwargs['categories'] = kwargs['categories'].exclude(id=settings.SITH_PRODUCTTYPE_SUBSCRIPTION)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class EbouticCommand(TemplateView):
|
class EbouticCommand(TemplateView):
|
||||||
template_name = 'eboutic/eboutic_makecommand.jinja'
|
template_name = 'eboutic/eboutic_makecommand.jinja'
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if not request.user.is_authenticated():
|
if not request.user.is_authenticated():
|
||||||
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
||||||
request.path)
|
request.path)
|
||||||
return HttpResponseRedirect(reverse_lazy('eboutic:main', args=self.args, kwargs=kwargs))
|
return HttpResponseRedirect(reverse_lazy('eboutic:main', args=self.args, kwargs=kwargs))
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if not request.user.is_authenticated():
|
if not request.user.is_authenticated():
|
||||||
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
return HttpResponseRedirect(reverse_lazy('core:login', args=self.args, kwargs=kwargs) + "?next=" +
|
||||||
request.path)
|
request.path)
|
||||||
if 'basket_id' not in request.session.keys():
|
if 'basket_id' not in request.session.keys():
|
||||||
return HttpResponseRedirect(reverse_lazy('eboutic:main', args=self.args, kwargs=kwargs))
|
return HttpResponseRedirect(reverse_lazy('eboutic:main', args=self.args, kwargs=kwargs))
|
||||||
self.basket = Basket.objects.filter(id=request.session['basket_id']).first()
|
self.basket = Basket.objects.filter(id=request.session['basket_id']).first()
|
||||||
@ -136,8 +134,8 @@ class EbouticCommand(TemplateView):
|
|||||||
kwargs['et_request']['PBX_SITE'] = settings.SITH_EBOUTIC_PBX_SITE
|
kwargs['et_request']['PBX_SITE'] = settings.SITH_EBOUTIC_PBX_SITE
|
||||||
kwargs['et_request']['PBX_RANG'] = settings.SITH_EBOUTIC_PBX_RANG
|
kwargs['et_request']['PBX_RANG'] = settings.SITH_EBOUTIC_PBX_RANG
|
||||||
kwargs['et_request']['PBX_IDENTIFIANT'] = settings.SITH_EBOUTIC_PBX_IDENTIFIANT
|
kwargs['et_request']['PBX_IDENTIFIANT'] = settings.SITH_EBOUTIC_PBX_IDENTIFIANT
|
||||||
kwargs['et_request']['PBX_TOTAL'] = int(self.basket.get_total()*100)
|
kwargs['et_request']['PBX_TOTAL'] = int(self.basket.get_total() * 100)
|
||||||
kwargs['et_request']['PBX_DEVISE'] = 978 # This is Euro. ET support only this value anyway
|
kwargs['et_request']['PBX_DEVISE'] = 978 # This is Euro. ET support only this value anyway
|
||||||
kwargs['et_request']['PBX_CMD'] = self.basket.id
|
kwargs['et_request']['PBX_CMD'] = self.basket.id
|
||||||
kwargs['et_request']['PBX_PORTEUR'] = self.basket.user.email
|
kwargs['et_request']['PBX_PORTEUR'] = self.basket.user.email
|
||||||
kwargs['et_request']['PBX_RETOUR'] = "Amount:M;BasketID:R;Auto:A;Error:E;Sig:K"
|
kwargs['et_request']['PBX_RETOUR'] = "Amount:M;BasketID:R;Auto:A;Error:E;Sig:K"
|
||||||
@ -146,10 +144,11 @@ class EbouticCommand(TemplateView):
|
|||||||
kwargs['et_request']['PBX_TYPECARTE'] = "CB"
|
kwargs['et_request']['PBX_TYPECARTE'] = "CB"
|
||||||
kwargs['et_request']['PBX_TIME'] = str(datetime.now().replace(microsecond=0).isoformat('T'))
|
kwargs['et_request']['PBX_TIME'] = str(datetime.now().replace(microsecond=0).isoformat('T'))
|
||||||
kwargs['et_request']['PBX_HMAC'] = hmac.new(settings.SITH_EBOUTIC_HMAC_KEY,
|
kwargs['et_request']['PBX_HMAC'] = hmac.new(settings.SITH_EBOUTIC_HMAC_KEY,
|
||||||
bytes("&".join(["%s=%s"%(k,v) for k,v in kwargs['et_request'].items()]), 'utf-8'),
|
bytes("&".join(["%s=%s" % (k, v) for k, v in kwargs['et_request'].items()]), 'utf-8'),
|
||||||
"sha512").hexdigest().upper()
|
"sha512").hexdigest().upper()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class EbouticPayWithSith(TemplateView):
|
class EbouticPayWithSith(TemplateView):
|
||||||
template_name = 'eboutic/eboutic_payment_result.jinja'
|
template_name = 'eboutic/eboutic_payment_result.jinja'
|
||||||
|
|
||||||
@ -172,30 +171,31 @@ class EbouticPayWithSith(TemplateView):
|
|||||||
for it in b.items.all():
|
for it in b.items.all():
|
||||||
product = eboutic.products.filter(id=it.product_id).first()
|
product = eboutic.products.filter(id=it.product_id).first()
|
||||||
Selling(
|
Selling(
|
||||||
label=it.product_name,
|
label=it.product_name,
|
||||||
counter=eboutic,
|
counter=eboutic,
|
||||||
club=product.club,
|
club=product.club,
|
||||||
product=product,
|
product=product,
|
||||||
seller=c.user,
|
seller=c.user,
|
||||||
customer=c,
|
customer=c,
|
||||||
unit_price=it.product_unit_price,
|
unit_price=it.product_unit_price,
|
||||||
quantity=it.quantity,
|
quantity=it.quantity,
|
||||||
payment_method="SITH_ACCOUNT",
|
payment_method="SITH_ACCOUNT",
|
||||||
).save()
|
).save()
|
||||||
b.delete()
|
b.delete()
|
||||||
kwargs['not_enough'] = False
|
kwargs['not_enough'] = False
|
||||||
request.session.pop('basket_id', None)
|
request.session.pop('basket_id', None)
|
||||||
except DataError as e:
|
except DataError as e:
|
||||||
kwargs['not_enough'] = True
|
kwargs['not_enough'] = True
|
||||||
return self.render_to_response(self.get_context_data(**kwargs))
|
return self.render_to_response(self.get_context_data(**kwargs))
|
||||||
|
|
||||||
|
|
||||||
class EtransactionAutoAnswer(View):
|
class EtransactionAutoAnswer(View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if (not 'Amount' in request.GET.keys() or
|
if (not 'Amount' in request.GET.keys() or
|
||||||
not 'BasketID' in request.GET.keys() or
|
not 'BasketID' in request.GET.keys() or
|
||||||
not 'Auto' in request.GET.keys() or
|
not 'Auto' in request.GET.keys() or
|
||||||
not 'Error' in request.GET.keys() or
|
not 'Error' in request.GET.keys() or
|
||||||
not 'Sig' in request.GET.keys()):
|
not 'Sig' in request.GET.keys()):
|
||||||
return HttpResponse("Bad arguments", status=400)
|
return HttpResponse("Bad arguments", status=400)
|
||||||
key = crypto.load_publickey(crypto.FILETYPE_PEM, settings.SITH_EBOUTIC_PUB_KEY)
|
key = crypto.load_publickey(crypto.FILETYPE_PEM, settings.SITH_EBOUTIC_PUB_KEY)
|
||||||
cert = crypto.X509()
|
cert = crypto.X509()
|
||||||
@ -217,12 +217,11 @@ class EtransactionAutoAnswer(View):
|
|||||||
i.save()
|
i.save()
|
||||||
for it in b.items.all():
|
for it in b.items.all():
|
||||||
InvoiceItem(invoice=i, product_id=it.product_id, product_name=it.product_name, type_id=it.type_id,
|
InvoiceItem(invoice=i, product_id=it.product_id, product_name=it.product_name, type_id=it.type_id,
|
||||||
product_unit_price=it.product_unit_price, quantity=it.quantity).save()
|
product_unit_price=it.product_unit_price, quantity=it.quantity).save()
|
||||||
i.validate()
|
i.validate()
|
||||||
b.delete()
|
b.delete()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return HttpResponse("Payment failed with error: "+repr(e), status=400)
|
return HttpResponse("Payment failed with error: " + repr(e), status=400)
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
else:
|
else:
|
||||||
return HttpResponse("Payment failed with error: "+request.GET['Error'], status=400)
|
return HttpResponse("Payment failed with error: " + request.GET['Error'], status=400)
|
||||||
|
|
||||||
|
@ -23,11 +23,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core import validators
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import IntegrityError, transaction
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
@ -35,9 +33,10 @@ from django.utils.functional import cached_property
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from core.models import User, MetaGroup, Group, SithFile
|
from core.models import User, Group
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
|
|
||||||
|
|
||||||
class Forum(models.Model):
|
class Forum(models.Model):
|
||||||
"""
|
"""
|
||||||
The Forum class, made as a tree to allow nice tidy organization
|
The Forum class, made as a tree to allow nice tidy organization
|
||||||
@ -52,14 +51,14 @@ class Forum(models.Model):
|
|||||||
is_category = models.BooleanField(_('is a category'), default=False)
|
is_category = models.BooleanField(_('is a category'), default=False)
|
||||||
parent = models.ForeignKey('Forum', related_name='children', null=True, blank=True)
|
parent = models.ForeignKey('Forum', related_name='children', null=True, blank=True)
|
||||||
owner_club = models.ForeignKey(Club, related_name="owned_forums", verbose_name=_("owner club"),
|
owner_club = models.ForeignKey(Club, related_name="owned_forums", verbose_name=_("owner club"),
|
||||||
default=settings.SITH_MAIN_CLUB_ID)
|
default=settings.SITH_MAIN_CLUB_ID)
|
||||||
edit_groups = models.ManyToManyField(Group, related_name="editable_forums", blank=True,
|
edit_groups = models.ManyToManyField(Group, related_name="editable_forums", blank=True,
|
||||||
default=[settings.SITH_GROUP_OLD_SUBSCRIBERS_ID])
|
default=[settings.SITH_GROUP_OLD_SUBSCRIBERS_ID])
|
||||||
view_groups = models.ManyToManyField(Group, related_name="viewable_forums", blank=True,
|
view_groups = models.ManyToManyField(Group, related_name="viewable_forums", blank=True,
|
||||||
default=[settings.SITH_GROUP_PUBLIC_ID])
|
default=[settings.SITH_GROUP_PUBLIC_ID])
|
||||||
number = models.IntegerField(_("number to choose a specific forum ordering"), default=1)
|
number = models.IntegerField(_("number to choose a specific forum ordering"), default=1)
|
||||||
_last_message = models.ForeignKey('ForumMessage', related_name="forums_where_its_last",
|
_last_message = models.ForeignKey('ForumMessage', related_name="forums_where_its_last",
|
||||||
verbose_name=_("the last message"), null=True, on_delete=models.SET_NULL)
|
verbose_name=_("the last message"), null=True, on_delete=models.SET_NULL)
|
||||||
_topic_number = models.IntegerField(_("number of topics"), default=0)
|
_topic_number = models.IntegerField(_("number of topics"), default=0)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -112,16 +111,18 @@ class Forum(models.Model):
|
|||||||
self.view_groups = self.parent.view_groups.all()
|
self.view_groups = self.parent.view_groups.all()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
_club_memberships = {} # This cache is particularly efficient:
|
_club_memberships = {} # This cache is particularly efficient:
|
||||||
# divided by 3 the number of requests on the main forum page
|
# divided by 3 the number of requests on the main forum page
|
||||||
# after the first load
|
# after the first load
|
||||||
def is_owned_by(self, user):
|
def is_owned_by(self, user):
|
||||||
if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID):
|
if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID):
|
||||||
return True
|
return True
|
||||||
try: m = Forum._club_memberships[self.id][user.id]
|
try:
|
||||||
|
m = Forum._club_memberships[self.id][user.id]
|
||||||
except:
|
except:
|
||||||
m = self.owner_club.get_membership_for(user)
|
m = self.owner_club.get_membership_for(user)
|
||||||
try: Forum._club_memberships[self.id][user.id] = m
|
try:
|
||||||
|
Forum._club_memberships[self.id][user.id] = m
|
||||||
except:
|
except:
|
||||||
Forum._club_memberships[self.id] = {}
|
Forum._club_memberships[self.id] = {}
|
||||||
Forum._club_memberships[self.id][user.id] = m
|
Forum._club_memberships[self.id][user.id] = m
|
||||||
@ -178,12 +179,13 @@ class Forum(models.Model):
|
|||||||
l += c.get_children_list()
|
l += c.get_children_list()
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
|
||||||
class ForumTopic(models.Model):
|
class ForumTopic(models.Model):
|
||||||
forum = models.ForeignKey(Forum, related_name='topics')
|
forum = models.ForeignKey(Forum, related_name='topics')
|
||||||
author = models.ForeignKey(User, related_name='forum_topics')
|
author = models.ForeignKey(User, related_name='forum_topics')
|
||||||
description = models.CharField(_('description'), max_length=256, default="")
|
description = models.CharField(_('description'), max_length=256, default="")
|
||||||
_last_message = models.ForeignKey('ForumMessage', related_name="+", verbose_name=_("the last message"),
|
_last_message = models.ForeignKey('ForumMessage', related_name="+", verbose_name=_("the last message"),
|
||||||
null=True, on_delete=models.SET_NULL)
|
null=True, on_delete=models.SET_NULL)
|
||||||
_title = models.CharField(_('title'), max_length=64, blank=True)
|
_title = models.CharField(_('title'), max_length=64, blank=True)
|
||||||
_message_number = models.IntegerField(_("number of messages"), default=0)
|
_message_number = models.IntegerField(_("number of messages"), default=0)
|
||||||
|
|
||||||
@ -192,7 +194,7 @@ class ForumTopic(models.Model):
|
|||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
super(ForumTopic, self).save(*args, **kwargs)
|
super(ForumTopic, self).save(*args, **kwargs)
|
||||||
self.forum.set_topic_number() # Recompute the cached value
|
self.forum.set_topic_number() # Recompute the cached value
|
||||||
self.forum.set_last_message()
|
self.forum.set_last_message()
|
||||||
|
|
||||||
def is_owned_by(self, user):
|
def is_owned_by(self, user):
|
||||||
@ -225,6 +227,7 @@ class ForumTopic(models.Model):
|
|||||||
def title(self):
|
def title(self):
|
||||||
return self._title
|
return self._title
|
||||||
|
|
||||||
|
|
||||||
class ForumMessage(models.Model):
|
class ForumMessage(models.Model):
|
||||||
"""
|
"""
|
||||||
"A ForumMessage object represents a message in the forum" -- Cpt. Obvious
|
"A ForumMessage object represents a message in the forum" -- Cpt. Obvious
|
||||||
@ -244,7 +247,7 @@ class ForumMessage(models.Model):
|
|||||||
return "%s (%s) - %s" % (self.id, self.author, self.title)
|
return "%s (%s) - %s" % (self.id, self.author, self.title)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self._deleted = self.is_deleted() # Recompute the cached value
|
self._deleted = self.is_deleted() # Recompute the cached value
|
||||||
super(ForumMessage, self).save(*args, **kwargs)
|
super(ForumMessage, self).save(*args, **kwargs)
|
||||||
if self.is_last_in_topic():
|
if self.is_last_in_topic():
|
||||||
self.topic._last_message_id = self.id
|
self.topic._last_message_id = self.id
|
||||||
@ -259,15 +262,15 @@ class ForumMessage(models.Model):
|
|||||||
def is_last_in_topic(self):
|
def is_last_in_topic(self):
|
||||||
return bool(self.id == self.topic.messages.order_by('date').last().id)
|
return bool(self.id == self.topic.messages.order_by('date').last().id)
|
||||||
|
|
||||||
def is_owned_by(self, user): # Anyone can create a topic: it's better to
|
def is_owned_by(self, user): # Anyone can create a topic: it's better to
|
||||||
# check the rights at the forum level, since it's more controlled
|
# check the rights at the forum level, since it's more controlled
|
||||||
return self.topic.forum.is_owned_by(user) or user.id == self.author.id
|
return self.topic.forum.is_owned_by(user) or user.id == self.author.id
|
||||||
|
|
||||||
def can_be_edited_by(self, user):
|
def can_be_edited_by(self, user):
|
||||||
return user.can_edit(self.topic.forum)
|
return user.can_edit(self.topic.forum)
|
||||||
|
|
||||||
def can_be_viewed_by(self, user):
|
def can_be_viewed_by(self, user):
|
||||||
return not self._deleted # No need to check the real rights since it's already done by the Topic view
|
return not self._deleted # No need to check the real rights since it's already done by the Topic view
|
||||||
|
|
||||||
def can_be_moderated_by(self, user):
|
def can_be_moderated_by(self, user):
|
||||||
return self.topic.forum.is_owned_by(user) or user.id == self.author.id
|
return self.topic.forum.is_owned_by(user) or user.id == self.author.id
|
||||||
@ -282,10 +285,11 @@ class ForumMessage(models.Model):
|
|||||||
return int(self.topic.messages.filter(id__lt=self.id).count() / settings.SITH_FORUM_PAGE_LENGTH) + 1
|
return int(self.topic.messages.filter(id__lt=self.id).count() / settings.SITH_FORUM_PAGE_LENGTH) + 1
|
||||||
|
|
||||||
def mark_as_read(self, user):
|
def mark_as_read(self, user):
|
||||||
try: # Need the try/except because of AnonymousUser
|
try: # Need the try/except because of AnonymousUser
|
||||||
if not self.is_read(user):
|
if not self.is_read(user):
|
||||||
self.readers.add(user)
|
self.readers.add(user)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def is_read(self, user):
|
def is_read(self, user):
|
||||||
return (self.date < user.forum_infos.last_read_date) or (user in self.readers.all())
|
return (self.date < user.forum_infos.last_read_date) or (user in self.readers.all())
|
||||||
@ -296,11 +300,13 @@ class ForumMessage(models.Model):
|
|||||||
return meta.action == "DELETE"
|
return meta.action == "DELETE"
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
MESSAGE_META_ACTIONS = [
|
MESSAGE_META_ACTIONS = [
|
||||||
('EDIT', _("Message edited by")),
|
('EDIT', _("Message edited by")),
|
||||||
('DELETE', _("Message deleted by")),
|
('DELETE', _("Message deleted by")),
|
||||||
('UNDELETE', _("Message undeleted by")),
|
('UNDELETE', _("Message undeleted by")),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ForumMessageMeta(models.Model):
|
class ForumMessageMeta(models.Model):
|
||||||
user = models.ForeignKey(User, related_name="forum_message_metas")
|
user = models.ForeignKey(User, related_name="forum_message_metas")
|
||||||
@ -322,8 +328,7 @@ class ForumUserInfo(models.Model):
|
|||||||
"""
|
"""
|
||||||
user = models.OneToOneField(User, related_name="_forum_infos")
|
user = models.OneToOneField(User, related_name="_forum_infos")
|
||||||
last_read_date = models.DateTimeField(_('last read date'), default=datetime(year=settings.SITH_SCHOOL_START_YEAR,
|
last_read_date = models.DateTimeField(_('last read date'), default=datetime(year=settings.SITH_SCHOOL_START_YEAR,
|
||||||
month=1, day=1, tzinfo=pytz.UTC))
|
month=1, day=1, tzinfo=pytz.UTC))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.user)
|
return str(self.user)
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from forum.views import *
|
from forum.views import *
|
||||||
|
|
||||||
|
@ -22,30 +22,30 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.views.generic import ListView, DetailView, RedirectView
|
from django.views.generic import ListView, DetailView, RedirectView
|
||||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.urlresolvers import reverse, reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db import models
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
|
|
||||||
from ajax_select import make_ajax_form, make_ajax_field
|
from ajax_select import make_ajax_field
|
||||||
|
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
||||||
from core.views.forms import MarkdownInput
|
from core.views.forms import MarkdownInput
|
||||||
from core.models import Page
|
|
||||||
from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
|
from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
|
||||||
|
|
||||||
|
|
||||||
class ForumMainView(ListView):
|
class ForumMainView(ListView):
|
||||||
queryset = Forum.objects.filter(parent=None).prefetch_related("children___last_message__author", "children___last_message__topic")
|
queryset = Forum.objects.filter(parent=None).prefetch_related("children___last_message__author", "children___last_message__topic")
|
||||||
template_name = "forum/main.jinja"
|
template_name = "forum/main.jinja"
|
||||||
|
|
||||||
|
|
||||||
class ForumMarkAllAsRead(RedirectView):
|
class ForumMarkAllAsRead(RedirectView):
|
||||||
permanent = False
|
permanent = False
|
||||||
url = reverse_lazy('forum:last_unread')
|
url = reverse_lazy('forum:last_unread')
|
||||||
@ -56,10 +56,12 @@ class ForumMarkAllAsRead(RedirectView):
|
|||||||
fi.last_read_date = timezone.now()
|
fi.last_read_date = timezone.now()
|
||||||
fi.save()
|
fi.save()
|
||||||
for m in request.user.read_messages.filter(date__lt=fi.last_read_date):
|
for m in request.user.read_messages.filter(date__lt=fi.last_read_date):
|
||||||
m.readers.remove(request.user) # Clean up to keep table low in data
|
m.readers.remove(request.user) # Clean up to keep table low in data
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)
|
return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ForumLastUnread(ListView):
|
class ForumLastUnread(ListView):
|
||||||
model = ForumTopic
|
model = ForumTopic
|
||||||
template_name = "forum/last_unread.jinja"
|
template_name = "forum/last_unread.jinja"
|
||||||
@ -67,12 +69,13 @@ class ForumLastUnread(ListView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
topic_list = self.model.objects.filter(_last_message__date__gt=self.request.user.forum_infos.last_read_date)\
|
topic_list = self.model.objects.filter(_last_message__date__gt=self.request.user.forum_infos.last_read_date)\
|
||||||
.exclude(_last_message__readers=self.request.user)\
|
.exclude(_last_message__readers=self.request.user)\
|
||||||
.order_by('-_last_message__date')\
|
.order_by('-_last_message__date')\
|
||||||
.select_related('_last_message__author', 'author')\
|
.select_related('_last_message__author', 'author')\
|
||||||
.prefetch_related('forum__edit_groups')
|
.prefetch_related('forum__edit_groups')
|
||||||
return topic_list
|
return topic_list
|
||||||
|
|
||||||
|
|
||||||
class ForumForm(forms.ModelForm):
|
class ForumForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Forum
|
model = Forum
|
||||||
@ -80,6 +83,7 @@ class ForumForm(forms.ModelForm):
|
|||||||
edit_groups = make_ajax_field(Forum, 'edit_groups', 'groups', help_text="")
|
edit_groups = make_ajax_field(Forum, 'edit_groups', 'groups', help_text="")
|
||||||
view_groups = make_ajax_field(Forum, 'view_groups', 'groups', help_text="")
|
view_groups = make_ajax_field(Forum, 'view_groups', 'groups', help_text="")
|
||||||
|
|
||||||
|
|
||||||
class ForumCreateView(CanCreateMixin, CreateView):
|
class ForumCreateView(CanCreateMixin, CreateView):
|
||||||
model = Forum
|
model = Forum
|
||||||
form_class = ForumForm
|
form_class = ForumForm
|
||||||
@ -93,12 +97,15 @@ class ForumCreateView(CanCreateMixin, CreateView):
|
|||||||
init['owner_club'] = parent.owner_club
|
init['owner_club'] = parent.owner_club
|
||||||
init['edit_groups'] = parent.edit_groups.all()
|
init['edit_groups'] = parent.edit_groups.all()
|
||||||
init['view_groups'] = parent.view_groups.all()
|
init['view_groups'] = parent.view_groups.all()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return init
|
return init
|
||||||
|
|
||||||
|
|
||||||
class ForumEditForm(ForumForm):
|
class ForumEditForm(ForumForm):
|
||||||
recursive = forms.BooleanField(label=_("Apply rights and club owner recursively"), required=False)
|
recursive = forms.BooleanField(label=_("Apply rights and club owner recursively"), required=False)
|
||||||
|
|
||||||
|
|
||||||
class ForumEditView(CanEditPropMixin, UpdateView):
|
class ForumEditView(CanEditPropMixin, UpdateView):
|
||||||
model = Forum
|
model = Forum
|
||||||
pk_url_kwarg = "forum_id"
|
pk_url_kwarg = "forum_id"
|
||||||
@ -112,12 +119,14 @@ class ForumEditView(CanEditPropMixin, UpdateView):
|
|||||||
self.object.apply_rights_recursively()
|
self.object.apply_rights_recursively()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class ForumDeleteView(CanEditPropMixin, DeleteView):
|
class ForumDeleteView(CanEditPropMixin, DeleteView):
|
||||||
model = Forum
|
model = Forum
|
||||||
pk_url_kwarg = "forum_id"
|
pk_url_kwarg = "forum_id"
|
||||||
template_name = "core/delete_confirm.jinja"
|
template_name = "core/delete_confirm.jinja"
|
||||||
success_url = reverse_lazy('forum:main')
|
success_url = reverse_lazy('forum:main')
|
||||||
|
|
||||||
|
|
||||||
class ForumDetailView(CanViewMixin, DetailView):
|
class ForumDetailView(CanViewMixin, DetailView):
|
||||||
model = Forum
|
model = Forum
|
||||||
template_name = "forum/forum.jinja"
|
template_name = "forum/forum.jinja"
|
||||||
@ -126,10 +135,10 @@ class ForumDetailView(CanViewMixin, DetailView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs = super(ForumDetailView, self).get_context_data(**kwargs)
|
kwargs = super(ForumDetailView, self).get_context_data(**kwargs)
|
||||||
qs = self.object.topics.order_by('-_last_message__date')\
|
qs = self.object.topics.order_by('-_last_message__date')\
|
||||||
.select_related('_last_message__author', 'author')\
|
.select_related('_last_message__author', 'author')\
|
||||||
.prefetch_related("forum__edit_groups")
|
.prefetch_related("forum__edit_groups")
|
||||||
paginator = Paginator(qs,
|
paginator = Paginator(qs,
|
||||||
settings.SITH_FORUM_PAGE_LENGTH)
|
settings.SITH_FORUM_PAGE_LENGTH)
|
||||||
page = self.request.GET.get('topic_page')
|
page = self.request.GET.get('topic_page')
|
||||||
try:
|
try:
|
||||||
kwargs["topics"] = paginator.page(page)
|
kwargs["topics"] = paginator.page(page)
|
||||||
@ -139,15 +148,17 @@ class ForumDetailView(CanViewMixin, DetailView):
|
|||||||
kwargs["topics"] = paginator.page(paginator.num_pages)
|
kwargs["topics"] = paginator.page(paginator.num_pages)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class TopicForm(forms.ModelForm):
|
class TopicForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ForumMessage
|
model = ForumMessage
|
||||||
fields = ['title', 'message']
|
fields = ['title', 'message']
|
||||||
widgets = {
|
widgets = {
|
||||||
'message': MarkdownInput,
|
'message': MarkdownInput,
|
||||||
}
|
}
|
||||||
title = forms.CharField(required=True, label=_("Title"))
|
title = forms.CharField(required=True, label=_("Title"))
|
||||||
|
|
||||||
|
|
||||||
class ForumTopicCreateView(CanCreateMixin, CreateView):
|
class ForumTopicCreateView(CanCreateMixin, CreateView):
|
||||||
model = ForumMessage
|
model = ForumMessage
|
||||||
form_class = TopicForm
|
form_class = TopicForm
|
||||||
@ -166,12 +177,14 @@ class ForumTopicCreateView(CanCreateMixin, CreateView):
|
|||||||
form.instance.author = self.request.user
|
form.instance.author = self.request.user
|
||||||
return super(ForumTopicCreateView, self).form_valid(form)
|
return super(ForumTopicCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class ForumTopicEditView(CanEditMixin, UpdateView):
|
class ForumTopicEditView(CanEditMixin, UpdateView):
|
||||||
model = ForumTopic
|
model = ForumTopic
|
||||||
fields = ['forum']
|
fields = ['forum']
|
||||||
pk_url_kwarg = "topic_id"
|
pk_url_kwarg = "topic_id"
|
||||||
template_name = "core/edit.jinja"
|
template_name = "core/edit.jinja"
|
||||||
|
|
||||||
|
|
||||||
class ForumTopicDetailView(CanViewMixin, DetailView):
|
class ForumTopicDetailView(CanViewMixin, DetailView):
|
||||||
model = ForumTopic
|
model = ForumTopic
|
||||||
pk_url_kwarg = "topic_id"
|
pk_url_kwarg = "topic_id"
|
||||||
@ -186,9 +199,9 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
|
|||||||
kwargs['first_unread_message_id'] = msg.id
|
kwargs['first_unread_message_id'] = msg.id
|
||||||
except:
|
except:
|
||||||
kwargs['first_unread_message_id'] = float("inf")
|
kwargs['first_unread_message_id'] = float("inf")
|
||||||
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')\
|
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')
|
||||||
.prefetch_related('topic__forum__edit_groups', 'readers').order_by('date'),
|
.prefetch_related('topic__forum__edit_groups', 'readers').order_by('date'),
|
||||||
settings.SITH_FORUM_PAGE_LENGTH)
|
settings.SITH_FORUM_PAGE_LENGTH)
|
||||||
page = self.request.GET.get('page')
|
page = self.request.GET.get('page')
|
||||||
try:
|
try:
|
||||||
kwargs["msgs"] = paginator.page(page)
|
kwargs["msgs"] = paginator.page(page)
|
||||||
@ -198,6 +211,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
|
|||||||
kwargs["msgs"] = paginator.page(paginator.num_pages)
|
kwargs["msgs"] = paginator.page(paginator.num_pages)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class ForumMessageView(SingleObjectMixin, RedirectView):
|
class ForumMessageView(SingleObjectMixin, RedirectView):
|
||||||
model = ForumMessage
|
model = ForumMessage
|
||||||
pk_url_kwarg = "message_id"
|
pk_url_kwarg = "message_id"
|
||||||
@ -207,9 +221,10 @@ class ForumMessageView(SingleObjectMixin, RedirectView):
|
|||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return self.object.get_url()
|
return self.object.get_url()
|
||||||
|
|
||||||
|
|
||||||
class ForumMessageEditView(CanEditMixin, UpdateView):
|
class ForumMessageEditView(CanEditMixin, UpdateView):
|
||||||
model = ForumMessage
|
model = ForumMessage
|
||||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message',], widgets={'message': MarkdownInput})
|
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
|
||||||
template_name = "forum/reply.jinja"
|
template_name = "forum/reply.jinja"
|
||||||
pk_url_kwarg = "message_id"
|
pk_url_kwarg = "message_id"
|
||||||
|
|
||||||
@ -222,6 +237,7 @@ class ForumMessageEditView(CanEditMixin, UpdateView):
|
|||||||
kwargs['topic'] = self.object.topic
|
kwargs['topic'] = self.object.topic
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
|
class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
|
||||||
model = ForumMessage
|
model = ForumMessage
|
||||||
pk_url_kwarg = "message_id"
|
pk_url_kwarg = "message_id"
|
||||||
@ -233,6 +249,7 @@ class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
|
|||||||
ForumMessageMeta(message=self.object, user=self.request.user, action="DELETE").save()
|
ForumMessageMeta(message=self.object, user=self.request.user, action="DELETE").save()
|
||||||
return self.object.get_absolute_url()
|
return self.object.get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
|
class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
|
||||||
model = ForumMessage
|
model = ForumMessage
|
||||||
pk_url_kwarg = "message_id"
|
pk_url_kwarg = "message_id"
|
||||||
@ -244,9 +261,10 @@ class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
|
|||||||
ForumMessageMeta(message=self.object, user=self.request.user, action="UNDELETE").save()
|
ForumMessageMeta(message=self.object, user=self.request.user, action="UNDELETE").save()
|
||||||
return self.object.get_absolute_url()
|
return self.object.get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
class ForumMessageCreateView(CanCreateMixin, CreateView):
|
class ForumMessageCreateView(CanCreateMixin, CreateView):
|
||||||
model = ForumMessage
|
model = ForumMessage
|
||||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message',], widgets={'message': MarkdownInput})
|
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
|
||||||
template_name = "forum/reply.jinja"
|
template_name = "forum/reply.jinja"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
@ -262,7 +280,7 @@ class ForumMessageCreateView(CanCreateMixin, CreateView):
|
|||||||
init['message'] = "> ##### %s\n" % (_("%(author)s said") % {'author': message.author.get_short_name()})
|
init['message'] = "> ##### %s\n" % (_("%(author)s said") % {'author': message.author.get_short_name()})
|
||||||
init['message'] += "\n".join([
|
init['message'] += "\n".join([
|
||||||
"> " + line for line in message.message.split('\n')
|
"> " + line for line in message.message.split('\n')
|
||||||
])
|
])
|
||||||
init['message'] += "\n\n"
|
init['message'] += "\n\n"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(repr(e))
|
print(repr(e))
|
||||||
@ -277,4 +295,3 @@ class ForumMessageCreateView(CanCreateMixin, CreateView):
|
|||||||
kwargs = super(ForumMessageCreateView, self).get_context_data(**kwargs)
|
kwargs = super(ForumMessageCreateView, self).get_context_data(**kwargs)
|
||||||
kwargs['topic'] = self.topic
|
kwargs['topic'] = self.topic
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
@ -27,12 +27,13 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from counter.models import Counter, Product
|
from counter.models import Counter
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
|
|
||||||
class Launderette(models.Model):
|
class Launderette(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=30)
|
name = models.CharField(_('name'), max_length=30)
|
||||||
counter = models.OneToOneField(Counter, verbose_name=_('counter'), related_name='launderette')
|
counter = models.OneToOneField(Counter, verbose_name=_('counter'), related_name='launderette')
|
||||||
@ -78,6 +79,7 @@ class Launderette(models.Model):
|
|||||||
def token_list(self):
|
def token_list(self):
|
||||||
return [t.id for t in self.get_token_list()]
|
return [t.id for t in self.get_token_list()]
|
||||||
|
|
||||||
|
|
||||||
class Machine(models.Model):
|
class Machine(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=30)
|
name = models.CharField(_('name'), max_length=30)
|
||||||
launderette = models.ForeignKey(Launderette, related_name='machines', verbose_name=_('launderette'))
|
launderette = models.ForeignKey(Launderette, related_name='machines', verbose_name=_('launderette'))
|
||||||
@ -103,6 +105,7 @@ class Machine(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('launderette:launderette_admin', kwargs={"launderette_id": self.launderette.id})
|
return reverse('launderette:launderette_admin', kwargs={"launderette_id": self.launderette.id})
|
||||||
|
|
||||||
|
|
||||||
class Token(models.Model):
|
class Token(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=5)
|
name = models.CharField(_('name'), max_length=5)
|
||||||
launderette = models.ForeignKey(Launderette, related_name='tokens', verbose_name=_('launderette'))
|
launderette = models.ForeignKey(Launderette, related_name='tokens', verbose_name=_('launderette'))
|
||||||
@ -140,6 +143,7 @@ class Token(models.Model):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Slot(models.Model):
|
class Slot(models.Model):
|
||||||
start_date = models.DateTimeField(_('start date'))
|
start_date = models.DateTimeField(_('start date'))
|
||||||
type = models.CharField(_('type'), max_length=10, choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES)
|
type = models.CharField(_('type'), max_length=10, choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES)
|
||||||
@ -156,6 +160,4 @@ class Slot(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % (self.user, self.start_date, self.get_type_display(),
|
return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % (self.user, self.start_date, self.get_type_display(),
|
||||||
self.machine.name, self.token)
|
self.machine.name, self.token)
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from launderette.views import *
|
from launderette.views import *
|
||||||
|
|
||||||
|
@ -26,11 +26,8 @@ from datetime import datetime, timedelta
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.views.generic import ListView, DetailView, TemplateView
|
||||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
|
||||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, BaseFormView
|
from django.views.generic.edit import UpdateView, CreateView, DeleteView, BaseFormView
|
||||||
from django.forms.models import modelform_factory
|
|
||||||
from django.forms import CheckboxSelectMultiple
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils import dateparse, timezone
|
from django.utils import dateparse, timezone
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
@ -38,7 +35,6 @@ from django.conf import settings
|
|||||||
from django.db import transaction, DataError
|
from django.db import transaction, DataError
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.template import defaultfilters
|
from django.template import defaultfilters
|
||||||
from django.utils import formats
|
|
||||||
|
|
||||||
from core.models import Page, User
|
from core.models import Page, User
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
@ -49,6 +45,7 @@ from counter.views import GetUserForm
|
|||||||
|
|
||||||
# For users
|
# For users
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteMainView(TemplateView):
|
class LaunderetteMainView(TemplateView):
|
||||||
"""Main presentation view"""
|
"""Main presentation view"""
|
||||||
template_name = 'launderette/launderette_main.jinja'
|
template_name = 'launderette/launderette_main.jinja'
|
||||||
@ -59,11 +56,13 @@ class LaunderetteMainView(TemplateView):
|
|||||||
kwargs['page'] = Page.objects.filter(name='launderette').first()
|
kwargs['page'] = Page.objects.filter(name='launderette').first()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteBookMainView(CanViewMixin, ListView):
|
class LaunderetteBookMainView(CanViewMixin, ListView):
|
||||||
"""Choose which launderette to book"""
|
"""Choose which launderette to book"""
|
||||||
model = Launderette
|
model = Launderette
|
||||||
template_name = 'launderette/launderette_book_choose.jinja'
|
template_name = 'launderette/launderette_book_choose.jinja'
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteBookView(CanViewMixin, DetailView):
|
class LaunderetteBookView(CanViewMixin, DetailView):
|
||||||
"""Display the launderette schedule"""
|
"""Display the launderette schedule"""
|
||||||
model = Launderette
|
model = Launderette
|
||||||
@ -96,11 +95,12 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
|||||||
if self.check_slot("WASHING") and self.check_slot("DRYING", self.date + timedelta(hours=1)):
|
if self.check_slot("WASHING") and self.check_slot("DRYING", self.date + timedelta(hours=1)):
|
||||||
Slot(user=self.subscriber, start_date=self.date, machine=self.machines["WASHING"], type="WASHING").save()
|
Slot(user=self.subscriber, start_date=self.date, machine=self.machines["WASHING"], type="WASHING").save()
|
||||||
Slot(user=self.subscriber, start_date=self.date + timedelta(hours=1),
|
Slot(user=self.subscriber, start_date=self.date + timedelta(hours=1),
|
||||||
machine=self.machines["DRYING"], type="DRYING").save()
|
machine=self.machines["DRYING"], type="DRYING").save()
|
||||||
return super(LaunderetteBookView, self).get(request, *args, **kwargs)
|
return super(LaunderetteBookView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def check_slot(self, type, date=None):
|
def check_slot(self, type, date=None):
|
||||||
if date is None: date = self.date
|
if date is None:
|
||||||
|
date = self.date
|
||||||
for m in self.object.machines.filter(is_working=True, type=type).all():
|
for m in self.object.machines.filter(is_working=True, type=type).all():
|
||||||
slot = Slot.objects.filter(start_date=date, machine=m).first()
|
slot = Slot.objects.filter(start_date=date, machine=m).first()
|
||||||
if slot is None:
|
if slot is None:
|
||||||
@ -121,9 +121,9 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
|||||||
kwargs['planning'] = OrderedDict()
|
kwargs['planning'] = OrderedDict()
|
||||||
kwargs['slot_type'] = self.slot_type
|
kwargs['slot_type'] = self.slot_type
|
||||||
start_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=pytz.UTC)
|
start_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=pytz.UTC)
|
||||||
for date in LaunderetteBookView.date_iterator(start_date, start_date+timedelta(days=6), timedelta(days=1)):
|
for date in LaunderetteBookView.date_iterator(start_date, start_date + timedelta(days=6), timedelta(days=1)):
|
||||||
kwargs['planning'][date] = []
|
kwargs['planning'][date] = []
|
||||||
for h in LaunderetteBookView.date_iterator(date, date+timedelta(days=1), timedelta(hours=1)):
|
for h in LaunderetteBookView.date_iterator(date, date + timedelta(days=1), timedelta(hours=1)):
|
||||||
free = False
|
free = False
|
||||||
if self.slot_type == "BOTH" and self.check_slot("WASHING", h) and self.check_slot("DRYING", h + timedelta(hours=1)):
|
if self.slot_type == "BOTH" and self.check_slot("WASHING", h) and self.check_slot("DRYING", h + timedelta(hours=1)):
|
||||||
free = True
|
free = True
|
||||||
@ -137,6 +137,7 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
|||||||
kwargs['planning'][date].append(None)
|
kwargs['planning'][date].append(None)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class SlotDeleteView(CanEditPropMixin, DeleteView):
|
class SlotDeleteView(CanEditPropMixin, DeleteView):
|
||||||
"""Delete a slot"""
|
"""Delete a slot"""
|
||||||
model = Slot
|
model = Slot
|
||||||
@ -154,6 +155,7 @@ class LaunderetteListView(CanEditPropMixin, ListView):
|
|||||||
model = Launderette
|
model = Launderette
|
||||||
template_name = 'launderette/launderette_list.jinja'
|
template_name = 'launderette/launderette_list.jinja'
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteEditView(CanEditPropMixin, UpdateView):
|
class LaunderetteEditView(CanEditPropMixin, UpdateView):
|
||||||
"""Edit a launderette"""
|
"""Edit a launderette"""
|
||||||
model = Launderette
|
model = Launderette
|
||||||
@ -161,6 +163,7 @@ class LaunderetteEditView(CanEditPropMixin, UpdateView):
|
|||||||
fields = ['name']
|
fields = ['name']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteCreateView(CanCreateMixin, CreateView):
|
class LaunderetteCreateView(CanCreateMixin, CreateView):
|
||||||
"""Create a new launderette"""
|
"""Create a new launderette"""
|
||||||
model = Launderette
|
model = Launderette
|
||||||
@ -174,11 +177,12 @@ class LaunderetteCreateView(CanCreateMixin, CreateView):
|
|||||||
form.instance.counter = c
|
form.instance.counter = c
|
||||||
return super(LaunderetteCreateView, self).form_valid(form)
|
return super(LaunderetteCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class ManageTokenForm(forms.Form):
|
class ManageTokenForm(forms.Form):
|
||||||
action = forms.ChoiceField(choices=[("BACK", _("Back")), ("ADD", _("Add")), ("DEL", _("Delete"))], initial="BACK",
|
action = forms.ChoiceField(choices=[("BACK", _("Back")), ("ADD", _("Add")), ("DEL", _("Delete"))], initial="BACK",
|
||||||
label=_("Action"), widget=forms.RadioSelect)
|
label=_("Action"), widget=forms.RadioSelect)
|
||||||
token_type = forms.ChoiceField(choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES, label=_("Type"), initial="WASHING",
|
token_type = forms.ChoiceField(choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES, label=_("Type"), initial="WASHING",
|
||||||
widget=forms.RadioSelect)
|
widget=forms.RadioSelect)
|
||||||
tokens = forms.CharField(max_length=512, widget=forms.widgets.Textarea, label=_("Tokens, separated by spaces"))
|
tokens = forms.CharField(max_length=512, widget=forms.widgets.Textarea, label=_("Tokens, separated by spaces"))
|
||||||
|
|
||||||
def process(self, launderette):
|
def process(self, launderette):
|
||||||
@ -210,6 +214,7 @@ class ManageTokenForm(forms.Form):
|
|||||||
except:
|
except:
|
||||||
self.add_error(None, _("Token %(token_name)s does not exists") % {'token_name': t})
|
self.add_error(None, _("Token %(token_name)s does not exists") % {'token_name': t})
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
||||||
"""The admin page of the launderette"""
|
"""The admin page of the launderette"""
|
||||||
model = Launderette
|
model = Launderette
|
||||||
@ -253,6 +258,7 @@ class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('launderette:launderette_admin', args=self.args, kwargs=self.kwargs)
|
return reverse_lazy('launderette:launderette_admin', args=self.args, kwargs=self.kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GetLaunderetteUserForm(GetUserForm):
|
class GetLaunderetteUserForm(GetUserForm):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super(GetLaunderetteUserForm, self).clean()
|
cleaned_data = super(GetLaunderetteUserForm, self).clean()
|
||||||
@ -261,12 +267,13 @@ class GetLaunderetteUserForm(GetUserForm):
|
|||||||
raise forms.ValidationError(_("User has booked no slot"))
|
raise forms.ValidationError(_("User has booked no slot"))
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
||||||
"""The click page of the launderette"""
|
"""The click page of the launderette"""
|
||||||
model = Launderette
|
model = Launderette
|
||||||
pk_url_kwarg = "launderette_id"
|
pk_url_kwarg = "launderette_id"
|
||||||
template_name = 'counter/counter_main.jinja'
|
template_name = 'counter/counter_main.jinja'
|
||||||
form_class = GetLaunderetteUserForm # Form to enter a client code and get the corresponding user id
|
form_class = GetLaunderetteUserForm # Form to enter a client code and get the corresponding user id
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
@ -301,6 +308,7 @@ class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('launderette:click', args=self.args, kwargs=self.kwargs)
|
return reverse_lazy('launderette:click', args=self.args, kwargs=self.kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ClickTokenForm(forms.BaseForm):
|
class ClickTokenForm(forms.BaseForm):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
@ -309,11 +317,11 @@ class ClickTokenForm(forms.BaseForm):
|
|||||||
counter = Counter.objects.filter(id=self.counter_id).first()
|
counter = Counter.objects.filter(id=self.counter_id).first()
|
||||||
subscriber = customer.user
|
subscriber = customer.user
|
||||||
self.last_basket = {
|
self.last_basket = {
|
||||||
'last_basket': [],
|
'last_basket': [],
|
||||||
'last_customer': customer.user.get_display_name(),
|
'last_customer': customer.user.get_display_name(),
|
||||||
}
|
}
|
||||||
total = 0
|
total = 0
|
||||||
for k,t in self.cleaned_data.items():
|
for k, t in self.cleaned_data.items():
|
||||||
if t is not None:
|
if t is not None:
|
||||||
slot_id = int(k[5:])
|
slot_id = int(k[5:])
|
||||||
slot = Slot.objects.filter(id=slot_id).first()
|
slot = Slot.objects.filter(id=slot_id).first()
|
||||||
@ -323,15 +331,16 @@ class ClickTokenForm(forms.BaseForm):
|
|||||||
t.borrow_date = datetime.now().replace(tzinfo=pytz.UTC)
|
t.borrow_date = datetime.now().replace(tzinfo=pytz.UTC)
|
||||||
t.save()
|
t.save()
|
||||||
price = settings.SITH_LAUNDERETTE_PRICES[t.type]
|
price = settings.SITH_LAUNDERETTE_PRICES[t.type]
|
||||||
s = Selling(label="Jeton "+t.get_type_display()+" N°"+t.name, club=counter.club, product=None, counter=counter, unit_price=price,
|
s = Selling(label="Jeton " + t.get_type_display() + " N°" + t.name, club=counter.club, product=None, counter=counter, unit_price=price,
|
||||||
quantity=1, seller=operator, customer=customer)
|
quantity=1, seller=operator, customer=customer)
|
||||||
s.save()
|
s.save()
|
||||||
total += price
|
total += price
|
||||||
self.last_basket['last_basket'].append("Jeton "+t.get_type_display()+" N°"+t.name)
|
self.last_basket['last_basket'].append("Jeton " + t.get_type_display() + " N°" + t.name)
|
||||||
self.last_basket['new_customer_amount'] = str(customer.amount)
|
self.last_basket['new_customer_amount'] = str(customer.amount)
|
||||||
self.last_basket['last_total'] = str(total)
|
self.last_basket['last_total'] = str(total)
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
||||||
"""The click page of the launderette"""
|
"""The click page of the launderette"""
|
||||||
model = Launderette
|
model = Launderette
|
||||||
@ -346,7 +355,7 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
|||||||
t_name = str(self2.data[field_name])
|
t_name = str(self2.data[field_name])
|
||||||
if t_name != "":
|
if t_name != "":
|
||||||
t = Token.objects.filter(name=str(self2.data[field_name]), type=slot.type, launderette=self.object,
|
t = Token.objects.filter(name=str(self2.data[field_name]), type=slot.type, launderette=self.object,
|
||||||
user=None).first()
|
user=None).first()
|
||||||
if t is None:
|
if t is None:
|
||||||
raise forms.ValidationError(_("Token not found"))
|
raise forms.ValidationError(_("Token not found"))
|
||||||
return t
|
return t
|
||||||
@ -354,9 +363,9 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
|||||||
for s in self.subscriber.slots.filter(token=None, start_date__gte=timezone.now().replace(tzinfo=None)).all():
|
for s in self.subscriber.slots.filter(token=None, start_date__gte=timezone.now().replace(tzinfo=None)).all():
|
||||||
field_name = "slot-%s" % (str(s.id))
|
field_name = "slot-%s" % (str(s.id))
|
||||||
fields[field_name] = forms.CharField(max_length=5, required=False,
|
fields[field_name] = forms.CharField(max_length=5, required=False,
|
||||||
label="%s - %s" % (s.get_type_display(), defaultfilters.date(s.start_date, "j N Y H:i")))
|
label="%s - %s" % (s.get_type_display(), defaultfilters.date(s.start_date, "j N Y H:i")))
|
||||||
# XXX l10n settings.DATETIME_FORMAT didn't work here :/
|
# XXX l10n settings.DATETIME_FORMAT didn't work here :/
|
||||||
kwargs["clean_"+field_name] = clean_field_factory(field_name, s)
|
kwargs["clean_" + field_name] = clean_field_factory(field_name, s)
|
||||||
kwargs['subscriber_id'] = self.subscriber.id
|
kwargs['subscriber_id'] = self.subscriber.id
|
||||||
kwargs['counter_id'] = self.object.counter.id
|
kwargs['counter_id'] = self.object.counter.id
|
||||||
kwargs['operator_id'] = self.operator.id
|
kwargs['operator_id'] = self.operator.id
|
||||||
@ -401,7 +410,6 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
|||||||
return reverse_lazy('launderette:main_click', args=self.args, kwargs=self.kwargs)
|
return reverse_lazy('launderette:main_click', args=self.args, kwargs=self.kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MachineEditView(CanEditPropMixin, UpdateView):
|
class MachineEditView(CanEditPropMixin, UpdateView):
|
||||||
"""Edit a machine"""
|
"""Edit a machine"""
|
||||||
model = Machine
|
model = Machine
|
||||||
@ -409,6 +417,7 @@ class MachineEditView(CanEditPropMixin, UpdateView):
|
|||||||
fields = ['name', 'launderette', 'type', 'is_working']
|
fields = ['name', 'launderette', 'type', 'is_working']
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
|
|
||||||
|
|
||||||
class MachineDeleteView(CanEditPropMixin, DeleteView):
|
class MachineDeleteView(CanEditPropMixin, DeleteView):
|
||||||
"""Edit a machine"""
|
"""Edit a machine"""
|
||||||
model = Machine
|
model = Machine
|
||||||
@ -416,6 +425,7 @@ class MachineDeleteView(CanEditPropMixin, DeleteView):
|
|||||||
template_name = 'core/delete_confirm.jinja'
|
template_name = 'core/delete_confirm.jinja'
|
||||||
success_url = reverse_lazy('launderette:launderette_list')
|
success_url = reverse_lazy('launderette:launderette_list')
|
||||||
|
|
||||||
|
|
||||||
class MachineCreateView(CanCreateMixin, CreateView):
|
class MachineCreateView(CanCreateMixin, CreateView):
|
||||||
"""Create a new machine"""
|
"""Create a new machine"""
|
||||||
model = Machine
|
model = Machine
|
||||||
@ -429,6 +439,3 @@ class MachineCreateView(CanCreateMixin, CreateView):
|
|||||||
if obj is not None:
|
if obj is not None:
|
||||||
ret['launderette'] = obj.id
|
ret['launderette'] = obj.id
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,5 +30,3 @@ from sas.models import *
|
|||||||
admin.site.register(Album)
|
admin.site.register(Album)
|
||||||
# admin.site.register(Picture)
|
# admin.site.register(Picture)
|
||||||
admin.site.register(PeoplePictureRelation)
|
admin.site.register(PeoplePictureRelation)
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,11 +23,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@ -36,6 +34,7 @@ import os
|
|||||||
from core.models import SithFile, User
|
from core.models import SithFile, User
|
||||||
from core.utils import resize_image, exif_auto_rotate
|
from core.utils import resize_image, exif_auto_rotate
|
||||||
|
|
||||||
|
|
||||||
class Picture(SithFile):
|
class Picture(SithFile):
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
@ -50,12 +49,12 @@ class Picture(SithFile):
|
|||||||
|
|
||||||
def can_be_edited_by(self, user):
|
def can_be_edited_by(self, user):
|
||||||
# file = SithFile.objects.filter(id=self.id).first()
|
# file = SithFile.objects.filter(id=self.id).first()
|
||||||
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID)# or user.can_edit(file)
|
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) # or user.can_edit(file)
|
||||||
|
|
||||||
def can_be_viewed_by(self, user):
|
def can_be_viewed_by(self, user):
|
||||||
# file = SithFile.objects.filter(id=self.id).first()
|
# file = SithFile.objects.filter(id=self.id).first()
|
||||||
return self.can_be_edited_by(user) or (self.is_in_sas and self.is_moderated and
|
return self.can_be_edited_by(user) or (self.is_in_sas and self.is_moderated and
|
||||||
user.was_subscribed)# or user.can_view(file)
|
user.was_subscribed) # or user.can_view(file)
|
||||||
|
|
||||||
def get_download_url(self):
|
def get_download_url(self):
|
||||||
return reverse('sas:download', kwargs={'picture_id': self.id})
|
return reverse('sas:download', kwargs={'picture_id': self.id})
|
||||||
@ -73,7 +72,8 @@ class Picture(SithFile):
|
|||||||
im = Image.open(BytesIO(self.file.read()))
|
im = Image.open(BytesIO(self.file.read()))
|
||||||
try:
|
try:
|
||||||
im = exif_auto_rotate(im)
|
im = exif_auto_rotate(im)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
file = resize_image(im, max(im.size), self.mime_type.split('/')[-1])
|
file = resize_image(im, max(im.size), self.mime_type.split('/')[-1])
|
||||||
thumb = resize_image(im, 200, self.mime_type.split('/')[-1])
|
thumb = resize_image(im, 200, self.mime_type.split('/')[-1])
|
||||||
compressed = resize_image(im, 1200, self.mime_type.split('/')[-1])
|
compressed = resize_image(im, 1200, self.mime_type.split('/')[-1])
|
||||||
@ -102,17 +102,18 @@ class Picture(SithFile):
|
|||||||
def get_next(self):
|
def get_next(self):
|
||||||
if self.is_moderated:
|
if self.is_moderated:
|
||||||
return self.parent.children.filter(is_moderated=True, asked_for_removal=False, is_folder=False,
|
return self.parent.children.filter(is_moderated=True, asked_for_removal=False, is_folder=False,
|
||||||
id__gt=self.id).order_by('id').first()
|
id__gt=self.id).order_by('id').first()
|
||||||
else:
|
else:
|
||||||
return Picture.objects.filter(id__gt=self.id, is_moderated=False, is_in_sas=True).order_by('id').first()
|
return Picture.objects.filter(id__gt=self.id, is_moderated=False, is_in_sas=True).order_by('id').first()
|
||||||
|
|
||||||
def get_previous(self):
|
def get_previous(self):
|
||||||
if self.is_moderated:
|
if self.is_moderated:
|
||||||
return self.parent.children.filter(is_moderated=True, asked_for_removal=False, is_folder=False,
|
return self.parent.children.filter(is_moderated=True, asked_for_removal=False, is_folder=False,
|
||||||
id__lt=self.id).order_by('id').last()
|
id__lt=self.id).order_by('id').last()
|
||||||
else:
|
else:
|
||||||
return Picture.objects.filter(id__lt=self.id, is_moderated=False, is_in_sas=True).order_by('-id').first()
|
return Picture.objects.filter(id__lt=self.id, is_moderated=False, is_in_sas=True).order_by('-id').first()
|
||||||
|
|
||||||
|
|
||||||
class Album(SithFile):
|
class Album(SithFile):
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
@ -127,12 +128,12 @@ class Album(SithFile):
|
|||||||
|
|
||||||
def can_be_edited_by(self, user):
|
def can_be_edited_by(self, user):
|
||||||
# file = SithFile.objects.filter(id=self.id).first()
|
# file = SithFile.objects.filter(id=self.id).first()
|
||||||
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID)# or user.can_edit(file)
|
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) # or user.can_edit(file)
|
||||||
|
|
||||||
def can_be_viewed_by(self, user):
|
def can_be_viewed_by(self, user):
|
||||||
# file = SithFile.objects.filter(id=self.id).first()
|
# file = SithFile.objects.filter(id=self.id).first()
|
||||||
return self.can_be_edited_by(user) or (self.is_in_sas and self.is_moderated and
|
return self.can_be_edited_by(user) or (self.is_in_sas and self.is_moderated and
|
||||||
user.was_subscribed)# or user.can_view(file)
|
user.was_subscribed) # or user.can_view(file)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('sas:album', kwargs={'album_id': self.id})
|
return reverse('sas:album', kwargs={'album_id': self.id})
|
||||||
@ -148,6 +149,7 @@ class Album(SithFile):
|
|||||||
self.file.name = self.name + '/thumb.jpg'
|
self.file.name = self.name + '/thumb.jpg'
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
class PeoplePictureRelation(models.Model):
|
class PeoplePictureRelation(models.Model):
|
||||||
"""
|
"""
|
||||||
The PeoplePictureRelation class makes the connection between User and Picture
|
The PeoplePictureRelation class makes the connection between User and Picture
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from sas.views import *
|
from sas.views import *
|
||||||
|
|
||||||
@ -40,4 +40,3 @@ urlpatterns = [
|
|||||||
# url(r'^album/new$', AlbumCreateView.as_view(), name='album_new'),
|
# url(r'^album/new$', AlbumCreateView.as_view(), name='album_new'),
|
||||||
# url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
|
# url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
73
sas/views.py
73
sas/views.py
@ -22,35 +22,31 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import redirect
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
from core.views.forms import SelectDate
|
||||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, ProcessFormView, FormMixin, FormView
|
from django.views.generic import DetailView, TemplateView
|
||||||
|
from django.views.generic.edit import UpdateView, FormMixin, FormView
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.forms.models import modelform_factory
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
from ajax_select import make_ajax_form, make_ajax_field
|
from ajax_select import make_ajax_field
|
||||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
from ajax_select.fields import AutoCompleteSelectMultipleField
|
||||||
|
|
||||||
from io import BytesIO
|
from core.views import CanViewMixin, CanEditMixin
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
|
||||||
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
|
|
||||||
from core.views.files import send_file, FileView
|
from core.views.files import send_file, FileView
|
||||||
from core.models import SithFile, User, Notification, RealGroup
|
from core.models import SithFile, User, Notification, RealGroup
|
||||||
|
|
||||||
from sas.models import Picture, Album, PeoplePictureRelation
|
from sas.models import Picture, Album, PeoplePictureRelation
|
||||||
|
|
||||||
|
|
||||||
class SASForm(forms.Form):
|
class SASForm(forms.Form):
|
||||||
album_name = forms.CharField(label=_("Add a new album"), max_length=30, required=False)
|
album_name = forms.CharField(label=_("Add a new album"), max_length=30, required=False)
|
||||||
images = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Upload images"),
|
images = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Upload images"),
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
def process(self, parent, owner, files, automodere=False):
|
def process(self, parent, owner, files, automodere=False):
|
||||||
notif = False
|
notif = False
|
||||||
@ -62,10 +58,10 @@ class SASForm(forms.Form):
|
|||||||
notif = not automodere
|
notif = not automodere
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.add_error(None, _("Error creating album %(album)s: %(msg)s") %
|
self.add_error(None, _("Error creating album %(album)s: %(msg)s") %
|
||||||
{'album': self.cleaned_data['album_name'], 'msg': repr(e)})
|
{'album': self.cleaned_data['album_name'], 'msg': repr(e)})
|
||||||
for f in files:
|
for f in files:
|
||||||
new_file = Picture(parent=parent, name=f.name, file=f, owner=owner, mime_type=f.content_type, size=f._size,
|
new_file = Picture(parent=parent, name=f.name, file=f, owner=owner, mime_type=f.content_type, size=f._size,
|
||||||
is_folder=False, is_moderated=automodere)
|
is_folder=False, is_moderated=automodere)
|
||||||
if automodere:
|
if automodere:
|
||||||
new_file.moderator = owner
|
new_file.moderator = owner
|
||||||
try:
|
try:
|
||||||
@ -80,6 +76,7 @@ class SASForm(forms.Form):
|
|||||||
if not u.notifications.filter(type="SAS_MODERATION", viewed=False).exists():
|
if not u.notifications.filter(type="SAS_MODERATION", viewed=False).exists():
|
||||||
Notification(user=u, url=reverse("sas:moderation"), type="SAS_MODERATION").save()
|
Notification(user=u, url=reverse("sas:moderation"), type="SAS_MODERATION").save()
|
||||||
|
|
||||||
|
|
||||||
class RelationForm(forms.ModelForm):
|
class RelationForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PeoplePictureRelation
|
model = PeoplePictureRelation
|
||||||
@ -87,6 +84,7 @@ class RelationForm(forms.ModelForm):
|
|||||||
widgets = {'picture': forms.HiddenInput}
|
widgets = {'picture': forms.HiddenInput}
|
||||||
users = AutoCompleteSelectMultipleField('users', show_help_text=False, help_text="", label=_("Add user"), required=False)
|
users = AutoCompleteSelectMultipleField('users', show_help_text=False, help_text="", label=_("Add user"), required=False)
|
||||||
|
|
||||||
|
|
||||||
class SASMainView(FormView):
|
class SASMainView(FormView):
|
||||||
form_class = SASForm
|
form_class = SASForm
|
||||||
template_name = "sas/main.jinja"
|
template_name = "sas/main.jinja"
|
||||||
@ -112,6 +110,7 @@ class SASMainView(FormView):
|
|||||||
kwargs['latest'] = SithFile.objects.filter(is_in_sas=True, is_folder=True, is_moderated=True).order_by('-id')[:5]
|
kwargs['latest'] = SithFile.objects.filter(is_in_sas=True, is_folder=True, is_moderated=True).order_by('-id')[:5]
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class PictureView(CanViewMixin, DetailView, FormMixin):
|
class PictureView(CanViewMixin, DetailView, FormMixin):
|
||||||
model = Picture
|
model = Picture
|
||||||
form_class = RelationForm
|
form_class = RelationForm
|
||||||
@ -132,8 +131,9 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
|
|||||||
try:
|
try:
|
||||||
user = User.objects.filter(id=int(request.GET['remove_user'])).first()
|
user = User.objects.filter(id=int(request.GET['remove_user'])).first()
|
||||||
if user.id == request.user.id or request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID):
|
if user.id == request.user.id or request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID):
|
||||||
r = PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
|
PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
if 'ask_removal' in request.GET.keys():
|
if 'ask_removal' in request.GET.keys():
|
||||||
self.object.is_moderated = False
|
self.object.is_moderated = False
|
||||||
self.object.asked_for_removal = True
|
self.object.asked_for_removal = True
|
||||||
@ -149,7 +149,7 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
|
|||||||
for uid in self.form.cleaned_data['users']:
|
for uid in self.form.cleaned_data['users']:
|
||||||
u = User.objects.filter(id=uid).first()
|
u = User.objects.filter(id=uid).first()
|
||||||
PeoplePictureRelation(user=u,
|
PeoplePictureRelation(user=u,
|
||||||
picture=self.form.cleaned_data['picture']).save()
|
picture=self.form.cleaned_data['picture']).save()
|
||||||
if not u.notifications.filter(type="NEW_PICTURES", viewed=False).exists():
|
if not u.notifications.filter(type="NEW_PICTURES", viewed=False).exists():
|
||||||
Notification(user=u, url=reverse("core:user_pictures", kwargs={'user_id': u.id}), type="NEW_PICTURES").save()
|
Notification(user=u, url=reverse("core:user_pictures", kwargs={'user_id': u.id}), type="NEW_PICTURES").save()
|
||||||
return super(PictureView, self).form_valid(self.form)
|
return super(PictureView, self).form_valid(self.form)
|
||||||
@ -165,15 +165,19 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('sas:picture', kwargs={'picture_id': self.object.id})
|
return reverse('sas:picture', kwargs={'picture_id': self.object.id})
|
||||||
|
|
||||||
|
|
||||||
def send_pict(request, picture_id):
|
def send_pict(request, picture_id):
|
||||||
return send_file(request, picture_id, Picture)
|
return send_file(request, picture_id, Picture)
|
||||||
|
|
||||||
|
|
||||||
def send_compressed(request, picture_id):
|
def send_compressed(request, picture_id):
|
||||||
return send_file(request, picture_id, Picture, "compressed")
|
return send_file(request, picture_id, Picture, "compressed")
|
||||||
|
|
||||||
|
|
||||||
def send_thumb(request, picture_id):
|
def send_thumb(request, picture_id):
|
||||||
return send_file(request, picture_id, Picture, "thumbnail")
|
return send_file(request, picture_id, Picture, "thumbnail")
|
||||||
|
|
||||||
|
|
||||||
class AlbumUploadView(CanViewMixin, DetailView, FormMixin):
|
class AlbumUploadView(CanViewMixin, DetailView, FormMixin):
|
||||||
model = Album
|
model = Album
|
||||||
form_class = SASForm
|
form_class = SASForm
|
||||||
@ -189,7 +193,7 @@ class AlbumUploadView(CanViewMixin, DetailView, FormMixin):
|
|||||||
if request.user.is_authenticated() and request.user.is_subscribed:
|
if request.user.is_authenticated() and request.user.is_subscribed:
|
||||||
if self.form.is_valid():
|
if self.form.is_valid():
|
||||||
self.form.process(parent=parent, owner=request.user, files=files,
|
self.form.process(parent=parent, owner=request.user, files=files,
|
||||||
automodere=request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID))
|
automodere=request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID))
|
||||||
if self.form.is_valid():
|
if self.form.is_valid():
|
||||||
return HttpResponse(str(self.form.errors), status=200)
|
return HttpResponse(str(self.form.errors), status=200)
|
||||||
return HttpResponse(str(self.form.errors), status=500)
|
return HttpResponse(str(self.form.errors), status=500)
|
||||||
@ -214,14 +218,14 @@ class AlbumView(CanViewMixin, DetailView, FormMixin):
|
|||||||
self.form = self.get_form()
|
self.form = self.get_form()
|
||||||
if 'clipboard' not in request.session.keys():
|
if 'clipboard' not in request.session.keys():
|
||||||
request.session['clipboard'] = []
|
request.session['clipboard'] = []
|
||||||
if request.user.can_edit(self.object): # Handle the copy-paste functions
|
if request.user.can_edit(self.object): # Handle the copy-paste functions
|
||||||
FileView.handle_clipboard(request, self.object)
|
FileView.handle_clipboard(request, self.object)
|
||||||
parent = SithFile.objects.filter(id=self.object.id).first()
|
parent = SithFile.objects.filter(id=self.object.id).first()
|
||||||
files = request.FILES.getlist('images')
|
files = request.FILES.getlist('images')
|
||||||
if request.user.is_authenticated() and request.user.is_subscribed:
|
if request.user.is_authenticated() and request.user.is_subscribed:
|
||||||
if self.form.is_valid():
|
if self.form.is_valid():
|
||||||
self.form.process(parent=parent, owner=request.user, files=files,
|
self.form.process(parent=parent, owner=request.user, files=files,
|
||||||
automodere=request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID))
|
automodere=request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID))
|
||||||
if self.form.is_valid():
|
if self.form.is_valid():
|
||||||
return super(AlbumView, self).form_valid(self.form)
|
return super(AlbumView, self).form_valid(self.form)
|
||||||
else:
|
else:
|
||||||
@ -239,6 +243,7 @@ class AlbumView(CanViewMixin, DetailView, FormMixin):
|
|||||||
|
|
||||||
# Admin views
|
# Admin views
|
||||||
|
|
||||||
|
|
||||||
class ModerationView(TemplateView):
|
class ModerationView(TemplateView):
|
||||||
template_name = "sas/moderation.jinja"
|
template_name = "sas/moderation.jinja"
|
||||||
|
|
||||||
@ -257,23 +262,26 @@ class ModerationView(TemplateView):
|
|||||||
a.save()
|
a.save()
|
||||||
elif 'delete' in request.POST.keys():
|
elif 'delete' in request.POST.keys():
|
||||||
a.delete()
|
a.delete()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return super(ModerationView, self).get(request, *args, **kwargs)
|
return super(ModerationView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs = super(ModerationView, self).get_context_data(**kwargs)
|
kwargs = super(ModerationView, self).get_context_data(**kwargs)
|
||||||
kwargs['albums_to_moderate'] = Album.objects.filter(is_moderated=False, is_in_sas=True,
|
kwargs['albums_to_moderate'] = Album.objects.filter(is_moderated=False, is_in_sas=True,
|
||||||
is_folder=True).order_by('id')
|
is_folder=True).order_by('id')
|
||||||
kwargs['pictures'] = Picture.objects.filter(is_moderated=False, is_in_sas=True, is_folder=False)
|
kwargs['pictures'] = Picture.objects.filter(is_moderated=False, is_in_sas=True, is_folder=False)
|
||||||
kwargs['albums'] = Album.objects.filter(id__in=kwargs['pictures'].values('parent').distinct('parent'))
|
kwargs['albums'] = Album.objects.filter(id__in=kwargs['pictures'].values('parent').distinct('parent'))
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class PictureEditForm(forms.ModelForm):
|
class PictureEditForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Picture
|
model = Picture
|
||||||
fields=['name', 'parent']
|
fields = ['name', 'parent']
|
||||||
parent = make_ajax_field(Picture, 'parent', 'files', help_text="")
|
parent = make_ajax_field(Picture, 'parent', 'files', help_text="")
|
||||||
|
|
||||||
|
|
||||||
class AlbumEditForm(forms.ModelForm):
|
class AlbumEditForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
@ -283,16 +291,18 @@ class AlbumEditForm(forms.ModelForm):
|
|||||||
edit_groups = make_ajax_field(Album, 'edit_groups', 'groups', help_text="")
|
edit_groups = make_ajax_field(Album, 'edit_groups', 'groups', help_text="")
|
||||||
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
||||||
|
|
||||||
|
|
||||||
class PictureEditView(CanEditMixin, UpdateView):
|
class PictureEditView(CanEditMixin, UpdateView):
|
||||||
model=Picture
|
model = Picture
|
||||||
form_class=PictureEditForm
|
form_class = PictureEditForm
|
||||||
template_name='core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
pk_url_kwarg = "picture_id"
|
pk_url_kwarg = "picture_id"
|
||||||
|
|
||||||
|
|
||||||
class AlbumEditView(CanEditMixin, UpdateView):
|
class AlbumEditView(CanEditMixin, UpdateView):
|
||||||
model=Album
|
model = Album
|
||||||
form_class=AlbumEditForm
|
form_class = AlbumEditForm
|
||||||
template_name='core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
pk_url_kwarg = "album_id"
|
pk_url_kwarg = "album_id"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@ -300,4 +310,3 @@ class AlbumEditView(CanEditMixin, UpdateView):
|
|||||||
if form.cleaned_data['recursive']:
|
if form.cleaned_data['recursive']:
|
||||||
self.object.apply_rights_recursively(True)
|
self.object.apply_rights_recursively(True)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
232
sith/settings.py
232
sith/settings.py
@ -114,14 +114,14 @@ TEMPLATES = [
|
|||||||
"app_dirname": "templates",
|
"app_dirname": "templates",
|
||||||
"newstyle_gettext": True,
|
"newstyle_gettext": True,
|
||||||
"context_processors": [
|
"context_processors": [
|
||||||
"django.contrib.auth.context_processors.auth",
|
"django.contrib.auth.context_processors.auth",
|
||||||
"django.template.context_processors.debug",
|
"django.template.context_processors.debug",
|
||||||
"django.template.context_processors.i18n",
|
"django.template.context_processors.i18n",
|
||||||
"django.template.context_processors.media",
|
"django.template.context_processors.media",
|
||||||
"django.template.context_processors.static",
|
"django.template.context_processors.static",
|
||||||
"django.template.context_processors.tz",
|
"django.template.context_processors.tz",
|
||||||
"django.contrib.messages.context_processors.messages",
|
"django.contrib.messages.context_processors.messages",
|
||||||
],
|
],
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"jinja2.ext.do",
|
"jinja2.ext.do",
|
||||||
"jinja2.ext.loopcontrols",
|
"jinja2.ext.loopcontrols",
|
||||||
@ -176,11 +176,11 @@ TEMPLATES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
HAYSTACK_CONNECTIONS = {
|
HAYSTACK_CONNECTIONS = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||||
'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
|
'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
HAYSTACK_SIGNAL_PROCESSOR = 'core.search_indexes.UserOnlySignalProcessor'
|
HAYSTACK_SIGNAL_PROCESSOR = 'core.search_indexes.UserOnlySignalProcessor'
|
||||||
|
|
||||||
@ -206,9 +206,9 @@ DATABASES = {
|
|||||||
LANGUAGE_CODE = 'fr-FR'
|
LANGUAGE_CODE = 'fr-FR'
|
||||||
|
|
||||||
LANGUAGES = [
|
LANGUAGES = [
|
||||||
('en', _('English')),
|
('en', _('English')),
|
||||||
('fr', _('French')),
|
('fr', _('French')),
|
||||||
]
|
]
|
||||||
|
|
||||||
TIME_ZONE = 'Europe/Paris'
|
TIME_ZONE = 'Europe/Paris'
|
||||||
|
|
||||||
@ -247,55 +247,55 @@ AUTH_ANONYMOUS_MODEL = 'core.models.AnonymousUser'
|
|||||||
LOGIN_URL = '/login'
|
LOGIN_URL = '/login'
|
||||||
LOGOUT_URL = '/logout'
|
LOGOUT_URL = '/logout'
|
||||||
LOGIN_REDIRECT_URL = '/'
|
LOGIN_REDIRECT_URL = '/'
|
||||||
DEFAULT_FROM_EMAIL="bibou@git.an"
|
DEFAULT_FROM_EMAIL = "bibou@git.an"
|
||||||
SITH_COM_EMAIL="bibou_com@git.an"
|
SITH_COM_EMAIL = "bibou_com@git.an"
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
EMAIL_HOST="localhost"
|
EMAIL_HOST = "localhost"
|
||||||
EMAIL_PORT=25
|
EMAIL_PORT = 25
|
||||||
|
|
||||||
# Below this line, only Sith-specific variables are defined
|
# Below this line, only Sith-specific variables are defined
|
||||||
|
|
||||||
IS_OLD_MYSQL_PRESENT = False
|
IS_OLD_MYSQL_PRESENT = False
|
||||||
OLD_MYSQL_INFOS = {
|
OLD_MYSQL_INFOS = {
|
||||||
'host': 'ae-db',
|
'host': 'ae-db',
|
||||||
'user': "my_user",
|
'user': "my_user",
|
||||||
'passwd': "password",
|
'passwd': "password",
|
||||||
'db': "ae2-db",
|
'db': "ae2-db",
|
||||||
'charset': 'utf8',
|
'charset': 'utf8',
|
||||||
'use_unicode': True,
|
'use_unicode': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SITH_URL = "my.url.git.an"
|
SITH_URL = "my.url.git.an"
|
||||||
SITH_NAME = "Sith website"
|
SITH_NAME = "Sith website"
|
||||||
|
|
||||||
# AE configuration
|
# AE configuration
|
||||||
SITH_MAIN_CLUB_ID = 1 # TODO: keep only that first setting, with the ID, and do the same for the other clubs
|
SITH_MAIN_CLUB_ID = 1 # TODO: keep only that first setting, with the ID, and do the same for the other clubs
|
||||||
SITH_MAIN_CLUB = {
|
SITH_MAIN_CLUB = {
|
||||||
'name': "AE",
|
'name': "AE",
|
||||||
'unix_name': "ae",
|
'unix_name': "ae",
|
||||||
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Bar managers
|
# Bar managers
|
||||||
SITH_BAR_MANAGER = {
|
SITH_BAR_MANAGER = {
|
||||||
'name': "BdF",
|
'name': "BdF",
|
||||||
'unix_name': "bdf",
|
'unix_name': "bdf",
|
||||||
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Launderette managers
|
# Launderette managers
|
||||||
SITH_LAUNDERETTE_MANAGER = {
|
SITH_LAUNDERETTE_MANAGER = {
|
||||||
'name': "Laverie",
|
'name': "Laverie",
|
||||||
'unix_name': "laverie",
|
'unix_name': "laverie",
|
||||||
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Define the date in the year serving as reference for the subscriptions calendar
|
# Define the date in the year serving as reference for the subscriptions calendar
|
||||||
# (month, day)
|
# (month, day)
|
||||||
SITH_START_DATE = (8, 15) # 15th August
|
SITH_START_DATE = (8, 15) # 15th August
|
||||||
|
|
||||||
# Used to determine the valid promos
|
# Used to determine the valid promos
|
||||||
SITH_SCHOOL_START_YEAR = 1999
|
SITH_SCHOOL_START_YEAR = 1999
|
||||||
@ -328,74 +328,74 @@ SITH_FORUM_PAGE_LENGTH = 30
|
|||||||
# SAS variables
|
# SAS variables
|
||||||
SITH_SAS_ROOT_DIR_ID = 4
|
SITH_SAS_ROOT_DIR_ID = 4
|
||||||
|
|
||||||
SITH_BOARD_SUFFIX="-bureau"
|
SITH_BOARD_SUFFIX = "-bureau"
|
||||||
SITH_MEMBER_SUFFIX="-membres"
|
SITH_MEMBER_SUFFIX = "-membres"
|
||||||
|
|
||||||
SITH_MAIN_BOARD_GROUP=SITH_MAIN_CLUB['unix_name']+SITH_BOARD_SUFFIX
|
SITH_MAIN_BOARD_GROUP = SITH_MAIN_CLUB['unix_name'] + SITH_BOARD_SUFFIX
|
||||||
SITH_MAIN_MEMBERS_GROUP=SITH_MAIN_CLUB['unix_name']+SITH_MEMBER_SUFFIX
|
SITH_MAIN_MEMBERS_GROUP = SITH_MAIN_CLUB['unix_name'] + SITH_MEMBER_SUFFIX
|
||||||
|
|
||||||
SITH_PROFILE_DEPARTMENTS = [
|
SITH_PROFILE_DEPARTMENTS = [
|
||||||
("TC", _("TC")),
|
("TC", _("TC")),
|
||||||
("IMSI", _("IMSI")),
|
("IMSI", _("IMSI")),
|
||||||
("IMAP", _("IMAP")),
|
("IMAP", _("IMAP")),
|
||||||
("INFO", _("INFO")),
|
("INFO", _("INFO")),
|
||||||
("GI", _("GI")),
|
("GI", _("GI")),
|
||||||
("E", _("E")),
|
("E", _("E")),
|
||||||
("EE", _("EE")),
|
("EE", _("EE")),
|
||||||
("GESC", _("GESC")),
|
("GESC", _("GESC")),
|
||||||
("GMC", _("GMC")),
|
("GMC", _("GMC")),
|
||||||
("MC", _("MC")),
|
("MC", _("MC")),
|
||||||
("EDIM", _("EDIM")),
|
("EDIM", _("EDIM")),
|
||||||
("HUMA", _("Humanities")),
|
("HUMA", _("Humanities")),
|
||||||
("NA", _("N/A")),
|
("NA", _("N/A")),
|
||||||
]
|
]
|
||||||
|
|
||||||
SITH_ACCOUNTING_PAYMENT_METHOD = [
|
SITH_ACCOUNTING_PAYMENT_METHOD = [
|
||||||
('CHECK', _('Check')),
|
('CHECK', _('Check')),
|
||||||
('CASH', _('Cash')),
|
('CASH', _('Cash')),
|
||||||
('TRANSFERT', _('Transfert')),
|
('TRANSFERT', _('Transfert')),
|
||||||
('CARD', _('Credit card')),
|
('CARD', _('Credit card')),
|
||||||
]
|
]
|
||||||
|
|
||||||
SITH_SUBSCRIPTION_PAYMENT_METHOD = [
|
SITH_SUBSCRIPTION_PAYMENT_METHOD = [
|
||||||
('CHECK', _('Check')),
|
('CHECK', _('Check')),
|
||||||
('CARD', _('Credit card')),
|
('CARD', _('Credit card')),
|
||||||
('CASH', _('Cash')),
|
('CASH', _('Cash')),
|
||||||
('EBOUTIC', _('Eboutic')),
|
('EBOUTIC', _('Eboutic')),
|
||||||
('OTHER', _('Other')),
|
('OTHER', _('Other')),
|
||||||
]
|
]
|
||||||
|
|
||||||
SITH_SUBSCRIPTION_LOCATIONS = [
|
SITH_SUBSCRIPTION_LOCATIONS = [
|
||||||
('BELFORT', _('Belfort')),
|
('BELFORT', _('Belfort')),
|
||||||
('SEVENANS', _('Sevenans')),
|
('SEVENANS', _('Sevenans')),
|
||||||
('MONTBELIARD', _('Montbéliard')),
|
('MONTBELIARD', _('Montbéliard')),
|
||||||
('EBOUTIC', _('Eboutic')),
|
('EBOUTIC', _('Eboutic')),
|
||||||
]
|
]
|
||||||
|
|
||||||
SITH_COUNTER_BARS = [
|
SITH_COUNTER_BARS = [
|
||||||
(1, "MDE"),
|
(1, "MDE"),
|
||||||
(2, "Foyer"),
|
(2, "Foyer"),
|
||||||
(35, "La Gommette"),
|
(35, "La Gommette"),
|
||||||
]
|
]
|
||||||
|
|
||||||
SITH_COUNTER_PAYMENT_METHOD = [
|
SITH_COUNTER_PAYMENT_METHOD = [
|
||||||
('CHECK', _('Check')),
|
('CHECK', _('Check')),
|
||||||
('CASH', _('Cash')),
|
('CASH', _('Cash')),
|
||||||
('CARD', _('Credit card')),
|
('CARD', _('Credit card')),
|
||||||
]
|
]
|
||||||
|
|
||||||
SITH_COUNTER_BANK = [
|
SITH_COUNTER_BANK = [
|
||||||
('OTHER', 'Autre'),
|
('OTHER', 'Autre'),
|
||||||
('SOCIETE-GENERALE', 'Société générale'),
|
('SOCIETE-GENERALE', 'Société générale'),
|
||||||
('BANQUE-POPULAIRE', 'Banque populaire'),
|
('BANQUE-POPULAIRE', 'Banque populaire'),
|
||||||
('BNP', 'BNP'),
|
('BNP', 'BNP'),
|
||||||
('CAISSE-EPARGNE', 'Caisse d\'épargne'),
|
('CAISSE-EPARGNE', 'Caisse d\'épargne'),
|
||||||
('CIC', 'CIC'),
|
('CIC', 'CIC'),
|
||||||
('CREDIT-AGRICOLE', 'Crédit Agricole'),
|
('CREDIT-AGRICOLE', 'Crédit Agricole'),
|
||||||
('CREDIT-MUTUEL', 'Credit Mutuel'),
|
('CREDIT-MUTUEL', 'Credit Mutuel'),
|
||||||
('CREDIT-LYONNAIS', 'Credit Lyonnais'),
|
('CREDIT-LYONNAIS', 'Credit Lyonnais'),
|
||||||
('LA-POSTE', 'La Poste'),
|
('LA-POSTE', 'La Poste'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Defines pagination for cash summary
|
# Defines pagination for cash summary
|
||||||
SITH_COUNTER_CASH_SUMMARY_LENGTH = 50
|
SITH_COUNTER_CASH_SUMMARY_LENGTH = 50
|
||||||
@ -466,7 +466,7 @@ SITH_SUBSCRIPTIONS = {
|
|||||||
'price': 15,
|
'price': 15,
|
||||||
'duration': 2,
|
'duration': 2,
|
||||||
},
|
},
|
||||||
# To be completed....
|
# To be completed....
|
||||||
}
|
}
|
||||||
|
|
||||||
SITH_CLUB_ROLES = {}
|
SITH_CLUB_ROLES = {}
|
||||||
@ -497,16 +497,16 @@ SITH_CLUB_ROLES = {
|
|||||||
|
|
||||||
# This corresponds to the maximum role a user can freely subscribe to
|
# This corresponds to the maximum role a user can freely subscribe to
|
||||||
# In this case, SITH_MAXIMUM_FREE_ROLE=1 means that a user can set himself as "Membre actif" or "Curieux", but not higher
|
# In this case, SITH_MAXIMUM_FREE_ROLE=1 means that a user can set himself as "Membre actif" or "Curieux", but not higher
|
||||||
SITH_MAXIMUM_FREE_ROLE=1
|
SITH_MAXIMUM_FREE_ROLE = 1
|
||||||
|
|
||||||
# Minutes to timeout the logged barmen
|
# Minutes to timeout the logged barmen
|
||||||
SITH_BARMAN_TIMEOUT=20
|
SITH_BARMAN_TIMEOUT = 20
|
||||||
|
|
||||||
# Minutes to delete the last operations
|
# Minutes to delete the last operations
|
||||||
SITH_LAST_OPERATIONS_LIMIT=10
|
SITH_LAST_OPERATIONS_LIMIT = 10
|
||||||
|
|
||||||
# Minutes for a counter to be inactive
|
# Minutes for a counter to be inactive
|
||||||
SITH_COUNTER_MINUTE_INACTIVE=10
|
SITH_COUNTER_MINUTE_INACTIVE = 10
|
||||||
|
|
||||||
# ET variables
|
# ET variables
|
||||||
SITH_EBOUTIC_ET_URL = "https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi"
|
SITH_EBOUTIC_ET_URL = "https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi"
|
||||||
@ -521,27 +521,27 @@ with open('./sith/et_keys/pubkey.pem') as f:
|
|||||||
# Launderette variables
|
# Launderette variables
|
||||||
SITH_LAUNDERETTE_MACHINE_TYPES = [('WASHING', _('Washing')), ('DRYING', _('Drying'))]
|
SITH_LAUNDERETTE_MACHINE_TYPES = [('WASHING', _('Washing')), ('DRYING', _('Drying'))]
|
||||||
SITH_LAUNDERETTE_PRICES = {
|
SITH_LAUNDERETTE_PRICES = {
|
||||||
'WASHING': 1.0,
|
'WASHING': 1.0,
|
||||||
'DRYING': 0.75,
|
'DRYING': 0.75,
|
||||||
}
|
}
|
||||||
|
|
||||||
SITH_NOTIFICATIONS = [
|
SITH_NOTIFICATIONS = [
|
||||||
('NEWS_MODERATION', _("A fresh new to be moderated")),
|
('NEWS_MODERATION', _("A fresh new to be moderated")),
|
||||||
('FILE_MODERATION', _("New files to be moderated")),
|
('FILE_MODERATION', _("New files to be moderated")),
|
||||||
('SAS_MODERATION', _("New pictures/album to be moderated in the SAS")),
|
('SAS_MODERATION', _("New pictures/album to be moderated in the SAS")),
|
||||||
('NEW_PICTURES', _("You've been identified on some pictures")),
|
('NEW_PICTURES', _("You've been identified on some pictures")),
|
||||||
('REFILLING', _("You just refilled of %s €")),
|
('REFILLING', _("You just refilled of %s €")),
|
||||||
('SELLING', _("You just bought %s")),
|
('SELLING', _("You just bought %s")),
|
||||||
('GENERIC', _("You have a notification")),
|
('GENERIC', _("You have a notification")),
|
||||||
]
|
]
|
||||||
|
|
||||||
SITH_QUICK_NOTIF = {
|
SITH_QUICK_NOTIF = {
|
||||||
'qn_success': _("Success!"),
|
'qn_success': _("Success!"),
|
||||||
'qn_fail': _("Fail!"),
|
'qn_fail': _("Fail!"),
|
||||||
'qn_weekmail_new_article': _("You successfully posted an article in the Weekmail"),
|
'qn_weekmail_new_article': _("You successfully posted an article in the Weekmail"),
|
||||||
'qn_weekmail_article_edit': _("You successfully edited an article in the Weekmail"),
|
'qn_weekmail_article_edit': _("You successfully edited an article in the Weekmail"),
|
||||||
'qn_weekmail_send_success': _("You successfully sent the Weekmail"),
|
'qn_weekmail_send_success': _("You successfully sent the Weekmail"),
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .settings_custom import *
|
from .settings_custom import *
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
from debug_toolbar.panels.templates import TemplatesPanel as BaseTemplatesPanel
|
from debug_toolbar.panels.templates import TemplatesPanel as BaseTemplatesPanel
|
||||||
|
|
||||||
|
|
||||||
class TemplatesPanel(BaseTemplatesPanel):
|
class TemplatesPanel(BaseTemplatesPanel):
|
||||||
def generate_stats(self, *args):
|
def generate_stats(self, *args):
|
||||||
template = self.templates[0]['template']
|
template = self.templates[0]['template']
|
||||||
|
@ -80,4 +80,3 @@ if settings.DEBUG:
|
|||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -26,6 +26,4 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Subscription)
|
admin.site.register(Subscription)
|
||||||
|
@ -35,37 +35,38 @@ from core.models import User
|
|||||||
from core.utils import get_start_of_semester
|
from core.utils import get_start_of_semester
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def validate_type(value):
|
def validate_type(value):
|
||||||
if value not in settings.SITH_SUBSCRIPTIONS.keys():
|
if value not in settings.SITH_SUBSCRIPTIONS.keys():
|
||||||
raise ValidationError(_('Bad subscription type'))
|
raise ValidationError(_('Bad subscription type'))
|
||||||
|
|
||||||
|
|
||||||
def validate_payment(value):
|
def validate_payment(value):
|
||||||
if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD:
|
if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD:
|
||||||
raise ValidationError(_('Bad payment method'))
|
raise ValidationError(_('Bad payment method'))
|
||||||
|
|
||||||
|
|
||||||
class Subscription(models.Model):
|
class Subscription(models.Model):
|
||||||
member = models.ForeignKey(User, related_name='subscriptions')
|
member = models.ForeignKey(User, related_name='subscriptions')
|
||||||
subscription_type = models.CharField(_('subscription type'),
|
subscription_type = models.CharField(_('subscription type'),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
choices=((k, v['name']) for k,v in sorted(settings.SITH_SUBSCRIPTIONS.items())))
|
choices=((k, v['name']) for k, v in sorted(settings.SITH_SUBSCRIPTIONS.items())))
|
||||||
subscription_start = models.DateField(_('subscription start'))
|
subscription_start = models.DateField(_('subscription start'))
|
||||||
subscription_end = models.DateField(_('subscription end'))
|
subscription_end = models.DateField(_('subscription end'))
|
||||||
payment_method = models.CharField(_('payment method'),
|
payment_method = models.CharField(_('payment method'),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
choices=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD)
|
choices=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD)
|
||||||
location = models.CharField(choices=settings.SITH_SUBSCRIPTION_LOCATIONS,
|
location = models.CharField(choices=settings.SITH_SUBSCRIPTION_LOCATIONS,
|
||||||
max_length=20, verbose_name=_('location'))
|
max_length=20, verbose_name=_('location'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['subscription_start',]
|
ordering = ['subscription_start', ]
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
try:
|
try:
|
||||||
for s in Subscription.objects.filter(member=self.member).exclude(pk=self.pk).all():
|
for s in Subscription.objects.filter(member=self.member).exclude(pk=self.pk).all():
|
||||||
if s.is_valid_now():
|
if s.is_valid_now():
|
||||||
raise ValidationError(_("You can not subscribe many time for the same period"))
|
raise ValidationError(_("You can not subscribe many time for the same period"))
|
||||||
except: # This should not happen, because the form should have handled the data before, but sadly, it still
|
except: # This should not happen, because the form should have handled the data before, but sadly, it still
|
||||||
# calls the model validation :'(
|
# calls the model validation :'(
|
||||||
# TODO see SubscriptionForm's clean method
|
# TODO see SubscriptionForm's clean method
|
||||||
raise ValidationError(_("Subscription error"))
|
raise ValidationError(_("Subscription error"))
|
||||||
@ -74,53 +75,53 @@ class Subscription(models.Model):
|
|||||||
super(Subscription, self).save()
|
super(Subscription, self).save()
|
||||||
from counter.models import Customer
|
from counter.models import Customer
|
||||||
if not Customer.objects.filter(user=self.member).exists():
|
if not Customer.objects.filter(user=self.member).exists():
|
||||||
last_id = Customer.objects.count() + 1504 # Number to keep a continuity with the old site
|
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()
|
Customer(user=self.member, account_id=Customer.generate_account_id(last_id + 1), amount=0).save()
|
||||||
form = PasswordResetForm({'email': self.member.email})
|
form = PasswordResetForm({'email': self.member.email})
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save(use_https=True, email_template_name='core/new_user_email.jinja',
|
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")
|
subject_template_name='core/new_user_email_subject.jinja', from_email="ae@utbm.fr")
|
||||||
self.member.make_home()
|
self.member.make_home()
|
||||||
if settings.IS_OLD_MYSQL_PRESENT:
|
if settings.IS_OLD_MYSQL_PRESENT:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
try: # Create subscription on the old site: TODO remove me!
|
try: # Create subscription on the old site: TODO remove me!
|
||||||
LOCATION = {
|
LOCATION = {
|
||||||
"SEVENANS": 5,
|
"SEVENANS": 5,
|
||||||
"BELFORT": 6,
|
"BELFORT": 6,
|
||||||
"MONTBELIARD": 9,
|
"MONTBELIARD": 9,
|
||||||
"EBOUTIC": 5,
|
"EBOUTIC": 5,
|
||||||
}
|
}
|
||||||
TYPE = {
|
TYPE = {
|
||||||
'un-semestre' : 0,
|
'un-semestre': 0,
|
||||||
'deux-semestres' : 1,
|
'deux-semestres': 1,
|
||||||
'cursus-tronc-commun' : 2,
|
'cursus-tronc-commun': 2,
|
||||||
'cursus-branche' : 3,
|
'cursus-branche': 3,
|
||||||
'membre-honoraire' : 4,
|
'membre-honoraire': 4,
|
||||||
'assidu' : 5,
|
'assidu': 5,
|
||||||
'amicale/doceo' : 6,
|
'amicale/doceo': 6,
|
||||||
'reseau-ut' : 7,
|
'reseau-ut': 7,
|
||||||
'crous' : 8,
|
'crous': 8,
|
||||||
'sbarro/esta' : 9,
|
'sbarro/esta': 9,
|
||||||
'cursus-alternant' : 10,
|
'cursus-alternant': 10,
|
||||||
}
|
}
|
||||||
PAYMENT = {
|
PAYMENT = {
|
||||||
"CHECK" : 1,
|
"CHECK": 1,
|
||||||
"CARD" : 2,
|
"CARD": 2,
|
||||||
"CASH" : 3,
|
"CASH": 3,
|
||||||
"OTHER" : 4,
|
"OTHER": 4,
|
||||||
"EBOUTIC" : 5,
|
"EBOUTIC": 5,
|
||||||
"OTHER" : 0,
|
"OTHER": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
|
db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute("""INSERT INTO ae_cotisations (id_utilisateur, date_cotis, date_fin_cotis, mode_paiement_cotis,
|
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,
|
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],
|
self.subscription_end, PAYMENT[self.payment_method], TYPE[self.subscription_type],
|
||||||
LOCATION[self.location]))
|
LOCATION[self.location]))
|
||||||
db.commit()
|
db.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
with open(settings.BASE_DIR+"/subscription_fail.log", "a") as f:
|
with open(settings.BASE_DIR + "/subscription_fail.log", "a") as f:
|
||||||
print("FAIL to add subscription to %s to old site" % (self.member), file=f)
|
print("FAIL to add subscription to %s to old site" % (self.member), file=f)
|
||||||
print("Reason: %s" % (repr(e)), file=f)
|
print("Reason: %s" % (repr(e)), file=f)
|
||||||
db.rollback()
|
db.rollback()
|
||||||
@ -130,10 +131,9 @@ class Subscription(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if hasattr(self, "member") and self.member is not None:
|
if hasattr(self, "member") and self.member is not None:
|
||||||
return self.member.username+' - '+str(self.pk)
|
return self.member.username + ' - ' + str(self.pk)
|
||||||
else:
|
else:
|
||||||
return 'No user - '+str(self.pk)
|
return 'No user - ' + str(self.pk)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_start(d=date.today(), duration=1):
|
def compute_start(d=date.today(), duration=1):
|
||||||
@ -146,7 +146,7 @@ class Subscription(models.Model):
|
|||||||
2015-03-17 -> 2015-02-15
|
2015-03-17 -> 2015-02-15
|
||||||
2015-01-11 -> 2014-08-15
|
2015-01-11 -> 2014-08-15
|
||||||
"""
|
"""
|
||||||
if duration <= 2: # Sliding subscriptions for 1 or 2 semesters
|
if duration <= 2: # Sliding subscriptions for 1 or 2 semesters
|
||||||
return d
|
return d
|
||||||
return get_start_of_semester(d)
|
return get_start_of_semester(d)
|
||||||
|
|
||||||
@ -165,12 +165,11 @@ class Subscription(models.Model):
|
|||||||
start = Subscription.compute_start(duration=duration)
|
start = Subscription.compute_start(duration=duration)
|
||||||
# This can certainly be simplified, but it works like this
|
# This can certainly be simplified, but it works like this
|
||||||
try:
|
try:
|
||||||
return start.replace(month=(start.month-1+6*duration)%12+1,
|
return start.replace(month=(start.month - 1 + 6 * duration) % 12 + 1,
|
||||||
year=start.year+int(duration/2)+(1 if start.month > 6 and duration%2 == 1 else 0))
|
year=start.year + int(duration / 2) + (1 if start.month > 6 and duration % 2 == 1 else 0))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return start.replace(day=1, month=(start.month+6*duration)%12+1,
|
return start.replace(day=1, month=(start.month + 6 * duration) % 12 + 1,
|
||||||
year=start.year+int(duration/2)+(1 if start.month > 6 and duration%2 == 1 else 0))
|
year=start.year + int(duration / 2) + (1 if start.month > 6 and duration % 2 == 1 else 0))
|
||||||
|
|
||||||
|
|
||||||
def can_be_edited_by(self, user):
|
def can_be_edited_by(self, user):
|
||||||
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
|
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
|
||||||
@ -178,10 +177,15 @@ class Subscription(models.Model):
|
|||||||
def is_valid_now(self):
|
def is_valid_now(self):
|
||||||
return self.subscription_start <= date.today() and date.today() <= self.subscription_end
|
return self.subscription_start <= date.today() and date.today() <= self.subscription_end
|
||||||
|
|
||||||
|
|
||||||
def guy_test(date, duration=4):
|
def guy_test(date, duration=4):
|
||||||
print(str(date)+" - "+str(duration)+" -> "+str(Subscription.compute_start(date, duration)))
|
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_start(date, duration)))
|
||||||
|
|
||||||
|
|
||||||
def bibou_test(duration, date=date.today()):
|
def bibou_test(duration, date=date.today()):
|
||||||
print(str(date)+" - "+str(duration)+" -> "+str(Subscription.compute_end(duration, Subscription.compute_start(date, duration))))
|
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_end(duration, Subscription.compute_start(date, duration))))
|
||||||
|
|
||||||
|
|
||||||
def guy():
|
def guy():
|
||||||
guy_test(date(2015, 7, 11))
|
guy_test(date(2015, 7, 11))
|
||||||
guy_test(date(2015, 8, 11))
|
guy_test(date(2015, 8, 11))
|
||||||
@ -191,7 +195,7 @@ def guy():
|
|||||||
guy_test(date(2015, 2, 11))
|
guy_test(date(2015, 2, 11))
|
||||||
guy_test(date(2015, 8, 17))
|
guy_test(date(2015, 8, 17))
|
||||||
guy_test(date(2015, 9, 17))
|
guy_test(date(2015, 9, 17))
|
||||||
print('='*80)
|
print('=' * 80)
|
||||||
guy_test(date(2015, 7, 11), 1)
|
guy_test(date(2015, 7, 11), 1)
|
||||||
guy_test(date(2015, 8, 11), 2)
|
guy_test(date(2015, 8, 11), 2)
|
||||||
guy_test(date(2015, 2, 17), 3)
|
guy_test(date(2015, 2, 17), 3)
|
||||||
@ -200,7 +204,7 @@ def guy():
|
|||||||
guy_test(date(2015, 2, 11), 2)
|
guy_test(date(2015, 2, 11), 2)
|
||||||
guy_test(date(2015, 8, 17), 3)
|
guy_test(date(2015, 8, 17), 3)
|
||||||
guy_test(date(2015, 9, 17), 4)
|
guy_test(date(2015, 9, 17), 4)
|
||||||
print('='*80)
|
print('=' * 80)
|
||||||
bibou_test(1, date(2015, 2, 18))
|
bibou_test(1, date(2015, 2, 18))
|
||||||
bibou_test(2, date(2015, 2, 18))
|
bibou_test(2, date(2015, 2, 18))
|
||||||
bibou_test(3, date(2015, 2, 18))
|
bibou_test(3, date(2015, 2, 18))
|
||||||
@ -209,7 +213,7 @@ def guy():
|
|||||||
bibou_test(2, date(2015, 9, 18))
|
bibou_test(2, date(2015, 9, 18))
|
||||||
bibou_test(3, date(2015, 9, 18))
|
bibou_test(3, date(2015, 9, 18))
|
||||||
bibou_test(4, date(2015, 9, 18))
|
bibou_test(4, date(2015, 9, 18))
|
||||||
print('='*80)
|
print('=' * 80)
|
||||||
bibou_test(1, date(2000, 2, 29))
|
bibou_test(1, date(2000, 2, 29))
|
||||||
bibou_test(2, date(2000, 2, 29))
|
bibou_test(2, date(2000, 2, 29))
|
||||||
bibou_test(1, date(2000, 5, 31))
|
bibou_test(1, date(2000, 5, 31))
|
||||||
@ -219,5 +223,6 @@ def guy():
|
|||||||
bibou_test(3)
|
bibou_test(3)
|
||||||
bibou_test(4)
|
bibou_test(4)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
guy()
|
guy()
|
||||||
|
@ -31,6 +31,3 @@ urlpatterns = [
|
|||||||
url(r'^$', NewSubscription.as_view(), name='subscription'),
|
url(r'^$', NewSubscription.as_view(), name='subscription'),
|
||||||
url(r'stats', SubscriptionsStatsView.as_view(), name='stats'),
|
url(r'stats', SubscriptionsStatsView.as_view(), name='stats'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,16 +26,13 @@ from django.views.generic.edit import CreateView, FormView
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.db import IntegrityError
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import Select
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from ajax_select.fields import AutoCompleteSelectField
|
from ajax_select.fields import AutoCompleteSelectField
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
|
|
||||||
from core.views.forms import SelectDateTime
|
from core.views.forms import SelectDateTime
|
||||||
from core.models import User
|
from core.models import User
|
||||||
|
|
||||||
@ -85,9 +82,9 @@ class SubscriptionForm(forms.ModelForm):
|
|||||||
if User.objects.filter(email=cleaned_data.get("email")).first() is not None:
|
if User.objects.filter(email=cleaned_data.get("email")).first() is not None:
|
||||||
self.add_error("email", ValidationError(_("A user with that email address already exists")))
|
self.add_error("email", ValidationError(_("A user with that email address already exists")))
|
||||||
else:
|
else:
|
||||||
u = User(last_name = self.cleaned_data.get("last_name"),
|
u = User(last_name=self.cleaned_data.get("last_name"),
|
||||||
first_name = self.cleaned_data.get("first_name"),
|
first_name=self.cleaned_data.get("first_name"),
|
||||||
email = self.cleaned_data.get("email"))
|
email=self.cleaned_data.get("email"))
|
||||||
u.generate_username()
|
u.generate_username()
|
||||||
u.set_password(str(random.randrange(1000000, 10000000)))
|
u.set_password(str(random.randrange(1000000, 10000000)))
|
||||||
u.save()
|
u.save()
|
||||||
@ -102,6 +99,7 @@ class SubscriptionForm(forms.ModelForm):
|
|||||||
raise ValidationError(_("You must either choose an existing user or create a new one properly"))
|
raise ValidationError(_("You must either choose an existing user or create a new one properly"))
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class NewSubscription(CreateView):
|
class NewSubscription(CreateView):
|
||||||
template_name = 'subscription/subscription.jinja'
|
template_name = 'subscription/subscription.jinja'
|
||||||
form_class = SubscriptionForm
|
form_class = SubscriptionForm
|
||||||
@ -119,11 +117,11 @@ class NewSubscription(CreateView):
|
|||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.subscription_start = Subscription.compute_start(
|
form.instance.subscription_start = Subscription.compute_start(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[form.instance.subscription_type]['duration'])
|
duration=settings.SITH_SUBSCRIPTIONS[form.instance.subscription_type]['duration'])
|
||||||
form.instance.subscription_end = Subscription.compute_end(
|
form.instance.subscription_end = Subscription.compute_end(
|
||||||
duration=settings.SITH_SUBSCRIPTIONS[form.instance.subscription_type]['duration'],
|
duration=settings.SITH_SUBSCRIPTIONS[form.instance.subscription_type]['duration'],
|
||||||
start=form.instance.subscription_start
|
start=form.instance.subscription_start
|
||||||
)
|
)
|
||||||
return super(NewSubscription, self).form_valid(form)
|
return super(NewSubscription, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,14 +34,17 @@ from core.models import User
|
|||||||
from core.utils import get_start_of_semester, get_semester
|
from core.utils import get_start_of_semester, get_semester
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
|
|
||||||
|
|
||||||
class TrombiManager(models.Manager):
|
class TrombiManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(TrombiManager, self).get_queryset()
|
return super(TrombiManager, self).get_queryset()
|
||||||
|
|
||||||
|
|
||||||
class AvailableTrombiManager(models.Manager):
|
class AvailableTrombiManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(AvailableTrombiManager,
|
return super(AvailableTrombiManager,
|
||||||
self).get_queryset().filter(subscription_deadline__gte=date.today())
|
self).get_queryset().filter(subscription_deadline__gte=date.today())
|
||||||
|
|
||||||
|
|
||||||
class Trombi(models.Model):
|
class Trombi(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -50,14 +53,14 @@ class Trombi(models.Model):
|
|||||||
its Trombi.
|
its Trombi.
|
||||||
"""
|
"""
|
||||||
subscription_deadline = models.DateField(_('subscription deadline'),
|
subscription_deadline = models.DateField(_('subscription deadline'),
|
||||||
default=date.today, help_text=_("Before this date, users are "
|
default=date.today, help_text=_("Before this date, users are "
|
||||||
"allowed to subscribe to this Trombi. "
|
"allowed to subscribe to this Trombi. "
|
||||||
"After this date, users subscribed will be allowed to comment on each other."))
|
"After this date, users subscribed will be allowed to comment on each other."))
|
||||||
comments_deadline = models.DateField(_('comments deadline'),
|
comments_deadline = models.DateField(_('comments deadline'),
|
||||||
default=date.today, help_text=_("After this date, users won't be "
|
default=date.today, help_text=_("After this date, users won't be "
|
||||||
"able to make comments anymore."))
|
"able to make comments anymore."))
|
||||||
max_chars = models.IntegerField(_('maximum characters'), default=400,
|
max_chars = models.IntegerField(_('maximum characters'), default=400,
|
||||||
help_text=_('Maximum number of characters allowed in a comment.'))
|
help_text=_('Maximum number of characters allowed in a comment.'))
|
||||||
show_profiles = models.BooleanField(_("show users profiles to each other"), default=True)
|
show_profiles = models.BooleanField(_("show users profiles to each other"), default=True)
|
||||||
club = models.OneToOneField(Club, related_name='trombi')
|
club = models.OneToOneField(Club, related_name='trombi')
|
||||||
|
|
||||||
@ -70,7 +73,7 @@ class Trombi(models.Model):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
if self.subscription_deadline > self.comments_deadline:
|
if self.subscription_deadline > self.comments_deadline:
|
||||||
raise ValidationError(_("Closing the subscriptions after the "
|
raise ValidationError(_("Closing the subscriptions after the "
|
||||||
"comments is definitively not a good idea."))
|
"comments is definitively not a good idea."))
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('trombi:detail', kwargs={'trombi_id': self.id})
|
return reverse('trombi:detail', kwargs={'trombi_id': self.id})
|
||||||
@ -81,6 +84,7 @@ class Trombi(models.Model):
|
|||||||
def can_be_viewed_by(self, user):
|
def can_be_viewed_by(self, user):
|
||||||
return user.id in [u.user.id for u in self.users.all()]
|
return user.id in [u.user.id for u in self.users.all()]
|
||||||
|
|
||||||
|
|
||||||
class TrombiUser(models.Model):
|
class TrombiUser(models.Model):
|
||||||
"""
|
"""
|
||||||
This class is only here to avoid cross references between the core, club,
|
This class is only here to avoid cross references between the core, club,
|
||||||
@ -92,9 +96,9 @@ class TrombiUser(models.Model):
|
|||||||
user = models.OneToOneField(User, verbose_name=_("trombi user"), related_name='trombi_user')
|
user = models.OneToOneField(User, verbose_name=_("trombi user"), related_name='trombi_user')
|
||||||
trombi = models.ForeignKey(Trombi, verbose_name=_("trombi"), related_name='users', blank=True, null=True, on_delete=models.SET_NULL)
|
trombi = models.ForeignKey(Trombi, verbose_name=_("trombi"), related_name='users', blank=True, null=True, on_delete=models.SET_NULL)
|
||||||
profile_pict = models.ImageField(upload_to='trombi', verbose_name=_("profile pict"), null=True, blank=True,
|
profile_pict = models.ImageField(upload_to='trombi', verbose_name=_("profile pict"), null=True, blank=True,
|
||||||
help_text=_("The profile picture you want in the trombi (warning: this picture may be published)"))
|
help_text=_("The profile picture you want in the trombi (warning: this picture may be published)"))
|
||||||
scrub_pict = models.ImageField(upload_to='trombi', verbose_name=_("scrub pict"), null=True, blank=True,
|
scrub_pict = models.ImageField(upload_to='trombi', verbose_name=_("scrub pict"), null=True, blank=True,
|
||||||
help_text=_("The scrub picture you want in the trombi (warning: this picture may be published)"))
|
help_text=_("The scrub picture you want in the trombi (warning: this picture may be published)"))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.user)
|
return str(self.user)
|
||||||
@ -113,12 +117,13 @@ class TrombiUser(models.Model):
|
|||||||
else:
|
else:
|
||||||
end_date = ""
|
end_date = ""
|
||||||
TrombiClubMembership(
|
TrombiClubMembership(
|
||||||
user=self,
|
user=self,
|
||||||
club=str(m.club),
|
club=str(m.club),
|
||||||
role=role[:64],
|
role=role[:64],
|
||||||
start=get_semester(m.start_date),
|
start=get_semester(m.start_date),
|
||||||
end=end_date,
|
end=end_date,
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
|
|
||||||
class TrombiComment(models.Model):
|
class TrombiComment(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -135,6 +140,7 @@ class TrombiComment(models.Model):
|
|||||||
return False
|
return False
|
||||||
return user.id == self.author.user.id or user.can_edit(self.author.trombi)
|
return user.id == self.author.user.id or user.can_edit(self.author.trombi)
|
||||||
|
|
||||||
|
|
||||||
class TrombiClubMembership(models.Model):
|
class TrombiClubMembership(models.Model):
|
||||||
"""
|
"""
|
||||||
This represent a membership to a club
|
This represent a membership to a club
|
||||||
@ -156,4 +162,3 @@ class TrombiClubMembership(models.Model):
|
|||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('trombi:profile')
|
return reverse('trombi:profile')
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url
|
||||||
|
|
||||||
from trombi.views import *
|
from trombi.views import *
|
||||||
|
|
||||||
@ -43,4 +43,3 @@ urlpatterns = [
|
|||||||
url(r'^membership/(?P<membership_id>[0-9]+)/edit$', UserTrombiEditMembershipView.as_view(), name='edit_membership'),
|
url(r'^membership/(?P<membership_id>[0-9]+)/edit$', UserTrombiEditMembershipView.as_view(), name='edit_membership'),
|
||||||
url(r'^membership/(?P<membership_id>[0-9]+)/delete$', UserTrombiDeleteMembershipView.as_view(), name='delete_membership'),
|
url(r'^membership/(?P<membership_id>[0-9]+)/delete$', UserTrombiDeleteMembershipView.as_view(), name='delete_membership'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
145
trombi/views.py
145
trombi/views.py
@ -23,10 +23,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
from django.views.generic import DetailView, RedirectView, TemplateView
|
||||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView, SingleObjectMixin
|
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -35,11 +35,12 @@ from django.forms.models import modelform_factory
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from trombi.models import Trombi, TrombiUser, TrombiComment, TrombiClubMembership
|
from trombi.models import Trombi, TrombiUser, TrombiComment, TrombiClubMembership
|
||||||
from core.views.forms import SelectFile, SelectDate
|
from core.views.forms import SelectDate
|
||||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
|
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
|
|
||||||
|
|
||||||
class TrombiTabsMixin(TabedViewMixin):
|
class TrombiTabsMixin(TabedViewMixin):
|
||||||
def get_tabs_title(self):
|
def get_tabs_title(self):
|
||||||
return _("Trombi")
|
return _("Trombi")
|
||||||
@ -47,39 +48,42 @@ class TrombiTabsMixin(TabedViewMixin):
|
|||||||
def get_list_of_tabs(self):
|
def get_list_of_tabs(self):
|
||||||
tab_list = []
|
tab_list = []
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('trombi:user_tools'),
|
'url': reverse('trombi:user_tools'),
|
||||||
'slug': 'tools',
|
'slug': 'tools',
|
||||||
'name': _("Tools"),
|
'name': _("Tools"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('trombi:profile'),
|
'url': reverse('trombi:profile'),
|
||||||
'slug': 'profile',
|
'slug': 'profile',
|
||||||
'name': _("My profile"),
|
'name': _("My profile"),
|
||||||
})
|
})
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('trombi:pictures'),
|
'url': reverse('trombi:pictures'),
|
||||||
'slug': 'pictures',
|
'slug': 'pictures',
|
||||||
'name': _("My pictures"),
|
'name': _("My pictures"),
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
trombi = self.request.user.trombi_user.trombi
|
trombi = self.request.user.trombi_user.trombi
|
||||||
if self.request.user.is_owner(trombi):
|
if self.request.user.is_owner(trombi):
|
||||||
tab_list.append({
|
tab_list.append({
|
||||||
'url': reverse('trombi:detail', kwargs={'trombi_id': trombi.id}),
|
'url': reverse('trombi:detail', kwargs={'trombi_id': trombi.id}),
|
||||||
'slug': 'admin_tools',
|
'slug': 'admin_tools',
|
||||||
'name': _("Admin tools"),
|
'name': _("Admin tools"),
|
||||||
})
|
})
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return tab_list
|
return tab_list
|
||||||
|
|
||||||
|
|
||||||
class TrombiForm(forms.ModelForm):
|
class TrombiForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Trombi
|
model = Trombi
|
||||||
fields = ['subscription_deadline', 'comments_deadline', 'max_chars', 'show_profiles']
|
fields = ['subscription_deadline', 'comments_deadline', 'max_chars', 'show_profiles']
|
||||||
widgets = {
|
widgets = {
|
||||||
'subscription_deadline': SelectDate,
|
'subscription_deadline': SelectDate,
|
||||||
'comments_deadline': SelectDate,
|
'comments_deadline': SelectDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TrombiCreateView(CanCreateMixin, CreateView):
|
class TrombiCreateView(CanCreateMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
@ -102,6 +106,7 @@ class TrombiCreateView(CanCreateMixin, CreateView):
|
|||||||
else:
|
else:
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
|
class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
|
||||||
model = Trombi
|
model = Trombi
|
||||||
form_class = TrombiForm
|
form_class = TrombiForm
|
||||||
@ -110,7 +115,8 @@ class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
|
|||||||
current_tab = "admin_tools"
|
current_tab = "admin_tools"
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return super(TrombiEditView, self).get_success_url()+"?qn_success"
|
return super(TrombiEditView, self).get_success_url() + "?qn_success"
|
||||||
|
|
||||||
|
|
||||||
class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
||||||
model = Trombi
|
model = Trombi
|
||||||
@ -118,6 +124,7 @@ class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailVie
|
|||||||
pk_url_kwarg = 'trombi_id'
|
pk_url_kwarg = 'trombi_id'
|
||||||
current_tab = "admin_tools"
|
current_tab = "admin_tools"
|
||||||
|
|
||||||
|
|
||||||
class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
|
class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
|
||||||
model = TrombiUser
|
model = TrombiUser
|
||||||
pk_url_kwarg = 'user_id'
|
pk_url_kwarg = 'user_id'
|
||||||
@ -127,6 +134,7 @@ class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('trombi:detail', kwargs={'trombi_id': self.object.trombi.id}) + "?qn_success"
|
return reverse('trombi:detail', kwargs={'trombi_id': self.object.trombi.id}) + "?qn_success"
|
||||||
|
|
||||||
|
|
||||||
class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
||||||
model = Trombi
|
model = Trombi
|
||||||
template_name = 'trombi/comment_moderation.jinja'
|
template_name = 'trombi/comment_moderation.jinja'
|
||||||
@ -136,13 +144,15 @@ class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMi
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs = super(TrombiModerateCommentsView, self).get_context_data(**kwargs)
|
kwargs = super(TrombiModerateCommentsView, self).get_context_data(**kwargs)
|
||||||
kwargs['comments'] = TrombiComment.objects.filter(is_moderated=False,
|
kwargs['comments'] = TrombiComment.objects.filter(is_moderated=False,
|
||||||
author__trombi__id=self.object.id).exclude(target__user__id=self.request.user.id)
|
author__trombi__id=self.object.id).exclude(target__user__id=self.request.user.id)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class TrombiModerateForm(forms.Form):
|
class TrombiModerateForm(forms.Form):
|
||||||
reason = forms.CharField(help_text=_("Explain why you rejected the comment"))
|
reason = forms.CharField(help_text=_("Explain why you rejected the comment"))
|
||||||
action = forms.CharField(initial="delete", widget=forms.widgets.HiddenInput)
|
action = forms.CharField(initial="delete", widget=forms.widgets.HiddenInput)
|
||||||
|
|
||||||
|
|
||||||
class TrombiModerateCommentView(DetailView):
|
class TrombiModerateCommentView(DetailView):
|
||||||
model = TrombiComment
|
model = TrombiComment
|
||||||
template_name = 'core/edit.jinja'
|
template_name = 'core/edit.jinja'
|
||||||
@ -159,42 +169,45 @@ class TrombiModerateCommentView(DetailView):
|
|||||||
if request.POST['action'] == "accept":
|
if request.POST['action'] == "accept":
|
||||||
self.object.is_moderated = True
|
self.object.is_moderated = True
|
||||||
self.object.save()
|
self.object.save()
|
||||||
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id})+"?qn_success")
|
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id}) + "?qn_success")
|
||||||
elif request.POST['action'] == "reject":
|
elif request.POST['action'] == "reject":
|
||||||
return super(TrombiModerateCommentView, self).get(request, *args, **kwargs)
|
return super(TrombiModerateCommentView, self).get(request, *args, **kwargs)
|
||||||
elif request.POST['action'] == "delete" and "reason" in request.POST.keys():
|
elif request.POST['action'] == "delete" and "reason" in request.POST.keys():
|
||||||
self.object.author.user.email_user(
|
self.object.author.user.email_user(
|
||||||
subject="[%s] %s" % (settings.SITH_NAME, _("Rejected comment")),
|
subject="[%s] %s" % (settings.SITH_NAME, _("Rejected comment")),
|
||||||
message=_("Your comment to %(target)s on the Trombi \"%(trombi)s\" was rejected for the following "
|
message=_("Your comment to %(target)s on the Trombi \"%(trombi)s\" was rejected for the following "
|
||||||
"reason: %(reason)s\n\n"
|
"reason: %(reason)s\n\n"
|
||||||
"Your comment was:\n\n%(content)s"
|
"Your comment was:\n\n%(content)s"
|
||||||
) % {
|
) % {
|
||||||
'target': self.object.target.user.get_display_name(),
|
'target': self.object.target.user.get_display_name(),
|
||||||
'trombi': self.object.author.trombi,
|
'trombi': self.object.author.trombi,
|
||||||
'reason': request.POST["reason"],
|
'reason': request.POST["reason"],
|
||||||
'content': self.object.content,
|
'content': self.object.content,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.object.delete()
|
self.object.delete()
|
||||||
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id})+"?qn_success")
|
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id}) + "?qn_success")
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs = super(TrombiModerateCommentView, self).get_context_data(**kwargs)
|
kwargs = super(TrombiModerateCommentView, self).get_context_data(**kwargs)
|
||||||
kwargs['form'] = TrombiModerateForm()
|
kwargs['form'] = TrombiModerateForm()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
# User side
|
# User side
|
||||||
|
|
||||||
|
|
||||||
class TrombiModelChoiceField(forms.ModelChoiceField):
|
class TrombiModelChoiceField(forms.ModelChoiceField):
|
||||||
def label_from_instance(self, obj):
|
def label_from_instance(self, obj):
|
||||||
return _("%(name)s (deadline: %(date)s)") % {'name': str(obj), 'date': str(obj.subscription_deadline)}
|
return _("%(name)s (deadline: %(date)s)") % {'name': str(obj), 'date': str(obj.subscription_deadline)}
|
||||||
|
|
||||||
|
|
||||||
class UserTrombiForm(forms.Form):
|
class UserTrombiForm(forms.Form):
|
||||||
trombi = TrombiModelChoiceField(Trombi.availables.all(), required=False, label=_("Select trombi"),
|
trombi = TrombiModelChoiceField(Trombi.availables.all(), required=False, label=_("Select trombi"),
|
||||||
help_text=_("This allows you to subscribe to a Trombi. "
|
help_text=_("This allows you to subscribe to a Trombi. "
|
||||||
"Be aware that you can subscribe only once, so don't play with that, "
|
"Be aware that you can subscribe only once, so don't play with that, "
|
||||||
"or you will expose yourself to the admins' wrath!"))
|
"or you will expose yourself to the admins' wrath!"))
|
||||||
|
|
||||||
|
|
||||||
class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
||||||
"""
|
"""
|
||||||
@ -207,7 +220,7 @@ class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
|||||||
self.form = UserTrombiForm(request.POST)
|
self.form = UserTrombiForm(request.POST)
|
||||||
if self.form.is_valid():
|
if self.form.is_valid():
|
||||||
trombi_user = TrombiUser(user=request.user,
|
trombi_user = TrombiUser(user=request.user,
|
||||||
trombi=self.form.cleaned_data['trombi'])
|
trombi=self.form.cleaned_data['trombi'])
|
||||||
trombi_user.save()
|
trombi_user.save()
|
||||||
self.quick_notif_list += ['qn_success']
|
self.quick_notif_list += ['qn_success']
|
||||||
return super(UserTrombiToolsView, self).get(request, *args, **kwargs)
|
return super(UserTrombiToolsView, self).get(request, *args, **kwargs)
|
||||||
@ -222,6 +235,7 @@ class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
|||||||
kwargs['date'] = date
|
kwargs['date'] = date
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
|
class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
|
||||||
model = TrombiUser
|
model = TrombiUser
|
||||||
fields = ['profile_pict', 'scrub_pict']
|
fields = ['profile_pict', 'scrub_pict']
|
||||||
@ -232,18 +246,19 @@ class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
|
|||||||
return self.request.user.trombi_user
|
return self.request.user.trombi_user
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('trombi:user_tools')+"?qn_success"
|
return reverse('trombi:user_tools') + "?qn_success"
|
||||||
|
|
||||||
|
|
||||||
class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
|
class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
|
||||||
model = User
|
model = User
|
||||||
form_class = modelform_factory(User,
|
form_class = modelform_factory(User,
|
||||||
fields=['second_email', 'phone', 'department', 'dpt_option',
|
fields=['second_email', 'phone', 'department', 'dpt_option',
|
||||||
'quote', 'parent_address'],
|
'quote', 'parent_address'],
|
||||||
labels={
|
labels={
|
||||||
'second_email': _("Personal email (not UTBM)"),
|
'second_email': _("Personal email (not UTBM)"),
|
||||||
'phone': _("Phone"),
|
'phone': _("Phone"),
|
||||||
'parent_address': _("Native town"),
|
'parent_address': _("Native town"),
|
||||||
})
|
})
|
||||||
template_name = "trombi/edit_profile.jinja"
|
template_name = "trombi/edit_profile.jinja"
|
||||||
current_tab = "profile"
|
current_tab = "profile"
|
||||||
|
|
||||||
@ -251,7 +266,8 @@ class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
|
|||||||
return self.request.user
|
return self.request.user
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('trombi:user_tools')+"?qn_success"
|
return reverse('trombi:user_tools') + "?qn_success"
|
||||||
|
|
||||||
|
|
||||||
class UserTrombiResetClubMembershipsView(RedirectView):
|
class UserTrombiResetClubMembershipsView(RedirectView):
|
||||||
permanent = False
|
permanent = False
|
||||||
@ -262,7 +278,8 @@ class UserTrombiResetClubMembershipsView(RedirectView):
|
|||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('trombi:profile')+"?qn_success"
|
return reverse('trombi:profile') + "?qn_success"
|
||||||
|
|
||||||
|
|
||||||
class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
|
class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
|
||||||
model = TrombiClubMembership
|
model = TrombiClubMembership
|
||||||
@ -274,6 +291,7 @@ class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return super(UserTrombiDeleteMembershipView, self).get_success_url() + "?qn_success"
|
return super(UserTrombiDeleteMembershipView, self).get_success_url() + "?qn_success"
|
||||||
|
|
||||||
|
|
||||||
class UserTrombiEditMembershipView(CanEditMixin, TrombiTabsMixin, UpdateView):
|
class UserTrombiEditMembershipView(CanEditMixin, TrombiTabsMixin, UpdateView):
|
||||||
model = TrombiClubMembership
|
model = TrombiClubMembership
|
||||||
pk_url_kwarg = "membership_id"
|
pk_url_kwarg = "membership_id"
|
||||||
@ -300,6 +318,7 @@ class UserTrombiProfileView(TrombiTabsMixin, DetailView):
|
|||||||
raise Http404()
|
raise Http404()
|
||||||
return super(UserTrombiProfileView, self).get(request, *args, **kwargs)
|
return super(UserTrombiProfileView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class TrombiCommentFormView():
|
class TrombiCommentFormView():
|
||||||
"""
|
"""
|
||||||
Create/edit a trombi comment
|
Create/edit a trombi comment
|
||||||
@ -312,20 +331,20 @@ class TrombiCommentFormView():
|
|||||||
self.trombi = self.request.user.trombi_user.trombi
|
self.trombi = self.request.user.trombi_user.trombi
|
||||||
if date.today() <= self.trombi.subscription_deadline:
|
if date.today() <= self.trombi.subscription_deadline:
|
||||||
raise Http404(_("You can not yet write comment, you must wait for "
|
raise Http404(_("You can not yet write comment, you must wait for "
|
||||||
"the subscription deadline to be passed."))
|
"the subscription deadline to be passed."))
|
||||||
if self.trombi.comments_deadline < date.today():
|
if self.trombi.comments_deadline < date.today():
|
||||||
raise Http404(_("You can not write comment anymore, the deadline is "
|
raise Http404(_("You can not write comment anymore, the deadline is "
|
||||||
"already passed."))
|
"already passed."))
|
||||||
return modelform_factory(self.model, fields=self.fields,
|
return modelform_factory(self.model, fields=self.fields,
|
||||||
widgets={
|
widgets={
|
||||||
'content': forms.widgets.Textarea(attrs={'maxlength': self.trombi.max_chars})
|
'content': forms.widgets.Textarea(attrs={'maxlength': self.trombi.max_chars})
|
||||||
},
|
},
|
||||||
help_texts={
|
help_texts={
|
||||||
'content': _("Maximum characters: %(max_length)s") % {'max_length': self.trombi.max_chars}
|
'content': _("Maximum characters: %(max_length)s") % {'max_length': self.trombi.max_chars}
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('trombi:user_tools')+"?qn_success"
|
return reverse('trombi:user_tools') + "?qn_success"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs = super(TrombiCommentFormView, self).get_context_data(**kwargs)
|
kwargs = super(TrombiCommentFormView, self).get_context_data(**kwargs)
|
||||||
@ -335,6 +354,7 @@ class TrombiCommentFormView():
|
|||||||
kwargs['target'] = self.object.target
|
kwargs['target'] = self.object.target
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
|
class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
target = get_object_or_404(TrombiUser, id=self.kwargs['user_id'])
|
target = get_object_or_404(TrombiUser, id=self.kwargs['user_id'])
|
||||||
@ -342,11 +362,10 @@ class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
|
|||||||
form.instance.target = target
|
form.instance.target = target
|
||||||
return super(TrombiCommentCreateView, self).form_valid(form)
|
return super(TrombiCommentCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class TrombiCommentEditView(TrombiCommentFormView, CanViewMixin, UpdateView):
|
class TrombiCommentEditView(TrombiCommentFormView, CanViewMixin, UpdateView):
|
||||||
pk_url_kwarg = "comment_id"
|
pk_url_kwarg = "comment_id"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.is_moderated = False
|
form.instance.is_moderated = False
|
||||||
return super(TrombiCommentEditView, self).form_valid(form)
|
return super(TrombiCommentEditView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user