Merge branch 'black' into 'master'

Passer black sur tout le repo et l'imposer

See merge request ae/Sith!159
This commit is contained in:
Soldat 2018-10-05 23:26:57 +02:00
commit a88430d43b
208 changed files with 13550 additions and 6552 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ db.sqlite3
*.mo
*__pycache__*
.DS_Store
.vscode/
env/
doc/html
data/

View File

@ -1,4 +1,8 @@
stages:
- test
test:
stage: test
script:
- apt-get update
- apt-get install -y gettext
@ -11,3 +15,9 @@ test:
artifacts:
paths:
- coverage_report/
black:
stage: test
script:
- pip install black
- black --check .

View File

@ -4,7 +4,7 @@
Hey ! Tu veux devenir un mec bien et en plus devenir bon en python si tu l'es pas déjà ?
Il se trouve que le sith AE prévu pour l'été 2016 a besoin de toi !
Pour faire le sith, on utilise le framework Web [Django](https://docs.djangoproject.com/fr/1.8/intro/)
Pour faire le sith, on utilise le framework Web [Django](https://docs.djangoproject.com/fr/1.11/intro/)
N'hésite pas à lire les tutos et à nous demander (ae.info@utbm.fr).
Bon, passons aux choses sérieuses, pour bidouiller le sith sans le casser :
@ -17,31 +17,81 @@ Ensuite, tu fais :
`git clone https://ae-dev.utbm.fr/ae/Sith.git`
Avec cette commande, tu clones le sith AE dans le dossier courant.
```bash
cd Sith
virtualenv --clear --python=python3 env_sith
source env_sith/bin/activate
pip install -r requirements.txt
```
Maintenant, faut passer le sith en mode debug dans le fichier de settings personnalisé.
```bash
echo "DEBUG=True" > sith/settings_custom.py
echo 'EXTERNAL_RES = "False"' >> sith/settings_custom.py
echo 'SITH_URL = "localhost:8000"' >> sith/settings_custom.py
```
Enfin, il s'agit de créer la base de donnée de test lors de la première utilisation
```bash
./manage.py setup
répondre no
```
Et pour lancer le sith, tu fais `python3 manage.py runserver`
Voilà, c'est le sith AE. Il y a des issues dans le gitlab qui sont à régler. Si tu as un domaine qui t'intéresse, une appli que tu voudrais développer, n'hésites pas et contacte-nous.
Va, et que l'AE soit avec toi.
# Black
Pour uniformiser le formattage du code nous utilisons [Black](https://github.com/ambv/black). Cela permet d'avoir le même codestyle et donc le codereview prend moins de temps. Tout étant dans le même format, il est plus facile pour chacun de comprendre le code de chacun ! Cela permet aussi d'éviter des erreurs (y parait 🤷‍♀️).
Installation de black:
```bash
pip install black
```
## Sous VsCode:
Attention, pour VsCode, Black doit être installé dans votre virtualenv !
Ajouter ces deux lignes dans les settings de VsCode
```json
{
"python.formatting.provider": "black",
"editor.formatOnSave": true
}
```
## Sous Sublime Text
Il faut installer le plugin [sublack](https://packagecontrol.io/packages/sublack) depuis Package Control.
Il suffit ensuite d'ajouter dans les settings du projet (ou en global)
```json
{
"sublack.black_on_save": true
}
```
Si vous utilisez le plugin [anaconda](http://damnwidget.github.io/anaconda/), pensez à modifier les paramètres du linter pep8 pour éviter de recevoir des warnings dans le formatage de black
```json
{
"pep8_ignore": [
"E203",
"E266",
"E501",
"W503"
]
}
```
Sites et doc cools
------------------
[Classy Class-Based Views](http://ccbv.co.uk/projects/Django/1.8/)
[Classy Class-Based Views](http://ccbv.co.uk/projects/Django/1.11/)
Helpers:

View File

@ -1,5 +1,6 @@
[![pipeline status](https://ae-dev.utbm.fr/ae/Sith/badges/master/pipeline.svg)](https://ae-dev.utbm.fr/ae/Sith/commits/master)
[![coverage report](https://ae-dev.utbm.fr/ae/Sith/badges/master/coverage.svg)](https://ae-dev.utbm.fr/ae/Sith/commits/master)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
## Sith AE

View File

@ -21,4 +21,3 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -8,103 +8,271 @@ import accounting.models
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='AccountingType',
name="AccountingType",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('code', models.CharField(max_length=16, verbose_name='code', validators=[django.core.validators.RegexValidator('^[0-9]*$', 'An accounting type code contains only numbers')])),
('label', models.CharField(max_length=128, verbose_name='label')),
('movement_type', models.CharField(choices=[('CREDIT', 'Credit'), ('DEBIT', 'Debit'), ('NEUTRAL', 'Neutral')], max_length=12, verbose_name='movement type')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
(
"code",
models.CharField(
max_length=16,
verbose_name="code",
validators=[
django.core.validators.RegexValidator(
"^[0-9]*$",
"An accounting type code contains only numbers",
)
],
),
),
("label", models.CharField(max_length=128, verbose_name="label")),
(
"movement_type",
models.CharField(
choices=[
("CREDIT", "Credit"),
("DEBIT", "Debit"),
("NEUTRAL", "Neutral"),
],
max_length=12,
verbose_name="movement type",
),
),
],
options={
'verbose_name': 'accounting type',
'ordering': ['movement_type', 'code'],
"verbose_name": "accounting type",
"ordering": ["movement_type", "code"],
},
),
migrations.CreateModel(
name='BankAccount',
name="BankAccount",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=30, verbose_name='name')),
('iban', models.CharField(max_length=255, blank=True, verbose_name='iban')),
('number', models.CharField(max_length=255, blank=True, verbose_name='account number')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=30, verbose_name="name")),
(
"iban",
models.CharField(max_length=255, blank=True, verbose_name="iban"),
),
(
"number",
models.CharField(
max_length=255, blank=True, verbose_name="account number"
),
),
],
options={"verbose_name": "Bank account", "ordering": ["club", "name"]},
),
migrations.CreateModel(
name="ClubAccount",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=30, verbose_name="name")),
],
options={
'verbose_name': 'Bank account',
'ordering': ['club', 'name'],
"verbose_name": "Club account",
"ordering": ["bank_account", "name"],
},
),
migrations.CreateModel(
name='ClubAccount',
name="Company",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=30, verbose_name='name')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=60, verbose_name="name")),
],
options={
'verbose_name': 'Club account',
'ordering': ['bank_account', 'name'],
},
options={"verbose_name": "company"},
),
migrations.CreateModel(
name='Company',
name="GeneralJournal",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=60, verbose_name='name')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("start_date", models.DateField(verbose_name="start date")),
(
"end_date",
models.DateField(
null=True, verbose_name="end date", default=None, blank=True
),
),
("name", models.CharField(max_length=40, verbose_name="name")),
(
"closed",
models.BooleanField(verbose_name="is closed", default=False),
),
(
"amount",
accounting.models.CurrencyField(
decimal_places=2,
default=0,
verbose_name="amount",
max_digits=12,
),
),
(
"effective_amount",
accounting.models.CurrencyField(
decimal_places=2,
default=0,
verbose_name="effective_amount",
max_digits=12,
),
),
],
options={
'verbose_name': 'company',
},
options={"verbose_name": "General journal", "ordering": ["-start_date"]},
),
migrations.CreateModel(
name='GeneralJournal',
name="Operation",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('start_date', models.DateField(verbose_name='start date')),
('end_date', models.DateField(null=True, verbose_name='end date', default=None, blank=True)),
('name', models.CharField(max_length=40, verbose_name='name')),
('closed', models.BooleanField(verbose_name='is closed', default=False)),
('amount', accounting.models.CurrencyField(decimal_places=2, default=0, verbose_name='amount', max_digits=12)),
('effective_amount', accounting.models.CurrencyField(decimal_places=2, default=0, verbose_name='effective_amount', max_digits=12)),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("number", models.IntegerField(verbose_name="number")),
(
"amount",
accounting.models.CurrencyField(
decimal_places=2, max_digits=12, verbose_name="amount"
),
),
("date", models.DateField(verbose_name="date")),
("remark", models.CharField(max_length=128, verbose_name="comment")),
(
"mode",
models.CharField(
choices=[
("CHECK", "Check"),
("CASH", "Cash"),
("TRANSFERT", "Transfert"),
("CARD", "Credit card"),
],
options={
'verbose_name': 'General journal',
'ordering': ['-start_date'],
},
max_length=255,
verbose_name="payment method",
),
),
(
"cheque_number",
models.CharField(
max_length=32,
null=True,
verbose_name="cheque number",
default="",
blank=True,
),
),
("done", models.BooleanField(verbose_name="is done", default=False)),
(
"target_type",
models.CharField(
choices=[
("USER", "User"),
("CLUB", "Club"),
("ACCOUNT", "Account"),
("COMPANY", "Company"),
("OTHER", "Other"),
],
max_length=10,
verbose_name="target type",
),
),
(
"target_id",
models.IntegerField(
null=True, verbose_name="target id", blank=True
),
),
(
"target_label",
models.CharField(
max_length=32,
blank=True,
verbose_name="target label",
default="",
),
),
(
"accounting_type",
models.ForeignKey(
null=True,
related_name="operations",
verbose_name="accounting type",
to="accounting.AccountingType",
blank=True,
),
),
],
options={"ordering": ["-number"]},
),
migrations.CreateModel(
name='Operation',
name="SimplifiedAccountingType",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('number', models.IntegerField(verbose_name='number')),
('amount', accounting.models.CurrencyField(decimal_places=2, max_digits=12, verbose_name='amount')),
('date', models.DateField(verbose_name='date')),
('remark', models.CharField(max_length=128, verbose_name='comment')),
('mode', models.CharField(choices=[('CHECK', 'Check'), ('CASH', 'Cash'), ('TRANSFERT', 'Transfert'), ('CARD', 'Credit card')], max_length=255, verbose_name='payment method')),
('cheque_number', models.CharField(max_length=32, null=True, verbose_name='cheque number', default='', blank=True)),
('done', models.BooleanField(verbose_name='is done', default=False)),
('target_type', models.CharField(choices=[('USER', 'User'), ('CLUB', 'Club'), ('ACCOUNT', 'Account'), ('COMPANY', 'Company'), ('OTHER', 'Other')], max_length=10, verbose_name='target type')),
('target_id', models.IntegerField(null=True, verbose_name='target id', blank=True)),
('target_label', models.CharField(max_length=32, blank=True, verbose_name='target label', default='')),
('accounting_type', models.ForeignKey(null=True, related_name='operations', verbose_name='accounting type', to='accounting.AccountingType', blank=True)),
],
options={
'ordering': ['-number'],
},
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("label", models.CharField(max_length=128, verbose_name="label")),
(
"accounting_type",
models.ForeignKey(
verbose_name="simplified accounting types",
to="accounting.AccountingType",
related_name="simplified_types",
),
),
migrations.CreateModel(
name='SimplifiedAccountingType',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('label', models.CharField(max_length=128, verbose_name='label')),
('accounting_type', models.ForeignKey(verbose_name='simplified accounting types', to='accounting.AccountingType', related_name='simplified_types')),
],
options={
'verbose_name': 'simplified type',
'ordering': ['accounting_type__movement_type', 'accounting_type__code'],
"verbose_name": "simplified type",
"ordering": ["accounting_type__movement_type", "accounting_type__code"],
},
),
]

View File

@ -7,54 +7,88 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('club', '0001_initial'),
('accounting', '0001_initial'),
('core', '0001_initial'),
("club", "0001_initial"),
("accounting", "0001_initial"),
("core", "0001_initial"),
]
operations = [
migrations.AddField(
model_name='operation',
name='invoice',
field=models.ForeignKey(null=True, related_name='operations', verbose_name='invoice', to='core.SithFile', blank=True),
model_name="operation",
name="invoice",
field=models.ForeignKey(
null=True,
related_name="operations",
verbose_name="invoice",
to="core.SithFile",
blank=True,
),
),
migrations.AddField(
model_name='operation',
name='journal',
field=models.ForeignKey(verbose_name='journal', to='accounting.GeneralJournal', related_name='operations'),
model_name="operation",
name="journal",
field=models.ForeignKey(
verbose_name="journal",
to="accounting.GeneralJournal",
related_name="operations",
),
),
migrations.AddField(
model_name='operation',
name='linked_operation',
field=models.OneToOneField(blank=True, to='accounting.Operation', null=True, related_name='operation_linked_to', verbose_name='linked operation', default=None),
model_name="operation",
name="linked_operation",
field=models.OneToOneField(
blank=True,
to="accounting.Operation",
null=True,
related_name="operation_linked_to",
verbose_name="linked operation",
default=None,
),
),
migrations.AddField(
model_name='operation',
name='simpleaccounting_type',
field=models.ForeignKey(null=True, related_name='operations', verbose_name='simple type', to='accounting.SimplifiedAccountingType', blank=True),
model_name="operation",
name="simpleaccounting_type",
field=models.ForeignKey(
null=True,
related_name="operations",
verbose_name="simple type",
to="accounting.SimplifiedAccountingType",
blank=True,
),
),
migrations.AddField(
model_name='generaljournal',
name='club_account',
field=models.ForeignKey(verbose_name='club account', to='accounting.ClubAccount', related_name='journals'),
model_name="generaljournal",
name="club_account",
field=models.ForeignKey(
verbose_name="club account",
to="accounting.ClubAccount",
related_name="journals",
),
),
migrations.AddField(
model_name='clubaccount',
name='bank_account',
field=models.ForeignKey(verbose_name='bank account', to='accounting.BankAccount', related_name='club_accounts'),
model_name="clubaccount",
name="bank_account",
field=models.ForeignKey(
verbose_name="bank account",
to="accounting.BankAccount",
related_name="club_accounts",
),
),
migrations.AddField(
model_name='clubaccount',
name='club',
field=models.ForeignKey(verbose_name='club', to='club.Club', related_name='club_account'),
model_name="clubaccount",
name="club",
field=models.ForeignKey(
verbose_name="club", to="club.Club", related_name="club_account"
),
),
migrations.AddField(
model_name='bankaccount',
name='club',
field=models.ForeignKey(verbose_name='club', to='club.Club', related_name='bank_accounts'),
model_name="bankaccount",
name="club",
field=models.ForeignKey(
verbose_name="club", to="club.Club", related_name="bank_accounts"
),
),
migrations.AlterUniqueTogether(
name='operation',
unique_together=set([('number', 'journal')]),
name="operation", unique_together=set([("number", "journal")])
),
]

View File

@ -7,44 +7,44 @@ import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
('accounting', '0002_auto_20160824_2152'),
]
dependencies = [("accounting", "0002_auto_20160824_2152")]
operations = [
migrations.AddField(
model_name='company',
name='city',
field=models.CharField(blank=True, verbose_name='city', max_length=60),
model_name="company",
name="city",
field=models.CharField(blank=True, verbose_name="city", max_length=60),
),
migrations.AddField(
model_name='company',
name='country',
field=models.CharField(blank=True, verbose_name='country', max_length=32),
model_name="company",
name="country",
field=models.CharField(blank=True, verbose_name="country", max_length=32),
),
migrations.AddField(
model_name='company',
name='email',
field=models.EmailField(blank=True, verbose_name='email', max_length=254),
model_name="company",
name="email",
field=models.EmailField(blank=True, verbose_name="email", max_length=254),
),
migrations.AddField(
model_name='company',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, verbose_name='phone', max_length=128),
model_name="company",
name="phone",
field=phonenumber_field.modelfields.PhoneNumberField(
blank=True, verbose_name="phone", max_length=128
),
),
migrations.AddField(
model_name='company',
name='postcode',
field=models.CharField(blank=True, verbose_name='postcode', max_length=10),
model_name="company",
name="postcode",
field=models.CharField(blank=True, verbose_name="postcode", max_length=10),
),
migrations.AddField(
model_name='company',
name='street',
field=models.CharField(blank=True, verbose_name='street', max_length=60),
model_name="company",
name="street",
field=models.CharField(blank=True, verbose_name="street", max_length=60),
),
migrations.AddField(
model_name='company',
name='website',
field=models.CharField(blank=True, verbose_name='website', max_length=64),
model_name="company",
name="website",
field=models.CharField(blank=True, verbose_name="website", max_length=64),
),
]

View File

@ -7,26 +7,45 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('accounting', '0003_auto_20160824_2203'),
]
dependencies = [("accounting", "0003_auto_20160824_2203")]
operations = [
migrations.CreateModel(
name='Label',
name="Label",
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', models.CharField(max_length=64, verbose_name='label')),
('club_account', models.ForeignKey(related_name='labels', verbose_name='club account', to='accounting.ClubAccount')),
(
"id",
models.AutoField(
verbose_name="ID",
primary_key=True,
auto_created=True,
serialize=False,
),
),
("name", models.CharField(max_length=64, verbose_name="label")),
(
"club_account",
models.ForeignKey(
related_name="labels",
verbose_name="club account",
to="accounting.ClubAccount",
),
),
],
),
migrations.AddField(
model_name='operation',
name='label',
field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, related_name='operations', null=True, blank=True, verbose_name='label', to='accounting.Label'),
model_name="operation",
name="label",
field=models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL,
related_name="operations",
null=True,
blank=True,
verbose_name="label",
to="accounting.Label",
),
),
migrations.AlterUniqueTogether(
name='label',
unique_together=set([('name', 'club_account')]),
name="label", unique_together=set([("name", "club_account")])
),
]

View File

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0004_auto_20161005_1505'),
]
dependencies = [("accounting", "0004_auto_20161005_1505")]
operations = [
migrations.AlterField(
model_name='operation',
name='remark',
field=models.CharField(null=True, max_length=128, blank=True, verbose_name='comment'),
model_name="operation",
name="remark",
field=models.CharField(
null=True, max_length=128, blank=True, verbose_name="comment"
),
)
]

View File

@ -41,9 +41,10 @@ class CurrencyField(models.DecimalField):
"""
This is a custom database field used for currency
"""
def __init__(self, *args, **kwargs):
kwargs['max_digits'] = 12
kwargs['decimal_places'] = 2
kwargs["max_digits"] = 12
kwargs["decimal_places"] = 2
super(CurrencyField, self).__init__(*args, **kwargs)
def to_python(self, value):
@ -52,18 +53,19 @@ class CurrencyField(models.DecimalField):
except AttributeError:
return None
# Accounting classes
class Company(models.Model):
name = models.CharField(_('name'), max_length=60)
street = models.CharField(_('street'), max_length=60, blank=True)
city = models.CharField(_('city'), max_length=60, blank=True)
postcode = models.CharField(_('postcode'), max_length=10, blank=True)
country = models.CharField(_('country'), max_length=32, blank=True)
phone = PhoneNumberField(_('phone'), blank=True)
email = models.EmailField(_('email'), blank=True)
website = models.CharField(_('website'), max_length=64, blank=True)
name = models.CharField(_("name"), max_length=60)
street = models.CharField(_("street"), max_length=60, blank=True)
city = models.CharField(_("city"), max_length=60, blank=True)
postcode = models.CharField(_("postcode"), max_length=10, blank=True)
country = models.CharField(_("country"), max_length=32, blank=True)
phone = PhoneNumberField(_("phone"), blank=True)
email = models.EmailField(_("email"), blank=True)
website = models.CharField(_("website"), max_length=64, blank=True)
class Meta:
verbose_name = _("company")
@ -81,7 +83,7 @@ class Company(models.Model):
Method to see if that object can be edited by the given user
"""
for club in user.memberships.filter(end_date=None).all():
if club and club.role == settings.SITH_CLUB_ROLES_ID['Treasurer']:
if club and club.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
@ -90,12 +92,12 @@ class Company(models.Model):
Method to see if that object can be viewed by the given user
"""
for club in user.memberships.filter(end_date=None).all():
if club and club.role >= settings.SITH_CLUB_ROLES_ID['Treasurer']:
if club and club.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def get_absolute_url(self):
return reverse('accounting:co_edit', kwargs={'co_id': self.id})
return reverse("accounting:co_edit", kwargs={"co_id": self.id})
def get_display_name(self):
return self.name
@ -105,14 +107,14 @@ class Company(models.Model):
class BankAccount(models.Model):
name = models.CharField(_('name'), max_length=30)
iban = models.CharField(_('iban'), max_length=255, blank=True)
number = models.CharField(_('account number'), max_length=255, blank=True)
name = models.CharField(_("name"), max_length=30)
iban = models.CharField(_("iban"), max_length=255, blank=True)
number = models.CharField(_("account number"), max_length=255, blank=True)
club = models.ForeignKey(Club, related_name="bank_accounts", verbose_name=_("club"))
class Meta:
verbose_name = _("Bank account")
ordering = ['club', 'name']
ordering = ["club", "name"]
def is_owned_by(self, user):
"""
@ -121,25 +123,27 @@ class BankAccount(models.Model):
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
m = self.club.get_membership_for(user)
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID['Treasurer']:
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def get_absolute_url(self):
return reverse('accounting:bank_details', kwargs={'b_account_id': self.id})
return reverse("accounting:bank_details", kwargs={"b_account_id": self.id})
def __str__(self):
return self.name
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"))
bank_account = models.ForeignKey(BankAccount, related_name="club_accounts", verbose_name=_("bank account"))
bank_account = models.ForeignKey(
BankAccount, related_name="club_accounts", verbose_name=_("bank account")
)
class Meta:
verbose_name = _("Club account")
ordering = ['bank_account', 'name']
ordering = ["bank_account", "name"]
def is_owned_by(self, user):
"""
@ -154,7 +158,7 @@ class ClubAccount(models.Model):
Method to see if that object can be edited by the given user
"""
m = self.club.get_membership_for(user)
if m and m.role == settings.SITH_CLUB_ROLES_ID['Treasurer']:
if m and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
@ -163,7 +167,7 @@ class ClubAccount(models.Model):
Method to see if that object can be viewed by the given user
"""
m = self.club.get_membership_for(user)
if m and m.role >= settings.SITH_CLUB_ROLES_ID['Treasurer']:
if m and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
@ -177,30 +181,36 @@ class ClubAccount(models.Model):
return self.journals.filter(closed=False).first()
def get_absolute_url(self):
return reverse('accounting:club_details', kwargs={'c_account_id': self.id})
return reverse("accounting:club_details", kwargs={"c_account_id": self.id})
def __str__(self):
return self.name
def get_display_name(self):
return _("%(club_account)s on %(bank_account)s") % {"club_account": self.name, "bank_account": self.bank_account}
return _("%(club_account)s on %(bank_account)s") % {
"club_account": self.name,
"bank_account": self.bank_account,
}
class GeneralJournal(models.Model):
"""
Class storing all the operations for a period of time
"""
start_date = models.DateField(_('start date'))
end_date = models.DateField(_('end date'), null=True, blank=True, default=None)
name = models.CharField(_('name'), max_length=40)
closed = models.BooleanField(_('is closed'), default=False)
club_account = models.ForeignKey(ClubAccount, related_name="journals", null=False, verbose_name=_("club account"))
amount = CurrencyField(_('amount'), default=0)
effective_amount = CurrencyField(_('effective_amount'), default=0)
start_date = models.DateField(_("start date"))
end_date = models.DateField(_("end date"), null=True, blank=True, default=None)
name = models.CharField(_("name"), max_length=40)
closed = models.BooleanField(_("is closed"), default=False)
club_account = models.ForeignKey(
ClubAccount, related_name="journals", null=False, verbose_name=_("club account")
)
amount = CurrencyField(_("amount"), default=0)
effective_amount = CurrencyField(_("effective_amount"), default=0)
class Meta:
verbose_name = _("General journal")
ordering = ['-start_date']
ordering = ["-start_date"]
def is_owned_by(self, user):
"""
@ -226,7 +236,7 @@ class GeneralJournal(models.Model):
return self.club_account.can_be_viewed_by(user)
def get_absolute_url(self):
return reverse('accounting:journal_details', kwargs={'j_id': self.id})
return reverse("accounting:journal_details", kwargs={"j_id": self.id})
def __str__(self):
return self.name
@ -250,31 +260,79 @@ class Operation(models.Model):
"""
An operation is a line in the journal, a debit or a credit
"""
number = models.IntegerField(_('number'))
journal = models.ForeignKey(GeneralJournal, related_name="operations", null=False, verbose_name=_("journal"))
amount = CurrencyField(_('amount'))
date = models.DateField(_('date'))
remark = models.CharField(_('comment'), max_length=128, null=True, blank=True)
mode = models.CharField(_('payment method'), max_length=255, choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD)
cheque_number = models.CharField(_('cheque number'), max_length=32, default="", 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)
simpleaccounting_type = models.ForeignKey('SimplifiedAccountingType', related_name="operations",
verbose_name=_("simple type"), null=True, blank=True)
accounting_type = models.ForeignKey('AccountingType', related_name="operations",
verbose_name=_("accounting type"), null=True, blank=True)
label = models.ForeignKey('Label', related_name="operations",
verbose_name=_("label"), null=True, blank=True, on_delete=models.SET_NULL)
target_type = models.CharField(_('target type'), max_length=10,
choices=[('USER', _('User')), ('CLUB', _('Club')), ('ACCOUNT', _('Account')), ('COMPANY', _('Company')), ('OTHER', _('Other'))])
target_id = models.IntegerField(_('target id'), null=True, 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"),
null=True, blank=True, default=None)
number = models.IntegerField(_("number"))
journal = models.ForeignKey(
GeneralJournal, related_name="operations", null=False, verbose_name=_("journal")
)
amount = CurrencyField(_("amount"))
date = models.DateField(_("date"))
remark = models.CharField(_("comment"), max_length=128, null=True, blank=True)
mode = models.CharField(
_("payment method"),
max_length=255,
choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD,
)
cheque_number = models.CharField(
_("cheque number"), max_length=32, default="", 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)
simpleaccounting_type = models.ForeignKey(
"SimplifiedAccountingType",
related_name="operations",
verbose_name=_("simple type"),
null=True,
blank=True,
)
accounting_type = models.ForeignKey(
"AccountingType",
related_name="operations",
verbose_name=_("accounting type"),
null=True,
blank=True,
)
label = models.ForeignKey(
"Label",
related_name="operations",
verbose_name=_("label"),
null=True,
blank=True,
on_delete=models.SET_NULL,
)
target_type = models.CharField(
_("target type"),
max_length=10,
choices=[
("USER", _("User")),
("CLUB", _("Club")),
("ACCOUNT", _("Account")),
("COMPANY", _("Company")),
("OTHER", _("Other")),
],
)
target_id = models.IntegerField(_("target id"), null=True, 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"),
null=True,
blank=True,
default=None,
)
class Meta:
unique_together = ('number', 'journal')
ordering = ['-number']
unique_together = ("number", "journal")
ordering = ["-number"]
def __getattribute__(self, attr):
if attr == "target":
@ -287,14 +345,29 @@ class Operation(models.Model):
if self.date is None:
raise ValidationError(_("The date must be set."))
elif self.date < self.journal.start_date:
raise ValidationError(_("""The date can not be before the start date of the journal, which is
%(start_date)s.""") % {'start_date': defaultfilters.date(self.journal.start_date, settings.DATE_FORMAT)})
raise ValidationError(
_(
"""The date can not be before the start date of the journal, which is
%(start_date)s."""
)
% {
"start_date": defaultfilters.date(
self.journal.start_date, settings.DATE_FORMAT
)
}
)
if self.target_type != "OTHER" and self.get_target() is None:
raise ValidationError(_("Target does not exists"))
if self.target_type == "OTHER" and self.target_label == "":
raise ValidationError(_("Please add a target label if you set no existing target"))
raise ValidationError(
_("Please add a target label if you set no existing target")
)
if not self.accounting_type and not self.simpleaccounting_type:
raise ValidationError(_("You need to provide ether a simplified accounting type or a standard accounting type"))
raise ValidationError(
_(
"You need to provide ether a simplified accounting type or a standard accounting type"
)
)
if self.simpleaccounting_type:
self.accounting_type = self.simpleaccounting_type.accounting_type
@ -329,7 +402,7 @@ class Operation(models.Model):
if self.journal.closed:
return False
m = self.journal.club_account.club.get_membership_for(user)
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID['Treasurer']:
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
@ -342,16 +415,19 @@ class Operation(models.Model):
if self.journal.closed:
return False
m = self.journal.club_account.club.get_membership_for(user)
if m is not None and m.role == settings.SITH_CLUB_ROLES_ID['Treasurer']:
if m is not None and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def get_absolute_url(self):
return reverse('accounting:journal_details', kwargs={'j_id': self.journal.id})
return reverse("accounting:journal_details", kwargs={"j_id": self.journal.id})
def __str__(self):
return "%d € | %s | %s | %s" % (
self.amount, self.date, self.accounting_type, self.done,
self.amount,
self.date,
self.accounting_type,
self.done,
)
@ -361,18 +437,30 @@ class AccountingType(models.Model):
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.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)
movement_type = models.CharField(_('movement type'), choices=[('CREDIT', _('Credit')), ('DEBIT', _('Debit')),
('NEUTRAL', _('Neutral'))], max_length=12)
label = models.CharField(_("label"), max_length=128)
movement_type = models.CharField(
_("movement type"),
choices=[
("CREDIT", _("Credit")),
("DEBIT", _("Debit")),
("NEUTRAL", _("Neutral")),
],
max_length=12,
)
class Meta:
verbose_name = _("accounting type")
ordering = ['movement_type', 'code']
ordering = ["movement_type", "code"]
def is_owned_by(self, user):
"""
@ -383,7 +471,7 @@ class AccountingType(models.Model):
return False
def get_absolute_url(self):
return reverse('accounting:type_list')
return reverse("accounting:type_list")
def __str__(self):
return self.code + " - " + self.get_movement_type_display() + " - " + self.label
@ -393,13 +481,17 @@ class SimplifiedAccountingType(models.Model):
"""
Class describing the simplified accounting types.
"""
label = models.CharField(_('label'), max_length=128)
accounting_type = models.ForeignKey(AccountingType, related_name="simplified_types",
verbose_name=_("simplified accounting types"))
label = models.CharField(_("label"), max_length=128)
accounting_type = models.ForeignKey(
AccountingType,
related_name="simplified_types",
verbose_name=_("simplified accounting types"),
)
class Meta:
verbose_name = _("simplified type")
ordering = ['accounting_type__movement_type', 'accounting_type__code']
ordering = ["accounting_type__movement_type", "accounting_type__code"]
@property
def movement_type(self):
@ -409,25 +501,36 @@ class SimplifiedAccountingType(models.Model):
return self.accounting_type.get_movement_type_display()
def get_absolute_url(self):
return reverse('accounting:simple_type_list')
return reverse("accounting:simple_type_list")
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):
"""Label allow a club to sort its operations"""
name = models.CharField(_('label'), max_length=64)
club_account = models.ForeignKey(ClubAccount, related_name="labels", verbose_name=_("club account"))
name = models.CharField(_("label"), max_length=64)
club_account = models.ForeignKey(
ClubAccount, related_name="labels", verbose_name=_("club account")
)
class Meta:
unique_together = ('name', 'club_account')
unique_together = ("name", "club_account")
def __str__(self):
return "%s (%s)" % (self.name, self.club_account.name)
def get_absolute_url(self):
return reverse('accounting:label_list', kwargs={'clubaccount_id': self.club_account.id})
return reverse(
"accounting:label_list", kwargs={"clubaccount_id": self.club_account.id}
)
def is_owned_by(self, user):
return self.club_account.is_owned_by(user)

View File

@ -28,7 +28,13 @@ from django.core.management import call_command
from datetime import date
from core.models import User
from accounting.models import GeneralJournal, Operation, Label, AccountingType, SimplifiedAccountingType
from accounting.models import (
GeneralJournal,
Operation,
Label,
AccountingType,
SimplifiedAccountingType,
)
class RefoundAccountTest(TestCase):
@ -40,18 +46,20 @@ class RefoundAccountTest(TestCase):
self.skia.customer.save()
def test_permission_denied(self):
self.client.login(username='guy', password='plop')
response_post = self.client.post(reverse("accounting:refound_account"),
{"user": self.skia.id})
self.client.login(username="guy", password="plop")
response_post = self.client.post(
reverse("accounting:refound_account"), {"user": self.skia.id}
)
response_get = self.client.get(reverse("accounting:refound_account"))
self.assertTrue(response_get.status_code == 403)
self.assertTrue(response_post.status_code == 403)
def test_root_granteed(self):
self.client.login(username='root', password='plop')
response_post = self.client.post(reverse("accounting:refound_account"),
{"user": self.skia.id})
self.skia = User.objects.filter(username='skia').first()
self.client.login(username="root", password="plop")
response_post = self.client.post(
reverse("accounting:refound_account"), {"user": self.skia.id}
)
self.skia = User.objects.filter(username="skia").first()
response_get = self.client.get(reverse("accounting:refound_account"))
self.assertFalse(response_get.status_code == 403)
self.assertTrue('<form action="" method="post">' in str(response_get.content))
@ -59,10 +67,11 @@ class RefoundAccountTest(TestCase):
self.assertTrue(self.skia.customer.amount == 0)
def test_comptable_granteed(self):
self.client.login(username='comptable', password='plop')
response_post = self.client.post(reverse("accounting:refound_account"),
{"user": self.skia.id})
self.skia = User.objects.filter(username='skia').first()
self.client.login(username="comptable", password="plop")
response_post = self.client.post(
reverse("accounting:refound_account"), {"user": self.skia.id}
)
self.skia = User.objects.filter(username="skia").first()
response_get = self.client.get(reverse("accounting:refound_account"))
self.assertFalse(response_get.status_code == 403)
self.assertTrue('<form action="" method="post">' in str(response_get.content))
@ -76,146 +85,217 @@ class JournalTest(TestCase):
self.journal = GeneralJournal.objects.filter(id=1).first()
def test_permission_granted(self):
self.client.login(username='comptable', password='plop')
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_details", args=[self.journal.id])
)
self.assertTrue(response_get.status_code == 200)
self.assertTrue('<td>M\\xc3\\xa9thode de paiement</td>' in str(response_get.content))
self.assertTrue(
"<td>M\\xc3\\xa9thode de paiement</td>" in str(response_get.content)
)
def test_permission_not_granted(self):
self.client.login(username='skia', password='plop')
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
self.client.login(username="skia", password="plop")
response_get = self.client.get(
reverse("accounting:journal_details", args=[self.journal.id])
)
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):
def setUp(self):
call_command("populate")
self.journal = GeneralJournal.objects.filter(id=1).first()
self.skia = User.objects.filter(username='skia').first()
at = AccountingType(code='443', label="Ce code n'existe pas", movement_type='CREDIT')
self.skia = User.objects.filter(username="skia").first()
at = AccountingType(
code="443", label="Ce code n'existe pas", movement_type="CREDIT"
)
at.save()
l = Label(club_account=self.journal.club_account, name='bob')
l = Label(club_account=self.journal.club_account, name="bob")
l.save()
self.client.login(username='comptable', password='plop')
self.op1 = Operation(journal=self.journal, date=date.today(), amount=1,
remark="Test bilan", mode='CASH', done=True, label=l,
accounting_type=at, target_type='USER', target_id=self.skia.id)
self.client.login(username="comptable", password="plop")
self.op1 = Operation(
journal=self.journal,
date=date.today(),
amount=1,
remark="Test bilan",
mode="CASH",
done=True,
label=l,
accounting_type=at,
target_type="USER",
target_id=self.skia.id,
)
self.op1.save()
self.op2 = Operation(journal=self.journal, date=date.today(), amount=2,
remark="Test bilan", mode='CASH', done=True, label=l,
accounting_type=at, target_type='USER', target_id=self.skia.id)
self.op2 = Operation(
journal=self.journal,
date=date.today(),
amount=2,
remark="Test bilan",
mode="CASH",
done=True,
label=l,
accounting_type=at,
target_type="USER",
target_id=self.skia.id,
)
self.op2.save()
def test_new_operation(self):
self.client.login(username='comptable', password='plop')
at = AccountingType.objects.filter(code='604').first()
response = self.client.post(reverse('accounting:op_new',
args=[self.journal.id]),
{'amount': 30,
'remark': "Un gros test",
'journal': self.journal.id,
'target_type': 'OTHER',
'target_id': '',
'target_label': "Le fantome de la nuit",
'date': '04/12/2020',
'mode': 'CASH',
'cheque_number': '',
'invoice': '',
'simpleaccounting_type': '',
'accounting_type': at.id,
'label': '',
'done': False,
})
self.client.login(username="comptable", password="plop")
at = AccountingType.objects.filter(code="604").first()
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 30,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome de la nuit",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": "",
"accounting_type": at.id,
"label": "",
"done": False,
},
)
self.assertFalse(response.status_code == 403)
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]))
self.assertTrue('<td>Le fantome de la nuit</td>' in str(response_get.content))
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])
)
self.assertTrue("<td>Le fantome de la nuit</td>" in str(response_get.content))
def test_bad_new_operation(self):
self.client.login(username='comptable', password='plop')
AccountingType.objects.filter(code='604').first()
response = self.client.post(reverse('accounting:op_new',
args=[self.journal.id]),
{'amount': 30,
'remark': "Un gros test",
'journal': self.journal.id,
'target_type': 'OTHER',
'target_id': '',
'target_label': "Le fantome de la nuit",
'date': '04/12/2020',
'mode': 'CASH',
'cheque_number': '',
'invoice': '',
'simpleaccounting_type': '',
'accounting_type': '',
'label': '',
'done': False,
})
self.assertTrue('Vous devez fournir soit un type comptable simplifi\\xc3\\xa9 ou un type comptable standard' in str(response.content))
self.client.login(username="comptable", password="plop")
AccountingType.objects.filter(code="604").first()
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 30,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome de la nuit",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": "",
"accounting_type": "",
"label": "",
"done": False,
},
)
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):
self.client.login(username='skia', password='plop')
at = AccountingType.objects.filter(code='604').first()
response = self.client.post(reverse('accounting:op_new',
args=[self.journal.id]),
{'amount': 30,
'remark': "Un gros test",
'journal': self.journal.id,
'target_type': 'OTHER',
'target_id': '',
'target_label': "Le fantome du jour",
'date': '04/12/2020',
'mode': 'CASH',
'cheque_number': '',
'invoice': '',
'simpleaccounting_type': '',
'accounting_type': at.id,
'label': '',
'done': False,
})
self.client.login(username="skia", password="plop")
at = AccountingType.objects.filter(code="604").first()
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 30,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome du jour",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": "",
"accounting_type": at.id,
"label": "",
"done": False,
},
)
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):
self.client.login(username='comptable', password='plop')
self.client.login(username="comptable", password="plop")
sat = SimplifiedAccountingType.objects.all().first()
response = self.client.post(reverse('accounting:op_new',
args=[self.journal.id]),
{'amount': 23,
'remark': "Un gros test",
'journal': self.journal.id,
'target_type': 'OTHER',
'target_id': '',
'target_label': "Le fantome de l'aurore",
'date': '04/12/2020',
'mode': 'CASH',
'cheque_number': '',
'invoice': '',
'simpleaccounting_type': sat.id,
'accounting_type': '',
'label': '',
'done': False,
})
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 23,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome de l'aurore",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": sat.id,
"accounting_type": "",
"label": "",
"done": False,
},
)
self.assertFalse(response.status_code == 403)
self.assertTrue(self.journal.operations.filter(amount=23).exists())
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
self.assertTrue("<td>Le fantome de l&#39;aurore</td>" in str(response_get.content))
self.assertTrue(self.journal.operations.filter(amount=23).values('accounting_type').first()['accounting_type'] == AccountingType.objects.filter(code=6).values('id').first()['id'])
response_get = self.client.get(
reverse("accounting:journal_details", args=[self.journal.id])
)
self.assertTrue(
"<td>Le fantome de l&#39;aurore</td>" in str(response_get.content)
)
self.assertTrue(
self.journal.operations.filter(amount=23)
.values("accounting_type")
.first()["accounting_type"]
== AccountingType.objects.filter(code=6).values("id").first()["id"]
)
def test_nature_statement(self):
self.client.login(username='comptable', password='plop')
response_get = self.client.get(reverse("accounting:journal_nature_statement", args=[self.journal.id]))
self.assertTrue("bob (Troll Pench\\xc3\\xa9) : 3.00" in str(response_get.content))
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_nature_statement", args=[self.journal.id])
)
self.assertTrue(
"bob (Troll Pench\\xc3\\xa9) : 3.00" in str(response_get.content)
)
def test_person_statement(self):
self.client.login(username='comptable', password='plop')
response_get = self.client.get(reverse("accounting:journal_person_statement", args=[self.journal.id]))
self.assertTrue("<td>3.00</td>" in str(response_get.content) and '<td><a href="/user/1/">S&#39; Kia</a></td>' in str(response_get.content))
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_person_statement", args=[self.journal.id])
)
self.assertTrue(
"<td>3.00</td>" in str(response_get.content)
and '<td><a href="/user/1/">S&#39; Kia</a></td>'
in str(response_get.content)
)
def test_accounting_statement(self):
self.client.login(username='comptable', password='plop')
response_get = self.client.get(reverse("accounting:journal_accounting_statement", args=[self.journal.id]))
self.assertTrue("<td>443 - Cr\\xc3\\xa9dit - Ce code n&#39;existe pas</td>" in str(response_get.content))
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_accounting_statement", args=[self.journal.id])
)
self.assertTrue(
"<td>443 - Cr\\xc3\\xa9dit - Ce code n&#39;existe pas</td>"
in str(response_get.content)
)

View File

@ -28,46 +28,125 @@ from accounting.views import *
urlpatterns = [
# Accounting types
url(r'^simple_type$', SimplifiedAccountingTypeListView.as_view(), name='simple_type_list'),
url(r'^simple_type/create$', SimplifiedAccountingTypeCreateView.as_view(), name='simple_type_new'),
url(r'^simple_type/(?P<type_id>[0-9]+)/edit$', SimplifiedAccountingTypeEditView.as_view(), name='simple_type_edit'),
url(
r"^simple_type$",
SimplifiedAccountingTypeListView.as_view(),
name="simple_type_list",
),
url(
r"^simple_type/create$",
SimplifiedAccountingTypeCreateView.as_view(),
name="simple_type_new",
),
url(
r"^simple_type/(?P<type_id>[0-9]+)/edit$",
SimplifiedAccountingTypeEditView.as_view(),
name="simple_type_edit",
),
# Accounting types
url(r'^type$', AccountingTypeListView.as_view(), name='type_list'),
url(r'^type/create$', AccountingTypeCreateView.as_view(), name='type_new'),
url(r'^type/(?P<type_id>[0-9]+)/edit$', AccountingTypeEditView.as_view(), name='type_edit'),
url(r"^type$", AccountingTypeListView.as_view(), name="type_list"),
url(r"^type/create$", AccountingTypeCreateView.as_view(), name="type_new"),
url(
r"^type/(?P<type_id>[0-9]+)/edit$",
AccountingTypeEditView.as_view(),
name="type_edit",
),
# Bank accounts
url(r'^$', BankAccountListView.as_view(), name='bank_list'),
url(r'^bank/create$', BankAccountCreateView.as_view(), name='bank_new'),
url(r'^bank/(?P<b_account_id>[0-9]+)$', BankAccountDetailView.as_view(), name='bank_details'),
url(r'^bank/(?P<b_account_id>[0-9]+)/edit$', BankAccountEditView.as_view(), name='bank_edit'),
url(r'^bank/(?P<b_account_id>[0-9]+)/delete$', BankAccountDeleteView.as_view(), name='bank_delete'),
url(r"^$", BankAccountListView.as_view(), name="bank_list"),
url(r"^bank/create$", BankAccountCreateView.as_view(), name="bank_new"),
url(
r"^bank/(?P<b_account_id>[0-9]+)$",
BankAccountDetailView.as_view(),
name="bank_details",
),
url(
r"^bank/(?P<b_account_id>[0-9]+)/edit$",
BankAccountEditView.as_view(),
name="bank_edit",
),
url(
r"^bank/(?P<b_account_id>[0-9]+)/delete$",
BankAccountDeleteView.as_view(),
name="bank_delete",
),
# Club accounts
url(r'^club/create$', ClubAccountCreateView.as_view(), name='club_new'),
url(r'^club/(?P<c_account_id>[0-9]+)$', ClubAccountDetailView.as_view(), name='club_details'),
url(r'^club/(?P<c_account_id>[0-9]+)/edit$', ClubAccountEditView.as_view(), name='club_edit'),
url(r'^club/(?P<c_account_id>[0-9]+)/delete$', ClubAccountDeleteView.as_view(), name='club_delete'),
url(r"^club/create$", ClubAccountCreateView.as_view(), name="club_new"),
url(
r"^club/(?P<c_account_id>[0-9]+)$",
ClubAccountDetailView.as_view(),
name="club_details",
),
url(
r"^club/(?P<c_account_id>[0-9]+)/edit$",
ClubAccountEditView.as_view(),
name="club_edit",
),
url(
r"^club/(?P<c_account_id>[0-9]+)/delete$",
ClubAccountDeleteView.as_view(),
name="club_delete",
),
# Journals
url(r'^journal/create$', JournalCreateView.as_view(), name='journal_new'),
url(r'^journal/(?P<j_id>[0-9]+)$', JournalDetailView.as_view(), name='journal_details'),
url(r'^journal/(?P<j_id>[0-9]+)/edit$', JournalEditView.as_view(), name='journal_edit'),
url(r'^journal/(?P<j_id>[0-9]+)/delete$', JournalDeleteView.as_view(), name='journal_delete'),
url(r'^journal/(?P<j_id>[0-9]+)/statement/nature$', JournalNatureStatementView.as_view(), name='journal_nature_statement'),
url(r'^journal/(?P<j_id>[0-9]+)/statement/person$', JournalPersonStatementView.as_view(), name='journal_person_statement'),
url(r'^journal/(?P<j_id>[0-9]+)/statement/accounting$', JournalAccountingStatementView.as_view(), name='journal_accounting_statement'),
url(r"^journal/create$", JournalCreateView.as_view(), name="journal_new"),
url(
r"^journal/(?P<j_id>[0-9]+)$",
JournalDetailView.as_view(),
name="journal_details",
),
url(
r"^journal/(?P<j_id>[0-9]+)/edit$",
JournalEditView.as_view(),
name="journal_edit",
),
url(
r"^journal/(?P<j_id>[0-9]+)/delete$",
JournalDeleteView.as_view(),
name="journal_delete",
),
url(
r"^journal/(?P<j_id>[0-9]+)/statement/nature$",
JournalNatureStatementView.as_view(),
name="journal_nature_statement",
),
url(
r"^journal/(?P<j_id>[0-9]+)/statement/person$",
JournalPersonStatementView.as_view(),
name="journal_person_statement",
),
url(
r"^journal/(?P<j_id>[0-9]+)/statement/accounting$",
JournalAccountingStatementView.as_view(),
name="journal_accounting_statement",
),
# Operations
url(r'^operation/create/(?P<j_id>[0-9]+)$', OperationCreateView.as_view(), name='op_new'),
url(r'^operation/(?P<op_id>[0-9]+)$', OperationEditView.as_view(), name='op_edit'),
url(r'^operation/(?P<op_id>[0-9]+)/pdf$', OperationPDFView.as_view(), name='op_pdf'),
url(
r"^operation/create/(?P<j_id>[0-9]+)$",
OperationCreateView.as_view(),
name="op_new",
),
url(r"^operation/(?P<op_id>[0-9]+)$", OperationEditView.as_view(), name="op_edit"),
url(
r"^operation/(?P<op_id>[0-9]+)/pdf$", OperationPDFView.as_view(), name="op_pdf"
),
# Companies
url(r'^company/list$', CompanyListView.as_view(), name='co_list'),
url(r'^company/create$', CompanyCreateView.as_view(), name='co_new'),
url(r'^company/(?P<co_id>[0-9]+)$', CompanyEditView.as_view(), name='co_edit'),
url(r"^company/list$", CompanyListView.as_view(), name="co_list"),
url(r"^company/create$", CompanyCreateView.as_view(), name="co_new"),
url(r"^company/(?P<co_id>[0-9]+)$", CompanyEditView.as_view(), name="co_edit"),
# Labels
url(r'^label/new$', LabelCreateView.as_view(), name='label_new'),
url(r'^label/(?P<clubaccount_id>[0-9]+)$', LabelListView.as_view(), name='label_list'),
url(r'^label/(?P<label_id>[0-9]+)/edit$', LabelEditView.as_view(), name='label_edit'),
url(r'^label/(?P<label_id>[0-9]+)/delete$', LabelDeleteView.as_view(), name='label_delete'),
url(r"^label/new$", LabelCreateView.as_view(), name="label_new"),
url(
r"^label/(?P<clubaccount_id>[0-9]+)$",
LabelListView.as_view(),
name="label_list",
),
url(
r"^label/(?P<label_id>[0-9]+)/edit$", LabelEditView.as_view(), name="label_edit"
),
url(
r"^label/(?P<label_id>[0-9]+)/delete$",
LabelDeleteView.as_view(),
name="label_delete",
),
# User account
url(r'^refound/account$', RefoundAccountView.as_view(), name='refound_account'),
url(r"^refound/account$", RefoundAccountView.as_view(), name="refound_account"),
]

View File

@ -38,9 +38,24 @@ import collections
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 accounting.models import BankAccount, ClubAccount, GeneralJournal, Operation, AccountingType, Company, SimplifiedAccountingType, Label
from accounting.models import (
BankAccount,
ClubAccount,
GeneralJournal,
Operation,
AccountingType,
Company,
SimplifiedAccountingType,
Label,
)
from counter.models import Counter, Selling, Product
# Main accounting view
@ -50,185 +65,228 @@ class BankAccountListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = BankAccount
template_name = 'accounting/bank_account_list.jinja'
ordering = ['name']
template_name = "accounting/bank_account_list.jinja"
ordering = ["name"]
# Simplified accounting types
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = SimplifiedAccountingType
template_name = 'accounting/simplifiedaccountingtype_list.jinja'
template_name = "accounting/simplifiedaccountingtype_list.jinja"
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = SimplifiedAccountingType
pk_url_kwarg = "type_id"
fields = ['label', 'accounting_type']
template_name = 'core/edit.jinja'
fields = ["label", "accounting_type"]
template_name = "core/edit.jinja"
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
"""
Create an accounting type (for the admins)
"""
model = SimplifiedAccountingType
fields = ['label', 'accounting_type']
template_name = 'core/create.jinja'
fields = ["label", "accounting_type"]
template_name = "core/create.jinja"
# Accounting types
class AccountingTypeListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = AccountingType
template_name = 'accounting/accountingtype_list.jinja'
template_name = "accounting/accountingtype_list.jinja"
class AccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = AccountingType
pk_url_kwarg = "type_id"
fields = ['code', 'label', 'movement_type']
template_name = 'core/edit.jinja'
fields = ["code", "label", "movement_type"]
template_name = "core/edit.jinja"
class AccountingTypeCreateView(CanCreateMixin, CreateView):
"""
Create an accounting type (for the admins)
"""
model = AccountingType
fields = ['code', 'label', 'movement_type']
template_name = 'core/create.jinja'
fields = ["code", "label", "movement_type"]
template_name = "core/create.jinja"
# BankAccount views
class BankAccountEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
fields = ['name', 'iban', 'number', 'club']
template_name = 'core/edit.jinja'
fields = ["name", "iban", "number", "club"]
template_name = "core/edit.jinja"
class BankAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every club account
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
template_name = 'accounting/bank_account_details.jinja'
template_name = "accounting/bank_account_details.jinja"
class BankAccountCreateView(CanCreateMixin, CreateView):
"""
Create a bank account (for the admins)
"""
model = BankAccount
fields = ['name', 'club', 'iban', 'number']
template_name = 'core/create.jinja'
fields = ["name", "club", "iban", "number"]
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)
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:bank_list')
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("accounting:bank_list")
# ClubAccount views
class ClubAccountEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
fields = ['name', 'club', 'bank_account']
template_name = 'core/edit.jinja'
fields = ["name", "club", "bank_account"]
template_name = "core/edit.jinja"
class ClubAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every journal
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
template_name = 'accounting/club_account_details.jinja'
template_name = "accounting/club_account_details.jinja"
class ClubAccountCreateView(CanCreateMixin, CreateView):
"""
Create a club account (for the admins)
"""
model = ClubAccount
fields = ['name', 'club', 'bank_account']
template_name = 'core/create.jinja'
fields = ["name", "club", "bank_account"]
template_name = "core/create.jinja"
def get_initial(self):
ret = super(ClubAccountCreateView, self).get_initial()
if 'parent' in self.request.GET.keys():
obj = BankAccount.objects.filter(id=int(self.request.GET['parent'])).first()
if "parent" in self.request.GET.keys():
obj = BankAccount.objects.filter(id=int(self.request.GET["parent"])).first()
if obj is not None:
ret['bank_account'] = obj.id
ret["bank_account"] = obj.id
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)
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:bank_list')
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("accounting:bank_list")
# Journal views
class JournalTabsMixin(TabedViewMixin):
def get_tabs_title(self):
return _("Journal")
def get_list_of_tabs(self):
tab_list = []
tab_list.append({
'url': reverse('accounting:journal_details', kwargs={'j_id': self.object.id}),
'slug': 'journal',
'name': _("Journal"),
})
tab_list.append({
'url': reverse('accounting:journal_nature_statement', kwargs={'j_id': self.object.id}),
'slug': 'nature_statement',
'name': _("Statement by nature"),
})
tab_list.append({
'url': reverse('accounting:journal_person_statement', kwargs={'j_id': self.object.id}),
'slug': 'person_statement',
'name': _("Statement by person"),
})
tab_list.append({
'url': reverse('accounting:journal_accounting_statement', kwargs={'j_id': self.object.id}),
'slug': 'accounting_statement',
'name': _("Accounting statement"),
})
tab_list.append(
{
"url": reverse(
"accounting:journal_details", kwargs={"j_id": self.object.id}
),
"slug": "journal",
"name": _("Journal"),
}
)
tab_list.append(
{
"url": reverse(
"accounting:journal_nature_statement",
kwargs={"j_id": self.object.id},
),
"slug": "nature_statement",
"name": _("Statement by nature"),
}
)
tab_list.append(
{
"url": reverse(
"accounting:journal_person_statement",
kwargs={"j_id": self.object.id},
),
"slug": "person_statement",
"name": _("Statement by person"),
}
)
tab_list.append(
{
"url": reverse(
"accounting:journal_accounting_statement",
kwargs={"j_id": self.object.id},
),
"slug": "accounting_statement",
"name": _("Accounting statement"),
}
)
return tab_list
@ -236,17 +294,21 @@ class JournalCreateView(CanCreateMixin, CreateView):
"""
Create a general journal
"""
model = GeneralJournal
form_class = modelform_factory(GeneralJournal, fields=['name', 'start_date', 'club_account'],
widgets={'start_date': SelectDate, })
template_name = 'core/create.jinja'
form_class = modelform_factory(
GeneralJournal,
fields=["name", "start_date", "club_account"],
widgets={"start_date": SelectDate},
)
template_name = "core/create.jinja"
def get_initial(self):
ret = super(JournalCreateView, self).get_initial()
if 'parent' in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET['parent'])).first()
if "parent" in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first()
if obj is not None:
ret['club_account'] = obj.id
ret["club_account"] = obj.id
return ret
@ -254,30 +316,33 @@ class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
"""
A detail view, listing every operation
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = 'accounting/journal_details.jinja'
current_tab = 'journal'
template_name = "accounting/journal_details.jinja"
current_tab = "journal"
class JournalEditView(CanEditMixin, UpdateView):
"""
Update a general journal
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
template_name = 'core/edit.jinja'
fields = ["name", "start_date", "end_date", "club_account", "closed"]
template_name = "core/edit.jinja"
class JournalDeleteView(CanEditPropMixin, DeleteView):
"""
Delete a club account (for the admins)
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:club_details')
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("accounting:club_details")
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
@ -289,64 +354,105 @@ class JournalDeleteView(CanEditPropMixin, DeleteView):
# Operation views
class OperationForm(forms.ModelForm):
class Meta:
model = Operation
fields = ['amount', 'remark', 'journal', 'target_type', 'target_id', 'target_label', 'date', 'mode',
'cheque_number', 'invoice', 'simpleaccounting_type', 'accounting_type', 'label', 'done']
fields = [
"amount",
"remark",
"journal",
"target_type",
"target_id",
"target_label",
"date",
"mode",
"cheque_number",
"invoice",
"simpleaccounting_type",
"accounting_type",
"label",
"done",
]
widgets = {
'journal': HiddenInput,
'target_id': HiddenInput,
'date': SelectDate,
'invoice': SelectFile,
"journal": HiddenInput,
"target_id": HiddenInput,
"date": SelectDate,
"invoice": SelectFile,
}
user = AutoCompleteSelectField('users', help_text=None, required=False)
club_account = AutoCompleteSelectField('club_accounts', help_text=None, required=False)
club = AutoCompleteSelectField('clubs', help_text=None, required=False)
company = AutoCompleteSelectField('companies', help_text=None, required=False)
need_link = forms.BooleanField(label=_("Link this operation to the target account"), required=False, initial=False)
user = AutoCompleteSelectField("users", help_text=None, required=False)
club_account = AutoCompleteSelectField(
"club_accounts", help_text=None, required=False
)
club = AutoCompleteSelectField("clubs", help_text=None, required=False)
company = AutoCompleteSelectField("companies", help_text=None, required=False)
need_link = forms.BooleanField(
label=_("Link this operation to the target account"),
required=False,
initial=False,
)
def __init__(self, *args, **kwargs):
club_account = kwargs.pop('club_account', None)
club_account = kwargs.pop("club_account", None)
super(OperationForm, self).__init__(*args, **kwargs)
if club_account:
self.fields['label'].queryset = club_account.labels.order_by('name').all()
self.fields["label"].queryset = club_account.labels.order_by("name").all()
if self.instance.target_type == "USER":
self.fields['user'].initial = self.instance.target_id
self.fields["user"].initial = self.instance.target_id
elif self.instance.target_type == "ACCOUNT":
self.fields['club_account'].initial = self.instance.target_id
self.fields["club_account"].initial = self.instance.target_id
elif self.instance.target_type == "CLUB":
self.fields['club'].initial = self.instance.target_id
self.fields["club"].initial = self.instance.target_id
elif self.instance.target_type == "COMPANY":
self.fields['company'].initial = self.instance.target_id
self.fields["company"].initial = self.instance.target_id
def clean(self):
self.cleaned_data = super(OperationForm, self).clean()
if 'target_type' in self.cleaned_data.keys():
if self.cleaned_data.get("user") is None and self.cleaned_data.get("club") is None and self.cleaned_data.get("club_account") is None and self.cleaned_data.get("company") is None and self.cleaned_data.get("target_label") is None:
self.add_error('target_type', ValidationError(_("The target must be set.")))
if "target_type" in self.cleaned_data.keys():
if (
self.cleaned_data.get("user") is None
and self.cleaned_data.get("club") is None
and self.cleaned_data.get("club_account") is None
and self.cleaned_data.get("company") is None
and self.cleaned_data.get("target_label") is None
):
self.add_error(
"target_type", ValidationError(_("The target must be set."))
)
else:
if self.cleaned_data['target_type'] == "USER":
self.cleaned_data['target_id'] = self.cleaned_data['user'].id
elif self.cleaned_data['target_type'] == "ACCOUNT":
self.cleaned_data['target_id'] = self.cleaned_data['club_account'].id
elif self.cleaned_data['target_type'] == "CLUB":
self.cleaned_data['target_id'] = self.cleaned_data['club'].id
elif self.cleaned_data['target_type'] == "COMPANY":
self.cleaned_data['target_id'] = self.cleaned_data['company'].id
if self.cleaned_data["target_type"] == "USER":
self.cleaned_data["target_id"] = self.cleaned_data["user"].id
elif self.cleaned_data["target_type"] == "ACCOUNT":
self.cleaned_data["target_id"] = self.cleaned_data[
"club_account"
].id
elif self.cleaned_data["target_type"] == "CLUB":
self.cleaned_data["target_id"] = self.cleaned_data["club"].id
elif self.cleaned_data["target_type"] == "COMPANY":
self.cleaned_data["target_id"] = self.cleaned_data["company"].id
if self.cleaned_data.get("amount") is None:
self.add_error('amount', ValidationError(_("The amount must be set.")))
self.add_error("amount", ValidationError(_("The amount must be set.")))
return self.cleaned_data
def save(self):
ret = super(OperationForm, self).save()
if self.instance.target_type == "ACCOUNT" and not self.instance.linked_operation and self.instance.target.has_open_journal() and self.cleaned_data['need_link']:
if (
self.instance.target_type == "ACCOUNT"
and not self.instance.linked_operation
and self.instance.target.has_open_journal()
and self.cleaned_data["need_link"]
):
inst = self.instance
club_account = inst.target
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
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
op = Operation(
journal=club_account.get_open_journal(),
amount=inst.amount,
@ -373,26 +479,27 @@ class OperationCreateView(CanCreateMixin, CreateView):
"""
Create an operation
"""
model = Operation
form_class = OperationForm
template_name = 'accounting/operation_edit.jinja'
template_name = "accounting/operation_edit.jinja"
def get_form(self, form_class=None):
self.journal = GeneralJournal.objects.filter(id=self.kwargs['j_id']).first()
self.journal = GeneralJournal.objects.filter(id=self.kwargs["j_id"]).first()
ca = self.journal.club_account if self.journal else None
return self.form_class(club_account=ca, **self.get_form_kwargs())
def get_initial(self):
ret = super(OperationCreateView, self).get_initial()
if self.journal is not None:
ret['journal'] = self.journal.id
ret["journal"] = self.journal.id
return ret
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(OperationCreateView, self).get_context_data(**kwargs)
if self.journal:
kwargs['object'] = self.journal
kwargs["object"] = self.journal
return kwargs
@ -400,15 +507,16 @@ class OperationEditView(CanEditMixin, UpdateView):
"""
An edit view, working as detail for the moment
"""
model = Operation
pk_url_kwarg = "op_id"
form_class = OperationForm
template_name = 'accounting/operation_edit.jinja'
template_name = "accounting/operation_edit.jinja"
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(OperationEditView, self).get_context_data(**kwargs)
kwargs['object'] = self.object.journal
kwargs["object"] = self.object.journal
return kwargs
@ -430,7 +538,7 @@ class OperationPDFView(CanViewMixin, DetailView):
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
pdfmetrics.registerFont(TTFont('DejaVu', 'DejaVuSerif.ttf'))
pdfmetrics.registerFont(TTFont("DejaVu", "DejaVuSerif.ttf"))
self.object = self.get_object()
amount = self.object.amount
@ -450,11 +558,15 @@ class OperationPDFView(CanViewMixin, DetailView):
else:
target = self.object.target.get_display_name()
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename="op-%d(%s_on_%s).pdf"' % (num, ti, club_name)
response = HttpResponse(content_type="application/pdf")
response["Content-Disposition"] = 'filename="op-%d(%s_on_%s).pdf"' % (
num,
ti,
club_name,
)
p = canvas.Canvas(response)
p.setFont('DejaVu', 12)
p.setFont("DejaVu", 12)
p.setTitle("%s %d" % (_("Operation"), num))
width, height = letter
@ -466,20 +578,29 @@ class OperationPDFView(CanViewMixin, DetailView):
label = Table(labelStr, colWidths=[150], rowHeights=[20])
label.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'RIGHT'),
]))
label.setStyle(TableStyle([("ALIGN", (0, 0), (-1, -1), "RIGHT")]))
w, h = label.wrapOn(label, 0, 0)
label.drawOn(p, width - 180, height)
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 - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label is not None else ""})
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 - 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})
data = []
data += [["%s" % (_("Credit").upper() if nature == 'CREDIT' else _("Debit").upper())]]
data += [
["%s" % (_("Credit").upper() if nature == "CREDIT" else _("Debit").upper())]
]
data += [[_("Amount: %(amount).2f") % {"amount": amount}]]
@ -493,33 +614,50 @@ class OperationPDFView(CanViewMixin, DetailView):
data += [[payment_mode]]
data += [["%s : %s" % (_("Debtor") if nature == 'CREDIT' else _("Creditor"), target), ""]]
data += [
[
"%s : %s"
% (_("Debtor") if nature == "CREDIT" else _("Creditor"), target),
"",
]
]
data += [["%s \n%s" % (_("Comment:"), remark)]]
t = Table(data, colWidths=[(width - 90 * 2) / 2] * 2, rowHeights=[20, 20, 70, 20, 80])
t.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('VALIGN', (-2, -1), (-1, -1), 'TOP'),
('VALIGN', (0, 0), (-1, -2), 'MIDDLE'),
('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
('SPAN', (0, 0), (1, 0)), # line DEBIT/CREDIT
('SPAN', (0, 1), (1, 1)), # line amount
('SPAN', (-2, -1), (-1, -1)), # line comment
('SPAN', (0, -2), (-1, -2)), # line creditor/debtor
('SPAN', (0, 2), (1, 2)), # line payment_mode
('ALIGN', (0, 2), (1, 2), 'LEFT'), # line payment_mode
('ALIGN', (-2, -1), (-1, -1), 'LEFT'),
('BOX', (0, 0), (-1, -1), 0.25, colors.black),
]))
t = Table(
data, colWidths=[(width - 90 * 2) / 2] * 2, rowHeights=[20, 20, 70, 20, 80]
)
t.setStyle(
TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("VALIGN", (-2, -1), (-1, -1), "TOP"),
("VALIGN", (0, 0), (-1, -2), "MIDDLE"),
("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),
("SPAN", (0, 0), (1, 0)), # line DEBIT/CREDIT
("SPAN", (0, 1), (1, 1)), # line amount
("SPAN", (-2, -1), (-1, -1)), # line comment
("SPAN", (0, -2), (-1, -2)), # line creditor/debtor
("SPAN", (0, 2), (1, 2)), # line payment_mode
("ALIGN", (0, 2), (1, 2), "LEFT"), # line payment_mode
("ALIGN", (-2, -1), (-1, -1), "LEFT"),
("BOX", (0, 0), (-1, -1), 0.25, colors.black),
]
)
)
signature = []
signature += [[_("Signature:")]]
tSig = Table(signature, colWidths=[(width - 90 * 2)], rowHeights=[80])
tSig.setStyle(TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('BOX', (0, 0), (-1, -1), 0.25, colors.black)]))
tSig.setStyle(
TableStyle(
[
("VALIGN", (0, 0), (-1, -1), "TOP"),
("BOX", (0, 0), (-1, -1), 0.25, colors.black),
]
)
)
w, h = tSig.wrapOn(p, 0, 0)
tSig.drawOn(p, 90, 200)
@ -540,18 +678,22 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Display a statement sorted by labels
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = 'accounting/journal_statement_nature.jinja'
current_tab = 'nature_statement'
template_name = "accounting/journal_statement_nature.jinja"
current_tab = "nature_statement"
def statement(self, queryset, movement_type):
ret = collections.OrderedDict()
statement = collections.OrderedDict()
total_sum = 0
for sat in [None] + list(SimplifiedAccountingType.objects.order_by('label').all()):
sum = queryset.filter(accounting_type__movement_type=movement_type,
simpleaccounting_type=sat).aggregate(amount_sum=Sum('amount'))['amount_sum']
for sat in [None] + list(
SimplifiedAccountingType.objects.order_by("label").all()
):
sum = queryset.filter(
accounting_type__movement_type=movement_type, simpleaccounting_type=sat
).aggregate(amount_sum=Sum("amount"))["amount_sum"]
if sat:
sat = sat.label
else:
@ -564,7 +706,9 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
return ret
def big_statement(self):
label_list = self.object.operations.order_by('label').values_list('label').distinct()
label_list = (
self.object.operations.order_by("label").values_list("label").distinct()
)
labels = Label.objects.filter(id__in=label_list).all()
statement = collections.OrderedDict()
gen_statement = collections.OrderedDict()
@ -572,20 +716,28 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
gen_statement.update(self.statement(self.object.operations.all(), "CREDIT"))
gen_statement.update(self.statement(self.object.operations.all(), "DEBIT"))
statement[_("General statement")] = gen_statement
no_label_statement.update(self.statement(self.object.operations.filter(label=None).all(), "CREDIT"))
no_label_statement.update(self.statement(self.object.operations.filter(label=None).all(), "DEBIT"))
no_label_statement.update(
self.statement(self.object.operations.filter(label=None).all(), "CREDIT")
)
no_label_statement.update(
self.statement(self.object.operations.filter(label=None).all(), "DEBIT")
)
statement[_("No label operations")] = no_label_statement
for l in labels:
l_stmt = collections.OrderedDict()
l_stmt.update(self.statement(self.object.operations.filter(label=l).all(), "CREDIT"))
l_stmt.update(self.statement(self.object.operations.filter(label=l).all(), "DEBIT"))
l_stmt.update(
self.statement(self.object.operations.filter(label=l).all(), "CREDIT")
)
l_stmt.update(
self.statement(self.object.operations.filter(label=l).all(), "DEBIT")
)
statement[l] = l_stmt
return statement
def get_context_data(self, **kwargs):
""" Add infos to the context """
kwargs = super(JournalNatureStatementView, self).get_context_data(**kwargs)
kwargs['statement'] = self.big_statement()
kwargs["statement"] = self.big_statement()
return kwargs
@ -593,20 +745,29 @@ class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Calculate a dictionary with operation target and sum of operations
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = 'accounting/journal_statement_person.jinja'
current_tab = 'person_statement'
template_name = "accounting/journal_statement_person.jinja"
current_tab = "person_statement"
def sum_by_target(self, target_id, target_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']
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"]
def statement(self, movement_type):
statement = collections.OrderedDict()
for op in self.object.operations.filter(accounting_type__movement_type=movement_type).order_by('target_type',
'target_id').distinct():
statement[op.target] = self.sum_by_target(op.target_id, op.target_type, movement_type)
for op in (
self.object.operations.filter(accounting_type__movement_type=movement_type)
.order_by("target_type", "target_id")
.distinct()
):
statement[op.target] = self.sum_by_target(
op.target_id, op.target_type, movement_type
)
return statement
def total(self, movement_type):
@ -615,10 +776,10 @@ class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(JournalPersonStatementView, self).get_context_data(**kwargs)
kwargs['credit_statement'] = self.statement("CREDIT")
kwargs['debit_statement'] = self.statement("DEBIT")
kwargs['total_credit'] = self.total("CREDIT")
kwargs['total_debit'] = self.total("DEBIT")
kwargs["credit_statement"] = self.statement("CREDIT")
kwargs["debit_statement"] = self.statement("DEBIT")
kwargs["total_credit"] = self.total("CREDIT")
kwargs["total_debit"] = self.total("DEBIT")
return kwargs
@ -626,16 +787,18 @@ class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView)
"""
Calculate a dictionary with operation type and sum of operations
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = 'accounting/journal_statement_accounting.jinja'
template_name = "accounting/journal_statement_accounting.jinja"
current_tab = "accounting_statement"
def statement(self):
statement = collections.OrderedDict()
for at in AccountingType.objects.order_by('code').all():
for at in AccountingType.objects.order_by("code").all():
sum_by_type = self.object.operations.filter(
accounting_type__code__startswith=at.code).aggregate(amount_sum=Sum('amount'))['amount_sum']
accounting_type__code__startswith=at.code
).aggregate(amount_sum=Sum("amount"))["amount_sum"]
if sum_by_type:
statement[at] = sum_by_type
return statement
@ -643,86 +806,95 @@ class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView)
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(JournalAccountingStatementView, self).get_context_data(**kwargs)
kwargs['statement'] = self.statement()
kwargs["statement"] = self.statement()
return kwargs
# Company views
class CompanyListView(CanViewMixin, ListView):
model = Company
template_name = 'accounting/co_list.jinja'
template_name = "accounting/co_list.jinja"
class CompanyCreateView(CanCreateMixin, CreateView):
"""
Create a company
"""
model = Company
fields = ['name']
template_name = 'core/create.jinja'
success_url = reverse_lazy('accounting:co_list')
fields = ["name"]
template_name = "core/create.jinja"
success_url = reverse_lazy("accounting:co_list")
class CompanyEditView(CanCreateMixin, UpdateView):
"""
Edit a company
"""
model = Company
pk_url_kwarg = "co_id"
fields = ['name']
template_name = 'core/edit.jinja'
success_url = reverse_lazy('accounting:co_list')
fields = ["name"]
template_name = "core/edit.jinja"
success_url = reverse_lazy("accounting:co_list")
# Label views
class LabelListView(CanViewMixin, DetailView):
model = ClubAccount
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
form_class = modelform_factory(Label, fields=['name', 'club_account'], widgets={
'club_account': HiddenInput,
})
template_name = 'core/create.jinja'
form_class = modelform_factory(
Label, fields=["name", "club_account"], widgets={"club_account": HiddenInput}
)
template_name = "core/create.jinja"
def get_initial(self):
ret = super(LabelCreateView, self).get_initial()
if 'parent' in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET['parent'])).first()
if "parent" in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first()
if obj is not None:
ret['club_account'] = obj.id
ret["club_account"] = obj.id
return ret
class LabelEditView(CanEditMixin, UpdateView):
model = Label
pk_url_kwarg = "label_id"
fields = ['name']
template_name = 'core/edit.jinja'
fields = ["name"]
template_name = "core/edit.jinja"
class LabelDeleteView(CanEditMixin, DeleteView):
model = Label
pk_url_kwarg = "label_id"
template_name = 'core/delete_confirm.jinja'
template_name = "core/delete_confirm.jinja"
def get_success_url(self):
return self.object.get_absolute_url()
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):
"""
Create a selling with the same amount than the current user money
"""
template_name = "accounting/refound_account.jinja"
form_class = CloseCustomerAccountForm
@ -743,21 +915,28 @@ class RefoundAccountView(FormView):
return super(RefoundAccountView, self).post(self, request, *arg, **kwargs)
def form_valid(self, form):
self.customer = form.cleaned_data['user']
self.customer = form.cleaned_data["user"]
self.create_selling()
return super(RefoundAccountView, self).form_valid(form)
def get_success_url(self):
return reverse('accounting:refound_account')
return reverse("accounting:refound_account")
def create_selling(self):
with transaction.atomic():
uprice = self.customer.customer.amount
refound_club_counter = Counter.objects.get(id=settings.SITH_COUNTER_REFOUND_ID)
refound_club_counter = Counter.objects.get(
id=settings.SITH_COUNTER_REFOUND_ID
)
refound_club = refound_club_counter.club
s = Selling(label=_('Refound account'), unit_price=uprice,
quantity=1, seller=self.operator,
s = Selling(
label=_("Refound account"),
unit_price=uprice,
quantity=1,
seller=self.operator,
customer=self.customer.customer,
club=refound_club, counter=refound_club_counter,
product=Product.objects.get(id=settings.SITH_PRODUCT_REFOUND_ID))
club=refound_club,
counter=refound_club_counter,
product=Product.objects.get(id=settings.SITH_PRODUCT_REFOUND_ID),
)
s.save()

View File

@ -21,4 +21,3 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -29,25 +29,28 @@ from rest_framework import routers
# Router config
router = routers.DefaultRouter()
router.register(r'counter', CounterViewSet, base_name='api_counter')
router.register(r'user', UserViewSet, base_name='api_user')
router.register(r'club', ClubViewSet, base_name='api_club')
router.register(r'group', GroupViewSet, base_name='api_group')
router.register(r"counter", CounterViewSet, base_name="api_counter")
router.register(r"user", UserViewSet, base_name="api_user")
router.register(r"club", ClubViewSet, base_name="api_club")
router.register(r"group", GroupViewSet, base_name="api_group")
# Launderette
router.register(r'launderette/place', LaunderettePlaceViewSet,
base_name='api_launderette_place')
router.register(r'launderette/machine', LaunderetteMachineViewSet,
base_name='api_launderette_machine')
router.register(r'launderette/token', LaunderetteTokenViewSet,
base_name='api_launderette_token')
router.register(
r"launderette/place", LaunderettePlaceViewSet, base_name="api_launderette_place"
)
router.register(
r"launderette/machine",
LaunderetteMachineViewSet,
base_name="api_launderette_machine",
)
router.register(
r"launderette/token", LaunderetteTokenViewSet, base_name="api_launderette_token"
)
urlpatterns = [
# API
url(r'^', include(router.urls)),
url(r'^login/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^markdown$', RenderMarkdown, name='api_markdown'),
url(r'^mailings$', FetchMailingLists, name='mailings_fetch')
url(r"^", include(router.urls)),
url(r"^login/", include("rest_framework.urls", namespace="rest_framework")),
url(r"^markdown$", RenderMarkdown, name="api_markdown"),
url(r"^mailings$", FetchMailingLists, name="mailings_fetch"),
]

View File

@ -30,19 +30,21 @@ from django.db.models.query import QuerySet
from core.views import can_view, can_edit
def check_if(obj, user, test):
"""
Detect if it's a single object or a queryset
aply a given test on individual object and return global permission
"""
if (isinstance(obj, QuerySet)):
if isinstance(obj, QuerySet):
for o in obj:
if (test(o, user) is False):
if test(o, user) is False:
return False
return True
else:
return test(obj, user)
class ManageModelMixin:
@detail_route()
def id(self, request, pk=None):
@ -53,19 +55,19 @@ class ManageModelMixin:
serializer = self.get_serializer(self.queryset)
return Response(serializer.data)
class RightModelViewSet(ManageModelMixin, viewsets.ModelViewSet):
class RightModelViewSet(ManageModelMixin, viewsets.ModelViewSet):
def dispatch(self, request, *arg, **kwargs):
res = super(RightModelViewSet,
self).dispatch(request, *arg, **kwargs)
res = super(RightModelViewSet, self).dispatch(request, *arg, **kwargs)
obj = self.queryset
user = self.request.user
try:
if (request.method == 'GET' and check_if(obj, user, can_view)):
if request.method == "GET" and check_if(obj, user, can_view):
return res
if (request.method != 'GET' and check_if(obj, user, can_edit)):
if request.method != "GET" and check_if(obj, user, can_edit):
return res
except: pass # To prevent bug with Anonymous user
except:
pass # To prevent bug with Anonymous user
raise PermissionDenied

View File

@ -30,15 +30,14 @@ from rest_framework.views import APIView
from core.templatetags.renderer import markdown
@api_view(['POST'])
@api_view(["POST"])
@renderer_classes((StaticHTMLRenderer,))
def RenderMarkdown(request):
"""
Render Markdown
"""
try:
data = markdown(request.POST['text'])
data = markdown(request.POST["text"])
except:
data = 'Error'
data = "Error"
return Response(data)

View File

@ -36,10 +36,9 @@ from api.views import RightModelViewSet
class ClubSerializer(serializers.ModelSerializer):
class Meta:
model = Club
fields = ('id', 'name', 'unix_name', 'address', 'members')
fields = ("id", "name", "unix_name", "address", "members")
class ClubViewSet(RightModelViewSet):
@ -51,13 +50,15 @@ class ClubViewSet(RightModelViewSet):
queryset = Club.objects.all()
@api_view(['GET'])
@api_view(["GET"])
@renderer_classes((StaticHTMLRenderer,))
def FetchMailingLists(request):
key = request.GET.get('key', '')
key = request.GET.get("key", "")
if key != settings.SITH_MAILING_FETCH_KEY:
raise PermissionDenied
data = ''
for mailing in Mailing.objects.filter(is_moderated=True, club__is_active=True).all():
data = ""
for mailing in Mailing.objects.filter(
is_moderated=True, club__is_active=True
).all():
data += mailing.fetch_format() + "\n"
return Response(data)

View File

@ -35,14 +35,12 @@ class CounterSerializer(serializers.ModelSerializer):
is_open = serializers.BooleanField(read_only=True)
barman_list = serializers.ListField(
child=serializers.IntegerField(),
read_only=True
child=serializers.IntegerField(), read_only=True
)
class Meta:
model = Counter
fields = ('id', 'name', 'type', 'club',
'products', 'is_open', 'barman_list')
fields = ("id", "name", "type", "club", "products", "is_open", "barman_list")
class CounterViewSet(RightModelViewSet):

View File

@ -30,7 +30,6 @@ from api.views import RightModelViewSet
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = RealGroup

View File

@ -30,34 +30,45 @@ from launderette.models import Launderette, Machine, Token
from api.views import RightModelViewSet
class LaunderettePlaceSerializer(serializers.ModelSerializer):
machine_list = serializers.ListField(
child=serializers.IntegerField(),
read_only=True
)
token_list = serializers.ListField(
child=serializers.IntegerField(),
read_only=True
child=serializers.IntegerField(), read_only=True
)
token_list = serializers.ListField(child=serializers.IntegerField(), read_only=True)
class Meta:
model = Launderette
fields = ('id', 'name', 'counter', 'machine_list',
'token_list', 'get_absolute_url')
fields = (
"id",
"name",
"counter",
"machine_list",
"token_list",
"get_absolute_url",
)
class LaunderetteMachineSerializer(serializers.ModelSerializer):
class Meta:
model = Machine
fields = ('id', 'name', 'type', 'is_working', 'launderette')
fields = ("id", "name", "type", "is_working", "launderette")
class LaunderetteTokenSerializer(serializers.ModelSerializer):
class Meta:
model = Token
fields = ('id', 'name', 'type', 'launderette', 'borrow_date',
'user', 'is_avaliable')
fields = (
"id",
"name",
"type",
"launderette",
"borrow_date",
"user",
"is_avaliable",
)
class LaunderettePlaceViewSet(RightModelViewSet):
"""
@ -67,6 +78,7 @@ class LaunderettePlaceViewSet(RightModelViewSet):
serializer_class = LaunderettePlaceSerializer
queryset = Launderette.objects.all()
class LaunderetteMachineViewSet(RightModelViewSet):
"""
Manage Washing Machines (api/v1/launderette/machine/)
@ -89,7 +101,7 @@ class LaunderetteTokenViewSet(RightModelViewSet):
"""
Return all washing tokens (api/v1/launderette/token/washing)
"""
self.queryset = self.queryset.filter(type='WASHING')
self.queryset = self.queryset.filter(type="WASHING")
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)
@ -98,7 +110,7 @@ class LaunderetteTokenViewSet(RightModelViewSet):
"""
Return all drying tokens (api/v1/launderette/token/drying)
"""
self.queryset = self.queryset.filter(type='DRYING')
self.queryset = self.queryset.filter(type="DRYING")
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)
@ -107,7 +119,9 @@ class LaunderetteTokenViewSet(RightModelViewSet):
"""
Return all avaliable tokens (api/v1/launderette/token/avaliable)
"""
self.queryset = self.queryset.filter(borrow_date__isnull=True, user__isnull=True)
self.queryset = self.queryset.filter(
borrow_date__isnull=True, user__isnull=True
)
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)
@ -116,6 +130,8 @@ class LaunderetteTokenViewSet(RightModelViewSet):
"""
Return all unavaliable tokens (api/v1/launderette/token/unavaliable)
"""
self.queryset = self.queryset.filter(borrow_date__isnull=False, user__isnull=False)
self.queryset = self.queryset.filter(
borrow_date__isnull=False, user__isnull=False
)
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)

View File

@ -34,11 +34,18 @@ from api.views import RightModelViewSet
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'email',
'date_of_birth', 'nick_name', 'is_active', 'date_joined')
fields = (
"id",
"first_name",
"last_name",
"email",
"date_of_birth",
"nick_name",
"is_active",
"date_joined",
)
class UserViewSet(RightModelViewSet):

View File

@ -21,4 +21,3 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -7,28 +7,92 @@ import django.core.validators
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Club',
name="Club",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=64, verbose_name='name')),
('unix_name', models.CharField(unique=True, max_length=30, error_messages={'unique': 'A club with that unix name already exists.'}, verbose_name='unix name', validators=[django.core.validators.RegexValidator('^[a-z0-9][a-z0-9._-]*[a-z0-9]$', 'Enter a valid unix name. This value may contain only letters, numbers ./-/_ characters.')])),
('address', models.CharField(max_length=254, verbose_name='address')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=64, verbose_name="name")),
(
"unix_name",
models.CharField(
unique=True,
max_length=30,
error_messages={
"unique": "A club with that unix name already exists."
},
verbose_name="unix name",
validators=[
django.core.validators.RegexValidator(
"^[a-z0-9][a-z0-9._-]*[a-z0-9]$",
"Enter a valid unix name. This value may contain only letters, numbers ./-/_ characters.",
)
],
),
),
("address", models.CharField(max_length=254, verbose_name="address")),
],
),
migrations.CreateModel(
name='Membership',
name="Membership",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('start_date', models.DateField(verbose_name='start date', auto_now=True)),
('end_date', models.DateField(null=True, verbose_name='end date', blank=True)),
('role', models.IntegerField(choices=[(0, 'Curious'), (1, 'Active member'), (2, 'Board member'), (3, 'IT supervisor'), (4, 'Secretary'), (5, 'Communication supervisor'), (7, 'Treasurer'), (9, 'Vice-President'), (10, 'President')], default=0, verbose_name='role')),
('description', models.CharField(max_length=128, blank=True, verbose_name='description')),
('club', models.ForeignKey(verbose_name='club', to='club.Club', related_name='members')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
(
"start_date",
models.DateField(verbose_name="start date", auto_now=True),
),
(
"end_date",
models.DateField(null=True, verbose_name="end date", blank=True),
),
(
"role",
models.IntegerField(
choices=[
(0, "Curious"),
(1, "Active member"),
(2, "Board member"),
(3, "IT supervisor"),
(4, "Secretary"),
(5, "Communication supervisor"),
(7, "Treasurer"),
(9, "Vice-President"),
(10, "President"),
],
default=0,
verbose_name="role",
),
),
(
"description",
models.CharField(
max_length=128, blank=True, verbose_name="description"
),
),
(
"club",
models.ForeignKey(
verbose_name="club", to="club.Club", related_name="members"
),
),
],
),
]

View File

@ -9,39 +9,57 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('club', '0001_initial'),
('core', '0001_initial'),
("club", "0001_initial"),
("core", "0001_initial"),
]
operations = [
migrations.AddField(
model_name='membership',
name='user',
field=models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, related_name='membership'),
model_name="membership",
name="user",
field=models.ForeignKey(
verbose_name="user",
to=settings.AUTH_USER_MODEL,
related_name="membership",
),
),
migrations.AddField(
model_name='club',
name='edit_groups',
field=models.ManyToManyField(to='core.Group', blank=True, related_name='editable_club'),
model_name="club",
name="edit_groups",
field=models.ManyToManyField(
to="core.Group", blank=True, related_name="editable_club"
),
),
migrations.AddField(
model_name='club',
name='home',
field=models.OneToOneField(blank=True, null=True, related_name='home_of_club', verbose_name='home', to='core.SithFile'),
model_name="club",
name="home",
field=models.OneToOneField(
blank=True,
null=True,
related_name="home_of_club",
verbose_name="home",
to="core.SithFile",
),
),
migrations.AddField(
model_name='club',
name='owner_group',
field=models.ForeignKey(default=1, to='core.Group', related_name='owned_club'),
model_name="club",
name="owner_group",
field=models.ForeignKey(
default=1, to="core.Group", related_name="owned_club"
),
),
migrations.AddField(
model_name='club',
name='parent',
field=models.ForeignKey(null=True, to='club.Club', related_name='children', blank=True),
model_name="club",
name="parent",
field=models.ForeignKey(
null=True, to="club.Club", related_name="children", blank=True
),
),
migrations.AddField(
model_name='club',
name='view_groups',
field=models.ManyToManyField(to='core.Group', blank=True, related_name='viewable_club'),
model_name="club",
name="view_groups",
field=models.ManyToManyField(
to="core.Group", blank=True, related_name="viewable_club"
),
),
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('club', '0002_auto_20160824_2152'),
]
dependencies = [("club", "0002_auto_20160824_2152")]
operations = [
migrations.AlterField(
model_name='membership',
name='start_date',
field=models.DateField(verbose_name='start date'),
),
model_name="membership",
name="start_date",
field=models.DateField(verbose_name="start date"),
)
]

View File

@ -7,14 +7,16 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('club', '0003_auto_20160902_2042'),
]
dependencies = [("club", "0003_auto_20160902_2042")]
operations = [
migrations.AlterField(
model_name='membership',
name='user',
field=models.ForeignKey(verbose_name='user', related_name='memberships', to=settings.AUTH_USER_MODEL),
model_name="membership",
name="user",
field=models.ForeignKey(
verbose_name="user",
related_name="memberships",
to=settings.AUTH_USER_MODEL,
),
)
]

View File

@ -7,14 +7,19 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('club', '0004_auto_20160915_1057'),
]
dependencies = [("club", "0004_auto_20160915_1057")]
operations = [
migrations.AlterField(
model_name='club',
name='home',
field=models.OneToOneField(related_name='home_of_club', blank=True, on_delete=django.db.models.deletion.SET_NULL, verbose_name='home', null=True, to='core.SithFile'),
model_name="club",
name="home",
field=models.OneToOneField(
related_name="home_of_club",
blank=True,
on_delete=django.db.models.deletion.SET_NULL,
verbose_name="home",
null=True,
to="core.SithFile",
),
)
]

View File

@ -7,14 +7,14 @@ import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('club', '0005_auto_20161120_1149'),
]
dependencies = [("club", "0005_auto_20161120_1149")]
operations = [
migrations.AlterField(
model_name='membership',
name='start_date',
field=models.DateField(verbose_name='start date', default=django.utils.timezone.now),
model_name="membership",
name="start_date",
field=models.DateField(
verbose_name="start date", default=django.utils.timezone.now
),
)
]

View File

@ -6,13 +6,10 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('club', '0006_auto_20161229_0040'),
]
dependencies = [("club", "0006_auto_20161229_0040")]
operations = [
migrations.AlterModelOptions(
name='club',
options={'ordering': ['name', 'unix_name']},
),
name="club", options={"ordering": ["name", "unix_name"]}
)
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('club', '0007_auto_20170324_0917'),
]
dependencies = [("club", "0007_auto_20170324_0917")]
operations = [
migrations.AlterField(
model_name='club',
name='id',
model_name="club",
name="id",
field=models.AutoField(primary_key=True, serialize=False, db_index=True),
),
)
]

View File

@ -11,31 +11,98 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('club', '0008_auto_20170515_2214'),
("club", "0008_auto_20170515_2214"),
]
operations = [
migrations.CreateModel(
name='Mailing',
name="Mailing",
fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('email', models.CharField(max_length=256, unique=True, validators=[django.core.validators.RegexValidator(re.compile('(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*\\Z|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"\\Z)', 34), 'Enter a valid address. Only the root of the address is needed.')], verbose_name='Email address')),
('is_moderated', models.BooleanField(default=False, verbose_name='is moderated')),
('club', models.ForeignKey(verbose_name='Club', related_name='mailings', to='club.Club')),
('moderator', models.ForeignKey(null=True, verbose_name='moderator', related_name='moderated_mailings', to=settings.AUTH_USER_MODEL)),
(
"id",
models.AutoField(
auto_created=True,
verbose_name="ID",
serialize=False,
primary_key=True,
),
),
(
"email",
models.CharField(
max_length=256,
unique=True,
validators=[
django.core.validators.RegexValidator(
re.compile(
"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\\Z|^\"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*\"\\Z)",
34,
),
"Enter a valid address. Only the root of the address is needed.",
)
],
verbose_name="Email address",
),
),
(
"is_moderated",
models.BooleanField(default=False, verbose_name="is moderated"),
),
(
"club",
models.ForeignKey(
verbose_name="Club", related_name="mailings", to="club.Club"
),
),
(
"moderator",
models.ForeignKey(
null=True,
verbose_name="moderator",
related_name="moderated_mailings",
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel(
name='MailingSubscription',
name="MailingSubscription",
fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('email', models.EmailField(max_length=254, verbose_name='Email address')),
('mailing', models.ForeignKey(verbose_name='Mailing', related_name='subscriptions', to='club.Mailing')),
('user', models.ForeignKey(null=True, verbose_name='User', related_name='mailing_subscriptions', blank=True, to=settings.AUTH_USER_MODEL)),
(
"id",
models.AutoField(
auto_created=True,
verbose_name="ID",
serialize=False,
primary_key=True,
),
),
(
"email",
models.EmailField(max_length=254, verbose_name="Email address"),
),
(
"mailing",
models.ForeignKey(
verbose_name="Mailing",
related_name="subscriptions",
to="club.Mailing",
),
),
(
"user",
models.ForeignKey(
null=True,
verbose_name="User",
related_name="mailing_subscriptions",
blank=True,
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.AlterUniqueTogether(
name='mailingsubscription',
unique_together=set([('user', 'email', 'mailing')]),
name="mailingsubscription",
unique_together=set([("user", "email", "mailing")]),
),
]

View File

@ -12,34 +12,44 @@ def generate_club_pages(apps, schema_editor):
club.make_page()
for child in Club.objects.filter(parent=club).all():
recursive_generate_club_page(child)
for club in Club.objects.filter(parent=None).all():
recursive_generate_club_page(club)
class Migration(migrations.Migration):
dependencies = [
('core', '0024_auto_20170906_1317'),
('club', '0010_club_logo'),
]
dependencies = [("core", "0024_auto_20170906_1317"), ("club", "0010_club_logo")]
operations = [
migrations.AddField(
model_name='club',
name='is_active',
field=models.BooleanField(default=True, verbose_name='is active'),
model_name="club",
name="is_active",
field=models.BooleanField(default=True, verbose_name="is active"),
),
migrations.AddField(
model_name='club',
name='page',
field=models.OneToOneField(related_name='club', blank=True, null=True, to='core.Page'),
model_name="club",
name="page",
field=models.OneToOneField(
related_name="club", blank=True, null=True, to="core.Page"
),
),
migrations.AddField(
model_name='club',
name='short_description',
field=models.CharField(verbose_name='short description', max_length=1000, default='', blank=True, null=True),
model_name="club",
name="short_description",
field=models.CharField(
verbose_name="short description",
max_length=1000,
default="",
blank=True,
null=True,
),
),
PsqlRunOnly(
"SET CONSTRAINTS ALL IMMEDIATE", reverse_sql=migrations.RunSQL.noop
),
PsqlRunOnly('SET CONSTRAINTS ALL IMMEDIATE', reverse_sql=migrations.RunSQL.noop),
migrations.RunPython(generate_club_pages),
PsqlRunOnly(migrations.RunSQL.noop, reverse_sql='SET CONSTRAINTS ALL IMMEDIATE'),
PsqlRunOnly(
migrations.RunSQL.noop, reverse_sql="SET CONSTRAINTS ALL IMMEDIATE"
),
]

View File

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('club', '0009_auto_20170822_2232'),
]
dependencies = [("club", "0009_auto_20170822_2232")]
operations = [
migrations.AddField(
model_name='club',
name='logo',
field=models.ImageField(null=True, upload_to='club_logos', blank=True, verbose_name='logo'),
model_name="club",
name="logo",
field=models.ImageField(
null=True, upload_to="club_logos", blank=True, verbose_name="logo"
),
)
]

View File

@ -7,14 +7,16 @@ import club.models
class Migration(migrations.Migration):
dependencies = [
('club', '0010_auto_20170912_2028'),
]
dependencies = [("club", "0010_auto_20170912_2028")]
operations = [
migrations.AlterField(
model_name='club',
name='owner_group',
field=models.ForeignKey(default=club.models.Club.get_default_owner_group, related_name='owned_club', to='core.Group'),
model_name="club",
name="owner_group",
field=models.ForeignKey(
default=club.models.Club.get_default_owner_group,
related_name="owned_club",
to="core.Group",
),
)
]

View File

@ -43,40 +43,64 @@ class Club(models.Model):
"""
The Club class, made as a tree to allow nice tidy organization
"""
id = models.AutoField(primary_key=True, db_index=True)
name = models.CharField(_('name'), max_length=64)
parent = models.ForeignKey('Club', related_name='children', null=True, blank=True)
unix_name = models.CharField(_('unix name'), max_length=30, unique=True,
name = models.CharField(_("name"), max_length=64)
parent = models.ForeignKey("Club", related_name="children", null=True, blank=True)
unix_name = models.CharField(
_("unix name"),
max_length=30,
unique=True,
validators=[
validators.RegexValidator(
r'^[a-z0-9][a-z0-9._-]*[a-z0-9]$',
_('Enter a valid unix name. This value may contain only '
'letters, numbers ./-/_ characters.')
r"^[a-z0-9][a-z0-9._-]*[a-z0-9]$",
_(
"Enter a valid unix name. This value may contain only "
"letters, numbers ./-/_ characters."
),
],
error_messages={
'unique': _("A club with that unix name already exists."),
},
)
logo = models.ImageField(upload_to='club_logos', verbose_name=_('logo'), null=True, blank=True)
is_active = models.BooleanField(_('is active'), default=True)
short_description = models.CharField(_('short description'), max_length=1000, default='', blank=True, null=True)
address = models.CharField(_('address'), max_length=254)
],
error_messages={"unique": _("A club with that unix name already exists.")},
)
logo = models.ImageField(
upload_to="club_logos", verbose_name=_("logo"), null=True, blank=True
)
is_active = models.BooleanField(_("is active"), default=True)
short_description = models.CharField(
_("short description"), max_length=1000, default="", blank=True, null=True
)
address = models.CharField(_("address"), max_length=254)
# This function prevents generating migration upon settings change
def get_default_owner_group(): return settings.SITH_GROUP_ROOT_ID
owner_group = models.ForeignKey(Group, related_name="owned_club", default=get_default_owner_group)
edit_groups = models.ManyToManyField(Group, related_name="editable_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,
on_delete=models.SET_NULL)
def get_default_owner_group():
return settings.SITH_GROUP_ROOT_ID
owner_group = models.ForeignKey(
Group, related_name="owned_club", default=get_default_owner_group
)
edit_groups = models.ManyToManyField(
Group, related_name="editable_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,
on_delete=models.SET_NULL,
)
page = models.OneToOneField(Page, related_name="club", blank=True, null=True)
class Meta:
ordering = ['name', 'unix_name']
ordering = ["name", "unix_name"]
@cached_property
def president(self):
return self.members.filter(role=settings.SITH_CLUB_ROLES_ID['President'], end_date=None).first()
return self.members.filter(
role=settings.SITH_CLUB_ROLES_ID["President"], end_date=None
).first()
def check_loop(self):
"""Raise a validation error when a loop is found within the parent list"""
@ -84,7 +108,7 @@ class Club(models.Model):
cur = self
while cur.parent is not None:
if cur in objs:
raise ValidationError(_('You can not make loops in clubs'))
raise ValidationError(_("You can not make loops in clubs"))
objs.append(cur)
cur = cur.parent
@ -130,7 +154,12 @@ class Club(models.Model):
self.page.unset_lock()
self.page.name = self.unix_name
self.page.save(force_lock=True)
elif self.page and self.parent and self.parent.page and self.page.parent != self.parent.page:
elif (
self.page
and self.parent
and self.parent.page
and self.page.parent != self.parent.page
):
self.page.unset_lock()
self.page.parent = self.parent.page
self.page.save(force_lock=True)
@ -150,7 +179,9 @@ class Club(models.Model):
board.save()
member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX)
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.home.edit_groups = [board]
self.home.view_groups = [member, subscribers]
@ -161,7 +192,7 @@ class Club(models.Model):
return self.name
def get_absolute_url(self):
return reverse('club:club_view', kwargs={'club_id': self.id})
return reverse("club:club_view", kwargs={"club_id": self.id})
def get_display_name(self):
return self.name
@ -223,24 +254,48 @@ class Membership(models.Model):
A User is currently member of all the Clubs where its Membership has an end_date set to null/None.
Otherwise, it's a past membership kept because it can be very useful to see who was in which Club in the past.
"""
user = models.ForeignKey(User, verbose_name=_('user'), related_name="memberships", null=False, blank=False)
club = models.ForeignKey(Club, verbose_name=_('club'), related_name="members", null=False, blank=False)
start_date = models.DateField(_('start date'), default=timezone.now)
end_date = models.DateField(_('end date'), null=True, blank=True)
role = models.IntegerField(_('role'), choices=sorted(settings.SITH_CLUB_ROLES.items()),
default=sorted(settings.SITH_CLUB_ROLES.items())[0][0])
description = models.CharField(_('description'), max_length=128, null=False, blank=True)
user = models.ForeignKey(
User,
verbose_name=_("user"),
related_name="memberships",
null=False,
blank=False,
)
club = models.ForeignKey(
Club, verbose_name=_("club"), related_name="members", null=False, blank=False
)
start_date = models.DateField(_("start date"), default=timezone.now)
end_date = models.DateField(_("end date"), null=True, blank=True)
role = models.IntegerField(
_("role"),
choices=sorted(settings.SITH_CLUB_ROLES.items()),
default=sorted(settings.SITH_CLUB_ROLES.items())[0][0],
)
description = models.CharField(
_("description"), max_length=128, null=False, blank=True
)
def clean(self):
sub = User.objects.filter(pk=self.user.pk).first()
if sub is None or not sub.is_subscribed:
raise ValidationError(_('User must be subscriber to take part to a club'))
if Membership.objects.filter(user=self.user).filter(club=self.club).filter(end_date=None).exists():
raise ValidationError(_('User is already member of that club'))
raise ValidationError(_("User must be subscriber to take part to a club"))
if (
Membership.objects.filter(user=self.user)
.filter(club=self.club)
.filter(end_date=None)
.exists()
):
raise ValidationError(_("User is already member of that club"))
def __str__(self):
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 ""
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 "")
)
def is_owned_by(self, user):
@ -255,11 +310,13 @@ class Membership(models.Model):
"""
if user.memberships:
ms = user.memberships.filter(club=self.club, end_date=None).first()
return (ms and ms.role >= self.role) or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
return (ms and ms.role >= self.role) or user.is_in_group(
settings.SITH_MAIN_BOARD_GROUP
)
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
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})
class Mailing(models.Model):
@ -267,14 +324,27 @@ class Mailing(models.Model):
This class correspond to a mailing list
Remember that mailing lists should be validated by UTBM
"""
club = models.ForeignKey(Club, verbose_name=_('Club'), related_name="mailings", null=False, blank=False)
email = models.CharField(_('Email address'), unique=True, null=False, blank=False, max_length=256,
club = models.ForeignKey(
Club, verbose_name=_("Club"), related_name="mailings", null=False, blank=False
)
email = models.CharField(
_("Email address"),
unique=True,
null=False,
blank=False,
max_length=256,
validators=[
RegexValidator(validate_email.user_regex,
_('Enter a valid address. Only the root of the address is needed.'))
])
is_moderated = models.BooleanField(_('is moderated'), default=False)
moderator = models.ForeignKey(User, related_name="moderated_mailings", verbose_name=_("moderator"), null=True)
RegexValidator(
validate_email.user_regex,
_("Enter a valid address. Only the root of the address is needed."),
)
],
)
is_moderated = models.BooleanField(_("is moderated"), default=False)
moderator = models.ForeignKey(
User, related_name="moderated_mailings", verbose_name=_("moderator"), null=True
)
def clean(self):
if self.can_moderate(self.moderator):
@ -285,13 +355,17 @@ class Mailing(models.Model):
@property
def email_full(self):
return self.email + '@' + settings.SITH_MAILING_DOMAIN
return self.email + "@" + settings.SITH_MAILING_DOMAIN
def can_moderate(self, user):
return user.is_root or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def is_owned_by(self, user):
return user.is_in_group(self) or user.is_root or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
return (
user.is_in_group(self)
or user.is_root
or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
)
def can_view(self, user):
return self.club.has_rights_in_club(user)
@ -305,16 +379,26 @@ class Mailing(models.Model):
super(Mailing, self).delete()
def fetch_format(self):
resp = self.email + ': '
resp = self.email + ": "
for sub in self.subscriptions.all():
resp += sub.fetch_format()
return resp
def save(self):
if not self.is_moderated:
for user in RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first().users.all():
if not user.notifications.filter(type="MAILING_MODERATION", viewed=False).exists():
Notification(user=user, url=reverse('com:mailing_admin'), type="MAILING_MODERATION").save()
for user in (
RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID)
.first()
.users.all()
):
if not user.notifications.filter(
type="MAILING_MODERATION", viewed=False
).exists():
Notification(
user=user,
url=reverse("com:mailing_admin"),
type="MAILING_MODERATION",
).save()
super(Mailing, self).save()
def __str__(self):
@ -325,12 +409,25 @@ class MailingSubscription(models.Model):
"""
This class makes the link between user and mailing list
"""
mailing = models.ForeignKey(Mailing, verbose_name=_('Mailing'), related_name="subscriptions", null=False, blank=False)
user = models.ForeignKey(User, verbose_name=_('User'), related_name="mailing_subscriptions", null=True, blank=True)
email = models.EmailField(_('Email address'), blank=False, null=False)
mailing = models.ForeignKey(
Mailing,
verbose_name=_("Mailing"),
related_name="subscriptions",
null=False,
blank=False,
)
user = models.ForeignKey(
User,
verbose_name=_("User"),
related_name="mailing_subscriptions",
null=True,
blank=True,
)
email = models.EmailField(_("Email address"), blank=False, null=False)
class Meta:
unique_together = (('user', 'email', 'mailing'),)
unique_together = (("user", "email", "mailing"),)
def clean(self):
if not self.user and not self.email:
@ -338,17 +435,25 @@ class MailingSubscription(models.Model):
try:
if self.user and not self.email:
self.email = self.user.email
if MailingSubscription.objects.filter(mailing=self.mailing, email=self.email).exists():
raise ValidationError(_("This email is already suscribed in this mailing"))
if MailingSubscription.objects.filter(
mailing=self.mailing, email=self.email
).exists():
raise ValidationError(
_("This email is already suscribed in this mailing")
)
except ObjectDoesNotExist:
pass
super(MailingSubscription, self).clean()
def is_owned_by(self, user):
return self.mailing.club.has_rights_in_club(user) or user.is_root or self.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
return (
self.mailing.club.has_rights_in_club(user)
or user.is_root
or self.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
)
def can_be_edited_by(self, user):
return (self.user is not None and user.id == self.user.id)
return self.user is not None and user.id == self.user.id
@property
def get_email(self):
@ -357,7 +462,7 @@ class MailingSubscription(models.Model):
return self.email
def fetch_format(self):
return self.get_email + ' '
return self.get_email + " "
def __str__(self):
if self.user:

View File

@ -41,66 +41,92 @@ class ClubTest(TestCase):
self.bdf = Club.objects.filter(unix_name="bdf").first()
def test_create_add_user_to_club_from_root_ok(self):
self.client.login(username='root', password='plop')
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
"user": self.skia.id,
"start_date": "12/06/2016",
"role": 3})
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 3},
)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(response.status_code == 200)
self.assertTrue("S&#39; Kia</a></td>\\n <td>Responsable info</td>" in str(response.content))
self.assertTrue(
"S&#39; 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):
self.client.login(username='root', password='plop')
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
"user": self.guy.id,
"start_date": "12/06/2016",
"role": 3})
self.client.login(username="root", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.guy.id, "start_date": "12/06/2016", "role": 3},
)
self.assertTrue(response.status_code == 200)
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}))
self.assertFalse("Guy Carlier</a></td>\\n <td>Responsable info</td>" in str(response.content))
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)
)
def test_create_add_user_to_club_from_root_fail_already_in_club(self):
self.client.login(username='root', password='plop')
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
"user": self.skia.id,
"start_date": "12/06/2016",
"role": 3})
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
self.assertTrue("S&#39; 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}), {
"user": self.skia.id,
"start_date": "12/06/2016",
"role": 4})
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 3},
)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
self.assertTrue(
"S&#39; 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}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 4},
)
self.assertTrue(response.status_code == 200)
self.assertFalse("S&#39; Kia</a></td>\\n <td>Secrétaire</td>" in str(response.content))
self.assertFalse(
"S&#39; Kia</a></td>\\n <td>Secrétaire</td>"
in str(response.content)
)
def test_create_add_user_to_club_from_skia_ok(self):
self.client.login(username='root', password='plop')
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
"user": self.skia.id,
"start_date": "12/06/2016",
"role": 10})
self.client.login(username='skia', password='plop')
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
"user": self.rbatsbak.id,
"start_date": "12/06/2016",
"role": 9})
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 10},
)
self.client.login(username="skia", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.rbatsbak.id, "start_date": "12/06/2016", "role": 9},
)
response = self.client.get(
reverse("club:club_members", kwargs={"club_id": self.bdf.id})
)
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):
self.client.login(username='root', password='plop')
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
"user": self.rbatsbak.id,
"start_date": "12/06/2016",
"role": 3})
self.client.login(username='rbatsbak', password='plop')
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
"user": self.skia.id,
"start_date": "12/06/2016",
"role": 10})
self.client.login(username="root", password="plop")
self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.rbatsbak.id, "start_date": "12/06/2016", "role": 3},
)
self.client.login(username="rbatsbak", password="plop")
response = self.client.post(
reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
{"user": self.skia.id, "start_date": "12/06/2016", "role": 10},
)
self.assertTrue(response.status_code == 200)
self.assertTrue("<li>Vous n&#39;avez pas la permission de faire cela</li>" in str(response.content))
self.assertTrue(
"<li>Vous n&#39;avez pas la permission de faire cela</li>"
in str(response.content)
)

View File

@ -28,30 +28,96 @@ from django.conf.urls import url
from club.views import *
urlpatterns = [
url(r'^$', ClubListView.as_view(), name='club_list'),
url(r'^new$', ClubCreateView.as_view(), name='club_new'),
url(r'^stats$', ClubStatView.as_view(), name='club_stats'),
url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
url(r'^(?P<club_id>[0-9]+)/rev/(?P<rev_id>[0-9]+)/$', ClubRevView.as_view(), name='club_view_rev'),
url(r'^(?P<club_id>[0-9]+)/hist$', ClubPageHistView.as_view(), name='club_hist'),
url(r'^(?P<club_id>[0-9]+)/edit$', ClubEditView.as_view(), name='club_edit'),
url(r'^(?P<club_id>[0-9]+)/edit/page$', ClubPageEditView.as_view(), name='club_edit_page'),
url(r'^(?P<club_id>[0-9]+)/members$', ClubMembersView.as_view(), name='club_members'),
url(r'^(?P<club_id>[0-9]+)/elderlies$', ClubOldMembersView.as_view(), name='club_old_members'),
url(r'^(?P<club_id>[0-9]+)/sellings$', ClubSellingView.as_view(), name='club_sellings'),
url(r'^(?P<club_id>[0-9]+)/sellings/csv$', ClubSellingCSVView.as_view(), name='sellings_csv'),
url(r'^(?P<club_id>[0-9]+)/prop$', ClubEditPropView.as_view(), name='club_prop'),
url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
url(r'^(?P<club_id>[0-9]+)/mailing$', ClubMailingView.as_view(action="display"), name='mailing'),
url(r'^(?P<club_id>[0-9]+)/mailing/new/mailing$', ClubMailingView.as_view(action="add_mailing"), name='mailing_create'),
url(r'^(?P<club_id>[0-9]+)/mailing/new/subscription$', ClubMailingView.as_view(action="add_member"), name='mailing_subscription_create'),
url(r'^(?P<mailing_id>[0-9]+)/mailing/generate$', MailingAutoGenerationView.as_view(), name='mailing_generate'),
url(r'^(?P<mailing_id>[0-9]+)/mailing/clean$', MailingAutoCleanView.as_view(), name='mailing_clean'),
url(r'^(?P<mailing_id>[0-9]+)/mailing/delete$', MailingDeleteView.as_view(), name='mailing_delete'),
url(r'^(?P<mailing_subscription_id>[0-9]+)/mailing/delete/subscription$', MailingSubscriptionDeleteView.as_view(), name='mailing_subscription_delete'),
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
url(r'^(?P<club_id>[0-9]+)/poster$', PosterListView.as_view(), name='poster_list'),
url(r'^(?P<club_id>[0-9]+)/poster/create$', PosterCreateView.as_view(), name='poster_create'),
url(r'^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/edit$', PosterEditView.as_view(), name='poster_edit'),
url(r'^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/delete$', PosterDeleteView.as_view(), name='poster_delete'),
url(r"^$", ClubListView.as_view(), name="club_list"),
url(r"^new$", ClubCreateView.as_view(), name="club_new"),
url(r"^stats$", ClubStatView.as_view(), name="club_stats"),
url(r"^(?P<club_id>[0-9]+)/$", ClubView.as_view(), name="club_view"),
url(
r"^(?P<club_id>[0-9]+)/rev/(?P<rev_id>[0-9]+)/$",
ClubRevView.as_view(),
name="club_view_rev",
),
url(r"^(?P<club_id>[0-9]+)/hist$", ClubPageHistView.as_view(), name="club_hist"),
url(r"^(?P<club_id>[0-9]+)/edit$", ClubEditView.as_view(), name="club_edit"),
url(
r"^(?P<club_id>[0-9]+)/edit/page$",
ClubPageEditView.as_view(),
name="club_edit_page",
),
url(
r"^(?P<club_id>[0-9]+)/members$", ClubMembersView.as_view(), name="club_members"
),
url(
r"^(?P<club_id>[0-9]+)/elderlies$",
ClubOldMembersView.as_view(),
name="club_old_members",
),
url(
r"^(?P<club_id>[0-9]+)/sellings$",
ClubSellingView.as_view(),
name="club_sellings",
),
url(
r"^(?P<club_id>[0-9]+)/sellings/csv$",
ClubSellingCSVView.as_view(),
name="sellings_csv",
),
url(r"^(?P<club_id>[0-9]+)/prop$", ClubEditPropView.as_view(), name="club_prop"),
url(r"^(?P<club_id>[0-9]+)/tools$", ClubToolsView.as_view(), name="tools"),
url(
r"^(?P<club_id>[0-9]+)/mailing$",
ClubMailingView.as_view(action="display"),
name="mailing",
),
url(
r"^(?P<club_id>[0-9]+)/mailing/new/mailing$",
ClubMailingView.as_view(action="add_mailing"),
name="mailing_create",
),
url(
r"^(?P<club_id>[0-9]+)/mailing/new/subscription$",
ClubMailingView.as_view(action="add_member"),
name="mailing_subscription_create",
),
url(
r"^(?P<mailing_id>[0-9]+)/mailing/generate$",
MailingAutoGenerationView.as_view(),
name="mailing_generate",
),
url(
r"^(?P<mailing_id>[0-9]+)/mailing/clean$",
MailingAutoCleanView.as_view(),
name="mailing_clean",
),
url(
r"^(?P<mailing_id>[0-9]+)/mailing/delete$",
MailingDeleteView.as_view(),
name="mailing_delete",
),
url(
r"^(?P<mailing_subscription_id>[0-9]+)/mailing/delete/subscription$",
MailingSubscriptionDeleteView.as_view(),
name="mailing_subscription_delete",
),
url(
r"^membership/(?P<membership_id>[0-9]+)/set_old$",
MembershipSetOldView.as_view(),
name="membership_set_old",
),
url(r"^(?P<club_id>[0-9]+)/poster$", PosterListView.as_view(), name="poster_list"),
url(
r"^(?P<club_id>[0-9]+)/poster/create$",
PosterCreateView.as_view(),
name="poster_create",
),
url(
r"^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/edit$",
PosterEditView.as_view(),
name="poster_edit",
),
url(
r"^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/delete$",
PosterDeleteView.as_view(),
name="poster_delete",
),
]

View File

@ -37,13 +37,25 @@ from ajax_select.fields import AutoCompleteSelectField
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, redirect
from core.views import CanCreateMixin, CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, PageEditViewBase
from core.views import (
CanCreateMixin,
CanViewMixin,
CanEditMixin,
CanEditPropMixin,
TabedViewMixin,
PageEditViewBase,
)
from core.views.forms import SelectDate, SelectDateTime
from club.models import Club, Membership, Mailing, MailingSubscription
from sith.settings import SITH_MAXIMUM_FREE_ROLE
from counter.models import Selling, Counter
from core.models import User, PageRev
from com.views import PosterListBaseView, PosterCreateBaseView, PosterEditBaseView, PosterDeleteBaseView
from com.views import (
PosterListBaseView,
PosterCreateBaseView,
PosterEditBaseView,
PosterDeleteBaseView,
)
from com.models import Poster
from django.conf import settings
@ -54,7 +66,7 @@ from django.conf import settings
class ClubEditForm(forms.ModelForm):
class Meta:
model = Club
fields = ['address', 'logo', 'short_description']
fields = ["address", "logo", "short_description"]
def __init__(self, *args, **kwargs):
super(ClubEditForm, self).__init__(*args, **kwargs)
@ -64,36 +76,40 @@ class ClubEditForm(forms.ModelForm):
class MailingForm(forms.ModelForm):
class Meta:
model = Mailing
fields = ('email', 'club', 'moderator')
fields = ("email", "club", "moderator")
def __init__(self, *args, **kwargs):
club_id = kwargs.pop('club_id', None)
user_id = kwargs.pop('user_id', -1) # Remember 0 is treated as None
club_id = kwargs.pop("club_id", None)
user_id = kwargs.pop("user_id", -1) # Remember 0 is treated as None
super(MailingForm, self).__init__(*args, **kwargs)
if club_id:
self.fields['club'].queryset = Club.objects.filter(id=club_id)
self.fields['club'].initial = club_id
self.fields['club'].widget = forms.HiddenInput()
self.fields["club"].queryset = Club.objects.filter(id=club_id)
self.fields["club"].initial = club_id
self.fields["club"].widget = forms.HiddenInput()
if user_id >= 0:
self.fields['moderator'].queryset = User.objects.filter(id=user_id)
self.fields['moderator'].initial = user_id
self.fields['moderator'].widget = forms.HiddenInput()
self.fields["moderator"].queryset = User.objects.filter(id=user_id)
self.fields["moderator"].initial = user_id
self.fields["moderator"].widget = forms.HiddenInput()
class MailingSubscriptionForm(forms.ModelForm):
class Meta:
model = MailingSubscription
fields = ('mailing', 'user', 'email')
fields = ("mailing", "user", "email")
def __init__(self, *args, **kwargs):
kwargs.pop('user_id', None) # For standart interface
club_id = kwargs.pop('club_id', None)
kwargs.pop("user_id", None) # For standart interface
club_id = kwargs.pop("club_id", None)
super(MailingSubscriptionForm, self).__init__(*args, **kwargs)
self.fields['email'].required = False
self.fields["email"].required = False
if club_id:
self.fields['mailing'].queryset = Mailing.objects.filter(club__id=club_id, is_moderated=True)
self.fields["mailing"].queryset = Mailing.objects.filter(
club__id=club_id, is_moderated=True
)
user = AutoCompleteSelectField('users', label=_('User'), help_text=None, required=False)
user = AutoCompleteSelectField(
"users", label=_("User"), help_text=None, required=False
)
class ClubTabsMixin(TabedViewMixin):
@ -105,66 +121,105 @@ class ClubTabsMixin(TabedViewMixin):
def get_list_of_tabs(self):
tab_list = []
tab_list.append({
'url': reverse('club:club_view', kwargs={'club_id': self.object.id}),
'slug': 'infos',
'name': _("Infos"),
})
tab_list.append(
{
"url": reverse("club:club_view", kwargs={"club_id": self.object.id}),
"slug": "infos",
"name": _("Infos"),
}
)
if self.request.user.can_view(self.object):
tab_list.append({
'url': reverse('club:club_members', kwargs={'club_id': self.object.id}),
'slug': 'members',
'name': _("Members"),
})
tab_list.append({
'url': reverse('club:club_old_members', kwargs={'club_id': self.object.id}),
'slug': 'elderlies',
'name': _("Old members"),
})
tab_list.append(
{
"url": reverse(
"club:club_members", kwargs={"club_id": self.object.id}
),
"slug": "members",
"name": _("Members"),
}
)
tab_list.append(
{
"url": reverse(
"club:club_old_members", kwargs={"club_id": self.object.id}
),
"slug": "elderlies",
"name": _("Old members"),
}
)
if self.object.page:
tab_list.append({
'url': reverse('club:club_hist', kwargs={'club_id': self.object.id}),
'slug': 'history',
'name': _("History"),
})
tab_list.append(
{
"url": reverse(
"club:club_hist", kwargs={"club_id": self.object.id}
),
"slug": "history",
"name": _("History"),
}
)
if self.request.user.can_edit(self.object):
tab_list.append({
'url': reverse('club:tools', kwargs={'club_id': self.object.id}),
'slug': 'tools',
'name': _("Tools"),
})
tab_list.append({
'url': reverse('club:club_edit', kwargs={'club_id': self.object.id}),
'slug': 'edit',
'name': _("Edit"),
})
tab_list.append(
{
"url": reverse("club:tools", kwargs={"club_id": self.object.id}),
"slug": "tools",
"name": _("Tools"),
}
)
tab_list.append(
{
"url": reverse(
"club:club_edit", kwargs={"club_id": self.object.id}
),
"slug": "edit",
"name": _("Edit"),
}
)
if self.object.page and self.request.user.can_edit(self.object.page):
tab_list.append({
'url': reverse('core:page_edit', kwargs={'page_name': self.object.page.get_full_name()}),
'slug': 'page_edit',
'name': _('Edit club page')
})
tab_list.append({
'url': reverse('club:club_sellings', kwargs={'club_id': self.object.id}),
'slug': 'sellings',
'name': _("Sellings"),
})
tab_list.append({
'url': reverse('club:mailing', kwargs={'club_id': self.object.id}),
'slug': 'mailing',
'name': _("Mailing list"),
})
tab_list.append({
'url': reverse('club:poster_list', kwargs={'club_id': self.object.id}),
'slug': 'posters',
'name': _("Posters list"),
})
tab_list.append(
{
"url": reverse(
"core:page_edit",
kwargs={"page_name": self.object.page.get_full_name()},
),
"slug": "page_edit",
"name": _("Edit club page"),
}
)
tab_list.append(
{
"url": reverse(
"club:club_sellings", kwargs={"club_id": self.object.id}
),
"slug": "sellings",
"name": _("Sellings"),
}
)
tab_list.append(
{
"url": reverse("club:mailing", kwargs={"club_id": self.object.id}),
"slug": "mailing",
"name": _("Mailing list"),
}
)
tab_list.append(
{
"url": reverse(
"club:poster_list", kwargs={"club_id": self.object.id}
),
"slug": "posters",
"name": _("Posters list"),
}
)
if self.request.user.is_owner(self.object):
tab_list.append({
'url': reverse('club:club_prop', kwargs={'club_id': self.object.id}),
'slug': 'props',
'name': _("Props"),
})
tab_list.append(
{
"url": reverse(
"club:club_prop", kwargs={"club_id": self.object.id}
),
"slug": "props",
"name": _("Props"),
}
)
return tab_list
@ -172,23 +227,25 @@ class ClubListView(ListView):
"""
List the Clubs
"""
model = Club
template_name = 'club/club_list.jinja'
template_name = "club/club_list.jinja"
class ClubView(ClubTabsMixin, DetailView):
"""
Front page of a Club
"""
model = Club
pk_url_kwarg = "club_id"
template_name = 'club/club_detail.jinja'
template_name = "club/club_detail.jinja"
current_tab = "infos"
def get_context_data(self, **kwargs):
kwargs = super(ClubView, self).get_context_data(**kwargs)
if self.object.page and self.object.page.revisions.exists():
kwargs['page_revision'] = self.object.page.revisions.last().content
kwargs["page_revision"] = self.object.page.revisions.last().content
return kwargs
@ -196,23 +253,24 @@ class ClubRevView(ClubView):
"""
Display a specific page revision
"""
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
self.revision = get_object_or_404(PageRev, pk=kwargs['rev_id'], page__club=obj)
self.revision = get_object_or_404(PageRev, pk=kwargs["rev_id"], page__club=obj)
return super(ClubRevView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
kwargs = super(ClubRevView, self).get_context_data(**kwargs)
kwargs['page_revision'] = self.revision.content
kwargs["page_revision"] = self.revision.content
return kwargs
class ClubPageEditView(ClubTabsMixin, PageEditViewBase):
template_name = 'club/pagerev_edit.jinja'
template_name = "club/pagerev_edit.jinja"
current_tab = "page_edit"
def dispatch(self, request, *args, **kwargs):
self.club = get_object_or_404(Club, pk=kwargs['club_id'])
self.club = get_object_or_404(Club, pk=kwargs["club_id"])
if not self.club.page:
raise Http404
return super(ClubPageEditView, self).dispatch(request, *args, **kwargs)
@ -222,16 +280,17 @@ class ClubPageEditView(ClubTabsMixin, PageEditViewBase):
return self._get_revision()
def get_success_url(self, **kwargs):
return reverse_lazy('club:club_view', kwargs={'club_id': self.club.id})
return reverse_lazy("club:club_view", kwargs={"club_id": self.club.id})
class ClubPageHistView(ClubTabsMixin, CanViewMixin, DetailView):
"""
Modification hostory of the page
"""
model = Club
pk_url_kwarg = "club_id"
template_name = 'club/page_history.jinja'
template_name = "club/page_history.jinja"
current_tab = "history"
@ -239,9 +298,10 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
"""
Tools page of a Club
"""
model = Club
pk_url_kwarg = "club_id"
template_name = 'club/club_tools.jinja'
template_name = "club/club_tools.jinja"
current_tab = "tools"
@ -249,16 +309,18 @@ class ClubMemberForm(forms.ModelForm):
"""
Form handling the members of a club
"""
error_css_class = 'error'
required_css_class = 'required'
error_css_class = "error"
required_css_class = "required"
class Meta:
model = Membership
fields = ['user', 'role', 'start_date', 'description']
widgets = {
'start_date': SelectDate
}
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
fields = ["user", "role", "start_date", "description"]
widgets = {"start_date": SelectDate}
user = AutoCompleteSelectField(
"users", required=True, label=_("Select user"), help_text=None
)
def save(self, *args, **kwargs):
"""
@ -272,10 +334,11 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
"""
View of a club's members
"""
model = Club
pk_url_kwarg = "club_id"
form_class = ClubMemberForm
template_name = 'club/club_members.jinja'
template_name = "club/club_members.jinja"
current_tab = "members"
def get_form(self):
@ -284,12 +347,19 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
That's why the save method of ClubMemberForm is overridden.
"""
form = super(ClubMembersView, self).get_form()
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()
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()
)
if form.instance is None: # Instanciate a new membership
form.instance = Membership(club=self.object, user=self.request.user)
if not self.request.user.is_root:
form.fields.pop('start_date', None)
form.fields.pop("start_date", None)
return form
def form_valid(self, form):
@ -298,9 +368,12 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
"""
user = self.request.user
ms = self.object.get_membership_for(user)
if (form.cleaned_data['role'] <= SITH_MAXIMUM_FREE_ROLE or
(ms is not None and ms.role >= form.cleaned_data['role']) or
user.is_board_member or user.is_root):
if (
form.cleaned_data["role"] <= SITH_MAXIMUM_FREE_ROLE
or (ms is not None and ms.role >= form.cleaned_data["role"])
or user.is_board_member
or user.is_root
):
form.save()
form = self.form_class()
return super(ModelFormMixin, self).form_valid(form)
@ -313,39 +386,57 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
return super(ClubMembersView, self).dispatch(request, *args, **kwargs)
def get_success_url(self, **kwargs):
return reverse_lazy('club:club_members', kwargs={'club_id': self.club.id})
return reverse_lazy("club:club_members", kwargs={"club_id": self.club.id})
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
"""
Old members of a club
"""
model = Club
pk_url_kwarg = "club_id"
template_name = 'club/club_old_members.jinja'
template_name = "club/club_old_members.jinja"
current_tab = "elderlies"
class SellingsFormBase(forms.Form):
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)
counter = forms.ModelChoiceField(Counter.objects.order_by('name').all(), label=_("Counter"), required=False)
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,
)
counter = forms.ModelChoiceField(
Counter.objects.order_by("name").all(), label=_("Counter"), required=False
)
class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
"""
Sellings of a club
"""
model = Club
pk_url_kwarg = "club_id"
template_name = 'club/club_sellings.jinja'
template_name = "club/club_sellings.jinja"
current_tab = "sellings"
def get_form_class(self):
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):
kwargs = super(ClubSellingView, self).get_context_data(**kwargs)
@ -354,21 +445,23 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
if form.is_valid():
if not len([v for v in form.cleaned_data.values() if v is not None]):
qs = Selling.objects.filter(id=-1)
if form.cleaned_data['begin_date']:
qs = qs.filter(date__gte=form.cleaned_data['begin_date'])
if form.cleaned_data['end_date']:
qs = qs.filter(date__lte=form.cleaned_data['end_date'])
if form.cleaned_data['counter']:
qs = qs.filter(counter=form.cleaned_data['counter'])
if form.cleaned_data['product']:
qs = qs.filter(product__id=form.cleaned_data['product'].id)
kwargs['result'] = qs.all().order_by('-id')
kwargs['total'] = sum([s.quantity * s.unit_price for s in qs.all()])
kwargs['total_quantity'] = sum([s.quantity for s in qs.all()])
kwargs['benefit'] = kwargs['total'] - sum([s.product.purchase_price for s in qs.exclude(product=None)])
if form.cleaned_data["begin_date"]:
qs = qs.filter(date__gte=form.cleaned_data["begin_date"])
if form.cleaned_data["end_date"]:
qs = qs.filter(date__lte=form.cleaned_data["end_date"])
if form.cleaned_data["counter"]:
qs = qs.filter(counter=form.cleaned_data["counter"])
if form.cleaned_data["product"]:
qs = qs.filter(product__id=form.cleaned_data["product"].id)
kwargs["result"] = qs.all().order_by("-id")
kwargs["total"] = sum([s.quantity * s.unit_price for s in qs.all()])
kwargs["total_quantity"] = sum([s.quantity for s in qs.all()])
kwargs["benefit"] = kwargs["total"] - sum(
[s.product.purchase_price for s in qs.exclude(product=None)]
)
else:
kwargs['result'] = qs[:0]
kwargs['form'] = form
kwargs["result"] = qs[:0]
kwargs["form"] = form
return kwargs
@ -379,36 +472,56 @@ class ClubSellingCSVView(ClubSellingView):
def get(self, request, *args, **kwargs):
import csv
response = HttpResponse(content_type='text/csv')
response = HttpResponse(content_type="text/csv")
self.object = self.get_object()
name = _("Sellings") + "_" + self.object.name + ".csv"
response['Content-Disposition'] = 'filename=' + name
response["Content-Disposition"] = "filename=" + name
kwargs = self.get_context_data(**kwargs)
writer = csv.writer(response, delimiter=";", lineterminator='\n', quoting=csv.QUOTE_ALL)
writer = csv.writer(
response, delimiter=";", lineterminator="\n", quoting=csv.QUOTE_ALL
)
writer.writerow([_t('Quantity'), kwargs['total_quantity']])
writer.writerow([_t('Total'), kwargs['total']])
writer.writerow([_t('Benefit'), kwargs['benefit']])
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')])
for o in kwargs['result']:
writer.writerow([_t("Quantity"), kwargs["total_quantity"]])
writer.writerow([_t("Total"), kwargs["total"]])
writer.writerow([_t("Benefit"), kwargs["benefit"]])
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"),
]
)
for o in kwargs["result"]:
row = [o.date, o.counter]
if o.seller:
row.append(o.seller.get_display_name())
else:
row.append('')
row.append("")
if o.customer:
row.append(o.customer.user.get_display_name())
else:
row.append('')
row = row + [o.label, o.quantity, o.quantity * o.unit_price,
o.get_payment_method_display()]
row.append("")
row = row + [
o.label,
o.quantity,
o.quantity * o.unit_price,
o.get_payment_method_display(),
]
if o.product:
row.append(o.product.selling_price)
row.append(o.product.purchase_price)
row.append(o.product.selling_price - o.product.purchase_price)
else:
row = row + ['', '', '']
row = row + ["", "", ""]
writer.writerow(row)
return response
@ -418,10 +531,11 @@ class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
"""
Edit a Club's main informations (for the club's members)
"""
model = Club
pk_url_kwarg = "club_id"
form_class = ClubEditForm
template_name = 'core/edit.jinja'
template_name = "core/edit.jinja"
current_tab = "edit"
@ -429,10 +543,11 @@ class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
"""
Edit the properties of a Club object (for the Sith admins)
"""
model = Club
pk_url_kwarg = "club_id"
fields = ['name', 'unix_name', 'parent', 'is_active']
template_name = 'core/edit.jinja'
fields = ["name", "unix_name", "parent", "is_active"]
template_name = "core/edit.jinja"
current_tab = "props"
@ -440,16 +555,18 @@ class ClubCreateView(CanEditPropMixin, CreateView):
"""
Create a club (for the Sith admin)
"""
model = Club
pk_url_kwarg = "club_id"
fields = ['name', 'unix_name', 'parent']
template_name = 'core/edit.jinja'
fields = ["name", "unix_name", "parent"]
template_name = "core/edit.jinja"
class MembershipSetOldView(CanEditMixin, DetailView):
"""
Set a membership as beeing old
"""
model = Membership
pk_url_kwarg = "membership_id"
@ -457,11 +574,23 @@ class MembershipSetOldView(CanEditMixin, DetailView):
self.object = self.get_object()
self.object.end_date = timezone.now()
self.object.save()
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},
)
)
def post(self, request, *args, **kwargs):
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):
@ -469,7 +598,7 @@ class ClubStatView(TemplateView):
def get_context_data(self, **kwargs):
kwargs = super(ClubStatView, self).get_context_data(**kwargs)
kwargs['club_list'] = Club.objects.all()
kwargs["club_list"] = Club.objects.all()
return kwargs
@ -477,16 +606,21 @@ class ClubMailingView(ClubTabsMixin, ListView):
"""
A list of mailing for a given club
"""
action = None
model = Mailing
template_name = "club/mailing.jinja"
current_tab = 'mailing'
current_tab = "mailing"
def authorized(self):
return self.club.has_rights_in_club(self.user) or self.user.is_root or self.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
return (
self.club.has_rights_in_club(self.user)
or self.user.is_root
or self.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
)
def dispatch(self, request, *args, **kwargs):
self.club = get_object_or_404(Club, pk=kwargs['club_id'])
self.club = get_object_or_404(Club, pk=kwargs["club_id"])
self.user = request.user
self.object = self.club
if not self.authorized():
@ -504,7 +638,9 @@ class ClubMailingView(ClubTabsMixin, ListView):
elif self.action == "add_member":
form = MailingSubscriptionForm
model = MailingSubscription
return MailingGenericCreateView.as_view(model=model, list_view=self, form_class=form)(request, *args, **kwargs)
return MailingGenericCreateView.as_view(
model=model, list_view=self, form_class=form
)(request, *args, **kwargs)
return res
def get_queryset(self):
@ -512,11 +648,11 @@ class ClubMailingView(ClubTabsMixin, ListView):
def get_context_data(self, **kwargs):
kwargs = super(ClubMailingView, self).get_context_data(**kwargs)
kwargs['add_member'] = self.member_form
kwargs['add_mailing'] = self.mailing_form
kwargs['club'] = self.club
kwargs['user'] = self.user
kwargs['has_objects'] = len(kwargs['object_list']) > 0
kwargs["add_member"] = self.member_form
kwargs["add_mailing"] = self.mailing_form
kwargs["club"] = self.club
kwargs["user"] = self.user
kwargs["has_objects"] = len(kwargs["object_list"]) > 0
return kwargs
def get_object(self):
@ -527,20 +663,23 @@ class MailingGenericCreateView(CreateView, SingleObjectMixin):
"""
Create a new mailing list
"""
list_view = None
form_class = None
def get_context_data(self, **kwargs):
view_kwargs = self.list_view.get_context_data(**kwargs)
for key, data in super(MailingGenericCreateView, self).get_context_data(**kwargs).items():
for key, data in (
super(MailingGenericCreateView, self).get_context_data(**kwargs).items()
):
view_kwargs[key] = data
view_kwargs[self.list_view.action] = view_kwargs['form']
view_kwargs[self.list_view.action] = view_kwargs["form"]
return view_kwargs
def get_form_kwargs(self):
kwargs = super(MailingGenericCreateView, self).get_form_kwargs()
kwargs['club_id'] = self.list_view.club.id
kwargs['user_id'] = self.list_view.user.id
kwargs["club_id"] = self.list_view.club.id
kwargs["user_id"] = self.list_view.user.id
return kwargs
def dispatch(self, request, *args, **kwargs):
@ -550,13 +689,13 @@ class MailingGenericCreateView(CreateView, SingleObjectMixin):
return super(MailingGenericCreateView, self).dispatch(request, *args, **kwargs)
def get_success_url(self, **kwargs):
return reverse_lazy('club:mailing', kwargs={'club_id': self.list_view.club.id})
return reverse_lazy("club:mailing", kwargs={"club_id": self.list_view.club.id})
class MailingDeleteView(CanEditMixin, DeleteView):
model = Mailing
template_name = 'core/delete_confirm.jinja'
template_name = "core/delete_confirm.jinja"
pk_url_kwarg = "mailing_id"
redirect_page = None
@ -568,27 +707,28 @@ class MailingDeleteView(CanEditMixin, DeleteView):
if self.redirect_page:
return reverse_lazy(self.redirect_page)
else:
return reverse_lazy('club:mailing', kwargs={'club_id': self.club_id})
return reverse_lazy("club:mailing", kwargs={"club_id": self.club_id})
class MailingSubscriptionDeleteView(CanEditMixin, DeleteView):
model = MailingSubscription
template_name = 'core/delete_confirm.jinja'
template_name = "core/delete_confirm.jinja"
pk_url_kwarg = "mailing_subscription_id"
def dispatch(self, request, *args, **kwargs):
self.club_id = self.get_object().mailing.club.id
return super(MailingSubscriptionDeleteView, self).dispatch(request, *args, **kwargs)
return super(MailingSubscriptionDeleteView, self).dispatch(
request, *args, **kwargs
)
def get_success_url(self, **kwargs):
return reverse_lazy('club:mailing', kwargs={'club_id': self.club_id})
return reverse_lazy("club:mailing", kwargs={"club_id": self.club_id})
class MailingAutoGenerationView(View):
def dispatch(self, request, *args, **kwargs):
self.mailing = get_object_or_404(Mailing, pk=kwargs['mailing_id'])
self.mailing = get_object_or_404(Mailing, pk=kwargs["mailing_id"])
if not request.user.can_edit(self.mailing):
raise PermissionDenied
return super(MailingAutoGenerationView, self).dispatch(request, *args, **kwargs)
@ -596,23 +736,24 @@ class MailingAutoGenerationView(View):
def get(self, request, *args, **kwargs):
club = self.mailing.club
self.mailing.subscriptions.all().delete()
members = club.members.filter(role__gte=settings.SITH_CLUB_ROLES_ID['Board member']).exclude(end_date__lte=timezone.now())
members = club.members.filter(
role__gte=settings.SITH_CLUB_ROLES_ID["Board member"]
).exclude(end_date__lte=timezone.now())
for member in members.all():
MailingSubscription(user=member.user, mailing=self.mailing).save()
return redirect('club:mailing', club_id=club.id)
return redirect("club:mailing", club_id=club.id)
class MailingAutoCleanView(View):
def dispatch(self, request, *args, **kwargs):
self.mailing = get_object_or_404(Mailing, pk=kwargs['mailing_id'])
self.mailing = get_object_or_404(Mailing, pk=kwargs["mailing_id"])
if not request.user.can_edit(self.mailing):
raise PermissionDenied
return super(MailingAutoCleanView, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
self.mailing.subscriptions.all().delete()
return redirect('club:mailing', club_id=self.mailing.club.id)
return redirect("club:mailing", club_id=self.mailing.club.id)
class PosterListView(ClubTabsMixin, PosterListBaseView, CanViewMixin):
@ -623,8 +764,8 @@ class PosterListView(ClubTabsMixin, PosterListBaseView, CanViewMixin):
def get_context_data(self, **kwargs):
kwargs = super(PosterListView, self).get_context_data(**kwargs)
kwargs['app'] = "club"
kwargs['club'] = self.club
kwargs["app"] = "club"
kwargs["club"] = self.club
return kwargs
@ -640,18 +781,18 @@ class PosterCreateView(PosterCreateBaseView, CanCreateMixin):
return obj
def get_success_url(self, **kwargs):
return reverse_lazy('club:poster_list', kwargs={'club_id': self.club.id})
return reverse_lazy("club:poster_list", kwargs={"club_id": self.club.id})
class PosterEditView(ClubTabsMixin, PosterEditBaseView, CanEditMixin):
"""Edit communication poster"""
def get_success_url(self):
return reverse_lazy('club:poster_list', kwargs={'club_id': self.club.id})
return reverse_lazy("club:poster_list", kwargs={"club_id": self.club.id})
def get_context_data(self, **kwargs):
kwargs = super(PosterEditView, self).get_context_data(**kwargs)
kwargs['app'] = "club"
kwargs["app"] = "club"
return kwargs
@ -659,5 +800,4 @@ class PosterDeleteView(PosterDeleteBaseView, ClubTabsMixin, CanEditMixin):
"""Delete communication poster"""
def get_success_url(self):
return reverse_lazy('club:poster_list', kwargs={'club_id': self.club.id})
return reverse_lazy("club:poster_list", kwargs={"club_id": self.club.id})

View File

@ -21,4 +21,3 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -41,4 +41,3 @@ admin.site.register(News, NewsAdmin)
admin.site.register(Weekmail, WeekmailAdmin)
admin.site.register(Screen)
admin.site.register(Poster)

View File

@ -6,17 +6,37 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Sith',
name="Sith",
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
('alert_msg', models.TextField(default='', verbose_name='alert message', blank=True)),
('info_msg', models.TextField(default='', verbose_name='info message', blank=True)),
('index_page', models.TextField(default='', verbose_name='index page', blank=True)),
],
(
"id",
models.AutoField(
verbose_name="ID",
auto_created=True,
serialize=False,
primary_key=True,
),
),
(
"alert_msg",
models.TextField(
default="", verbose_name="alert message", blank=True
),
),
(
"info_msg",
models.TextField(
default="", verbose_name="info message", blank=True
),
),
(
"index_page",
models.TextField(default="", verbose_name="index page", blank=True),
),
],
)
]

View File

@ -8,33 +8,100 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('club', '0005_auto_20161120_1149'),
("club", "0005_auto_20161120_1149"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('com', '0001_initial'),
("com", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='News',
name="News",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
('title', models.CharField(max_length=64, verbose_name='title')),
('summary', models.TextField(verbose_name='summary')),
('content', models.TextField(verbose_name='content')),
('type', models.CharField(choices=[('NOTICE', 'Notice'), ('EVENT', 'Event'), ('WEEKLY', 'Weekly'), ('CALL', 'Call')], default='EVENT', max_length=16, verbose_name='type')),
('is_moderated', models.BooleanField(default=False, verbose_name='is moderated')),
('author', models.ForeignKey(related_name='owned_news', to=settings.AUTH_USER_MODEL, verbose_name='author')),
('club', models.ForeignKey(related_name='news', to='club.Club', verbose_name='club')),
('moderator', models.ForeignKey(related_name='moderated_news', null=True, to=settings.AUTH_USER_MODEL, verbose_name='moderator')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
auto_created=True,
verbose_name="ID",
),
),
("title", models.CharField(max_length=64, verbose_name="title")),
("summary", models.TextField(verbose_name="summary")),
("content", models.TextField(verbose_name="content")),
(
"type",
models.CharField(
choices=[
("NOTICE", "Notice"),
("EVENT", "Event"),
("WEEKLY", "Weekly"),
("CALL", "Call"),
],
default="EVENT",
max_length=16,
verbose_name="type",
),
),
(
"is_moderated",
models.BooleanField(default=False, verbose_name="is moderated"),
),
(
"author",
models.ForeignKey(
related_name="owned_news",
to=settings.AUTH_USER_MODEL,
verbose_name="author",
),
),
(
"club",
models.ForeignKey(
related_name="news", to="club.Club", verbose_name="club"
),
),
(
"moderator",
models.ForeignKey(
related_name="moderated_news",
null=True,
to=settings.AUTH_USER_MODEL,
verbose_name="moderator",
),
),
],
),
migrations.CreateModel(
name='NewsDate',
name="NewsDate",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
('start_date', models.DateTimeField(null=True, blank=True, verbose_name='start_date')),
('end_date', models.DateTimeField(null=True, blank=True, verbose_name='end_date')),
('news', models.ForeignKey(related_name='dates', to='com.News', verbose_name='news_date')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
auto_created=True,
verbose_name="ID",
),
),
(
"start_date",
models.DateTimeField(
null=True, blank=True, verbose_name="start_date"
),
),
(
"end_date",
models.DateTimeField(
null=True, blank=True, verbose_name="end_date"
),
),
(
"news",
models.ForeignKey(
related_name="dates", to="com.News", verbose_name="news_date"
),
),
],
),
]

View File

@ -8,42 +8,81 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('club', '0006_auto_20161229_0040'),
("club", "0006_auto_20161229_0040"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('com', '0002_news_newsdate'),
("com", "0002_news_newsdate"),
]
operations = [
migrations.CreateModel(
name='Weekmail',
name="Weekmail",
fields=[
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
('title', models.CharField(max_length=64, verbose_name='title', blank=True)),
('intro', models.TextField(verbose_name='intro', blank=True)),
('joke', models.TextField(verbose_name='joke', blank=True)),
('protip', models.TextField(verbose_name='protip', blank=True)),
('conclusion', models.TextField(verbose_name='conclusion', blank=True)),
('sent', models.BooleanField(verbose_name='sent', default=False)),
(
"id",
models.AutoField(
serialize=False,
primary_key=True,
verbose_name="ID",
auto_created=True,
),
),
(
"title",
models.CharField(max_length=64, verbose_name="title", blank=True),
),
("intro", models.TextField(verbose_name="intro", blank=True)),
("joke", models.TextField(verbose_name="joke", blank=True)),
("protip", models.TextField(verbose_name="protip", blank=True)),
("conclusion", models.TextField(verbose_name="conclusion", blank=True)),
("sent", models.BooleanField(verbose_name="sent", default=False)),
],
options={
'ordering': ['-id'],
},
options={"ordering": ["-id"]},
),
migrations.CreateModel(
name='WeekmailArticle',
name="WeekmailArticle",
fields=[
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
('title', models.CharField(max_length=64, verbose_name='title')),
('content', models.TextField(verbose_name='content')),
('rank', models.IntegerField(verbose_name='rank', default=-1)),
('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='author', related_name='owned_weekmail_articles')),
('club', models.ForeignKey(to='club.Club', verbose_name='club', related_name='weekmail_articles')),
('weekmail', models.ForeignKey(to='com.Weekmail', verbose_name='weekmail', related_name='articles', null=True)),
(
"id",
models.AutoField(
serialize=False,
primary_key=True,
verbose_name="ID",
auto_created=True,
),
),
("title", models.CharField(max_length=64, verbose_name="title")),
("content", models.TextField(verbose_name="content")),
("rank", models.IntegerField(verbose_name="rank", default=-1)),
(
"author",
models.ForeignKey(
to=settings.AUTH_USER_MODEL,
verbose_name="author",
related_name="owned_weekmail_articles",
),
),
(
"club",
models.ForeignKey(
to="club.Club",
verbose_name="club",
related_name="weekmail_articles",
),
),
(
"weekmail",
models.ForeignKey(
to="com.Weekmail",
verbose_name="weekmail",
related_name="articles",
null=True,
),
),
],
),
migrations.AddField(
model_name='sith',
name='weekmail_destinations',
field=models.TextField(verbose_name='weekmail destinations', default=''),
model_name="sith",
name="weekmail_destinations",
field=models.TextField(verbose_name="weekmail destinations", default=""),
),
]

View File

@ -9,36 +9,78 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('club', '0010_auto_20170912_2028'),
("club", "0010_auto_20170912_2028"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('com', '0003_auto_20170115_2300'),
("com", "0003_auto_20170115_2300"),
]
operations = [
migrations.CreateModel(
name='Poster',
name="Poster",
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('name', models.CharField(verbose_name='name', max_length=128, default='')),
('file', models.ImageField(verbose_name='file', upload_to='com/posters')),
('date_begin', models.DateTimeField(default=django.utils.timezone.now)),
('date_end', models.DateTimeField(blank=True, null=True)),
('display_time', models.IntegerField(verbose_name='display time', default=30)),
('is_moderated', models.BooleanField(verbose_name='is moderated', default=False)),
('club', models.ForeignKey(verbose_name='club', related_name='posters', to='club.Club')),
('moderator', models.ForeignKey(verbose_name='moderator', blank=True, null=True, related_name='moderated_posters', to=settings.AUTH_USER_MODEL)),
(
"id",
models.AutoField(
verbose_name="ID",
primary_key=True,
serialize=False,
auto_created=True,
),
),
(
"name",
models.CharField(verbose_name="name", max_length=128, default=""),
),
(
"file",
models.ImageField(verbose_name="file", upload_to="com/posters"),
),
("date_begin", models.DateTimeField(default=django.utils.timezone.now)),
("date_end", models.DateTimeField(blank=True, null=True)),
(
"display_time",
models.IntegerField(verbose_name="display time", default=30),
),
(
"is_moderated",
models.BooleanField(verbose_name="is moderated", default=False),
),
(
"club",
models.ForeignKey(
verbose_name="club", related_name="posters", to="club.Club"
),
),
(
"moderator",
models.ForeignKey(
verbose_name="moderator",
blank=True,
null=True,
related_name="moderated_posters",
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel(
name='Screen',
name="Screen",
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('name', models.CharField(verbose_name='name', max_length=128)),
(
"id",
models.AutoField(
verbose_name="ID",
primary_key=True,
serialize=False,
auto_created=True,
),
),
("name", models.CharField(verbose_name="name", max_length=128)),
],
),
migrations.AddField(
model_name='poster',
name='screens',
field=models.ManyToManyField(related_name='posters', to='com.Screen'),
model_name="poster",
name="screens",
field=models.ManyToManyField(related_name="posters", to="com.Screen"),
),
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('com', '0004_auto_20171221_1614'),
]
dependencies = [("com", "0004_auto_20171221_1614")]
operations = [
migrations.AlterField(
model_name='poster',
name='display_time',
field=models.IntegerField(verbose_name='display time', default=15),
),
model_name="poster",
name="display_time",
field=models.IntegerField(verbose_name="display time", default=15),
)
]

View File

@ -40,10 +40,9 @@ from core.models import User, Preferences, RealGroup, Notification, SithFile
from club.models import Club
class Sith(models.Model):
"""A one instance class storing all the modifiable infos"""
alert_msg = models.TextField(_("alert message"), default="", blank=True)
info_msg = models.TextField(_("info message"), default="", blank=True)
index_page = models.TextField(_("index page"), default="", blank=True)
@ -57,23 +56,30 @@ class Sith(models.Model):
NEWS_TYPES = [
('NOTICE', _('Notice')),
('EVENT', _('Event')),
('WEEKLY', _('Weekly')),
('CALL', _('Call')),
("NOTICE", _("Notice")),
("EVENT", _("Event")),
("WEEKLY", _("Weekly")),
("CALL", _("Call")),
]
class News(models.Model):
"""The news class"""
title = models.CharField(_("title"), max_length=64)
summary = models.TextField(_("summary"))
content = models.TextField(_("content"))
type = models.CharField(_("type"), max_length=16, choices=NEWS_TYPES, default="EVENT")
type = models.CharField(
_("type"), max_length=16, choices=NEWS_TYPES, default="EVENT"
)
club = models.ForeignKey(Club, related_name="news", verbose_name=_("club"))
author = models.ForeignKey(User, related_name="owned_news", verbose_name=_("author"))
author = models.ForeignKey(
User, related_name="owned_news", verbose_name=_("author")
)
is_moderated = models.BooleanField(_("is moderated"), default=False)
moderator = models.ForeignKey(User, related_name="moderated_news", verbose_name=_("moderator"), null=True)
moderator = models.ForeignKey(
User, related_name="moderated_news", verbose_name=_("moderator"), null=True
)
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or user == self.author
@ -85,7 +91,7 @@ class News(models.Model):
return self.is_moderated or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def get_absolute_url(self):
return reverse('com:news_detail', kwargs={'news_id': self.id})
return reverse("com:news_detail", kwargs={"news_id": self.id})
def get_full_url(self):
return "https://%s%s" % (settings.SITH_URL, self.get_absolute_url())
@ -95,15 +101,28 @@ class News(models.Model):
def save(self, *args, **kwargs):
super(News, self).save(*args, **kwargs)
for u in RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first().users.all():
Notification(user=u, url=reverse("com:news_admin_list"),
type="NEWS_MODERATION", param="1").save()
for u in (
RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID)
.first()
.users.all()
):
Notification(
user=u,
url=reverse("com:news_admin_list"),
type="NEWS_MODERATION",
param="1",
).save()
def news_notification_callback(notif):
count = News.objects.filter(
Q(dates__start_date__gt=timezone.now(), is_moderated=False) |
Q(type="NOTICE", is_moderated=False)
).distinct().count()
count = (
News.objects.filter(
Q(dates__start_date__gt=timezone.now(), is_moderated=False)
| Q(type="NOTICE", is_moderated=False)
)
.distinct()
.count()
)
if count:
notif.viewed = False
notif.param = "%s" % count
@ -111,6 +130,7 @@ def news_notification_callback(notif):
else:
notif.viewed = True
class NewsDate(models.Model):
"""
A date class, useful for weekly events, or for events that just have no date
@ -118,9 +138,10 @@ class NewsDate(models.Model):
This class allows more flexibilty managing the dates related to a news, particularly when this news is weekly, since
we don't have to make copies
"""
news = models.ForeignKey(News, related_name="dates", verbose_name=_("news_date"))
start_date = models.DateTimeField(_('start_date'), null=True, blank=True)
end_date = models.DateTimeField(_('end_date'), null=True, blank=True)
start_date = models.DateTimeField(_("start_date"), null=True, blank=True)
end_date = models.DateTimeField(_("end_date"), null=True, blank=True)
def __str__(self):
return "%s: %s - %s" % (self.news.title, self.start_date, self.end_date)
@ -130,6 +151,7 @@ class Weekmail(models.Model):
"""
The weekmail class
"""
title = models.CharField(_("title"), max_length=64, blank=True)
intro = models.TextField(_("intro"), blank=True)
joke = models.TextField(_("joke"), blank=True)
@ -138,16 +160,21 @@ class Weekmail(models.Model):
sent = models.BooleanField(_("sent"), default=False)
class Meta:
ordering = ['-id']
ordering = ["-id"]
def send(self):
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():
email = EmailMultiAlternatives(
subject=self.title,
body=self.render_text(),
from_email=settings.SITH_COM_EMAIL,
to=Sith.objects.first().weekmail_destinations.split(' '),
to=Sith.objects.first().weekmail_destinations.split(" "),
bcc=dest,
)
email.attach_alternative(self.render_html(), "text/html")
@ -157,14 +184,14 @@ class Weekmail(models.Model):
Weekmail().save()
def render_text(self):
return render(None, "com/weekmail_renderer_text.jinja", context={
'weekmail': self,
}).content.decode('utf-8')
return render(
None, "com/weekmail_renderer_text.jinja", context={"weekmail": self}
).content.decode("utf-8")
def render_html(self):
return render(None, "com/weekmail_renderer_html.jinja", context={
'weekmail': self,
}).content.decode('utf-8')
return render(
None, "com/weekmail_renderer_html.jinja", context={"weekmail": self}
).content.decode("utf-8")
def get_banner(self):
return "http://" + settings.SITH_URL + static("com/img/weekmail_bannerA18.jpg")
@ -180,12 +207,18 @@ class Weekmail(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)
content = models.TextField(_("content"))
author = models.ForeignKey(User, related_name="owned_weekmail_articles", verbose_name=_("author"))
club = models.ForeignKey(Club, related_name="weekmail_articles", verbose_name=_("club"))
rank = models.IntegerField(_('rank'), default=-1)
author = models.ForeignKey(
User, related_name="owned_weekmail_articles", verbose_name=_("author")
)
club = models.ForeignKey(
Club, related_name="weekmail_articles", verbose_name=_("club")
)
rank = models.IntegerField(_("rank"), default=-1)
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
@ -199,7 +232,9 @@ class Screen(models.Model):
def active_posters(self):
now = timezone.now()
return self.posters.filter(is_moderated=True, date_begin__lte=now).filter(Q(date_end__isnull=True) | Q(date_end__gte=now))
return self.posters.filter(is_moderated=True, date_begin__lte=now).filter(
Q(date_end__isnull=True) | Q(date_end__gte=now)
)
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
@ -209,21 +244,40 @@ class Screen(models.Model):
class Poster(models.Model):
name = models.CharField(_("name"), blank=False, null=False, max_length=128, default="")
name = models.CharField(
_("name"), blank=False, null=False, max_length=128, default=""
)
file = models.ImageField(_("file"), null=False, upload_to="com/posters")
club = models.ForeignKey(Club, related_name="posters", verbose_name=_("club"), null=False)
club = models.ForeignKey(
Club, related_name="posters", verbose_name=_("club"), null=False
)
screens = models.ManyToManyField(Screen, related_name="posters")
date_begin = models.DateTimeField(blank=False, null=False, default=timezone.now)
date_end = models.DateTimeField(blank=True, null=True)
display_time = models.IntegerField(_("display time"), blank=False, null=False, default=15)
display_time = models.IntegerField(
_("display time"), blank=False, null=False, default=15
)
is_moderated = models.BooleanField(_("is moderated"), default=False)
moderator = models.ForeignKey(User, related_name="moderated_posters", verbose_name=_("moderator"), null=True, blank=True)
moderator = models.ForeignKey(
User,
related_name="moderated_posters",
verbose_name=_("moderator"),
null=True,
blank=True,
)
def save(self, *args, **kwargs):
if not self.is_moderated:
for u in RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first().users.all():
Notification(user=u, url=reverse("com:poster_moderate_list"),
type="POSTER_MODERATION").save()
for u in (
RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID)
.first()
.users.all()
):
Notification(
user=u,
url=reverse("com:poster_moderate_list"),
type="POSTER_MODERATION",
).save()
return super(Poster, self).save(*args, **kwargs)
def clean(self, *args, **kwargs):
@ -231,7 +285,9 @@ class Poster(models.Model):
raise ValidationError(_("Begin date should be before end date"))
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or Club.objects.filter(id__in=user.clubs_with_rights)
return user.is_in_group(
settings.SITH_GROUP_COM_ADMIN_ID
) or Club.objects.filter(id__in=user.clubs_with_rights)
def can_be_moderated_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)

View File

@ -34,25 +34,43 @@ class ComTest(TestCase):
def setUp(self):
call_command("populate")
self.skia = User.objects.filter(username="skia").first()
self.com_group = RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first()
self.com_group = RealGroup.objects.filter(
id=settings.SITH_GROUP_COM_ADMIN_ID
).first()
self.skia.groups = [self.com_group]
self.skia.save()
self.client.login(username=self.skia.username, password='plop')
self.client.login(username=self.skia.username, password="plop")
def test_alert_msg(self):
response = self.client.post(reverse("com:alert_edit"), {"alert_msg": """
response = self.client.post(
reverse("com:alert_edit"),
{
"alert_msg": """
### ALERTE!
**Caaaataaaapuuuulte!!!!**
"""})
"""
},
)
r = self.client.get(reverse("core:index"))
self.assertTrue(r.status_code == 200)
self.assertTrue("""<div id="alert_box">\\n <div class="markdown"><h3>ALERTE!</h3>\\n<p><strong>Caaaataaaapuuuulte!!!!</strong></p>""" in str(r.content))
self.assertTrue(
"""<div id="alert_box">\\n <div class="markdown"><h3>ALERTE!</h3>\\n<p><strong>Caaaataaaapuuuulte!!!!</strong></p>"""
in str(r.content)
)
def test_info_msg(self):
response = self.client.post(reverse("com:info_edit"), {"info_msg": """
response = self.client.post(
reverse("com:info_edit"),
{
"info_msg": """
### INFO: **Caaaataaaapuuuulte!!!!**
"""})
"""
},
)
r = self.client.get(reverse("core:index"))
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)
)

View File

@ -28,35 +28,94 @@ from com.views import *
from club.views import MailingDeleteView
urlpatterns = [
url(r'^sith/edit/alert$', AlertMsgEditView.as_view(), name='alert_edit'),
url(r'^sith/edit/info$', InfoMsgEditView.as_view(), name='info_edit'),
url(r'^sith/edit/index$', IndexEditView.as_view(), name='index_edit'),
url(r'^sith/edit/weekmail_destinations$', WeekmailDestinationEditView.as_view(), name='weekmail_destinations'),
url(r'^weekmail$', WeekmailEditView.as_view(), name='weekmail'),
url(r'^weekmail/preview$', WeekmailPreviewView.as_view(), name='weekmail_preview'),
url(r'^weekmail/new_article$', WeekmailArticleCreateView.as_view(), name='weekmail_article'),
url(r'^weekmail/article/(?P<article_id>[0-9]+)/delete$', WeekmailArticleDeleteView.as_view(), name='weekmail_article_delete'),
url(r'^weekmail/article/(?P<article_id>[0-9]+)/edit$', WeekmailArticleEditView.as_view(), name='weekmail_article_edit'),
url(r'^news$', NewsListView.as_view(), name='news_list'),
url(r'^news/admin$', NewsAdminListView.as_view(), name='news_admin_list'),
url(r'^news/create$', NewsCreateView.as_view(), name='news_new'),
url(r'^news/(?P<news_id>[0-9]+)/delete$', NewsDeleteView.as_view(), name='news_delete'),
url(r'^news/(?P<news_id>[0-9]+)/moderate$', NewsModerateView.as_view(), name='news_moderate'),
url(r'^news/(?P<news_id>[0-9]+)/edit$', NewsEditView.as_view(), name='news_edit'),
url(r'^news/(?P<news_id>[0-9]+)$', NewsDetailView.as_view(), name='news_detail'),
url(r'^mailings$', MailingListAdminView.as_view(), name='mailing_admin'),
url(r'^mailings/(?P<mailing_id>[0-9]+)/moderate$', MailingModerateView.as_view(), name='mailing_moderate'),
url(r'^mailings/(?P<mailing_id>[0-9]+)/delete$', MailingDeleteView.as_view(redirect_page='com:mailing_admin'), name='mailing_delete'),
url(r'^poster$', PosterListView.as_view(), name='poster_list'),
url(r'^poster/create$', PosterCreateView.as_view(), name='poster_create'),
url(r'^poster/(?P<poster_id>[0-9]+)/edit$', PosterEditView.as_view(), name='poster_edit'),
url(r'^poster/(?P<poster_id>[0-9]+)/delete$', PosterDeleteView.as_view(), name='poster_delete'),
url(r'^poster/moderate$', PosterModerateListView.as_view(), name='poster_moderate_list'),
url(r'^poster/(?P<object_id>[0-9]+)/moderate$', PosterModerateView.as_view(), name='poster_moderate'),
url(r'^screen$', ScreenListView.as_view(), name='screen_list'),
url(r'^screen/create$', ScreenCreateView.as_view(), name='screen_create'),
url(r'^screen/(?P<screen_id>[0-9]+)/slideshow$', ScreenSlideshowView.as_view(), name='screen_slideshow'),
url(r'^screen/(?P<screen_id>[0-9]+)/edit$', ScreenEditView.as_view(), name='screen_edit'),
url(r'^screen/(?P<screen_id>[0-9]+)/delete$', ScreenDeleteView.as_view(), name='screen_delete'),
url(r"^sith/edit/alert$", AlertMsgEditView.as_view(), name="alert_edit"),
url(r"^sith/edit/info$", InfoMsgEditView.as_view(), name="info_edit"),
url(r"^sith/edit/index$", IndexEditView.as_view(), name="index_edit"),
url(
r"^sith/edit/weekmail_destinations$",
WeekmailDestinationEditView.as_view(),
name="weekmail_destinations",
),
url(r"^weekmail$", WeekmailEditView.as_view(), name="weekmail"),
url(r"^weekmail/preview$", WeekmailPreviewView.as_view(), name="weekmail_preview"),
url(
r"^weekmail/new_article$",
WeekmailArticleCreateView.as_view(),
name="weekmail_article",
),
url(
r"^weekmail/article/(?P<article_id>[0-9]+)/delete$",
WeekmailArticleDeleteView.as_view(),
name="weekmail_article_delete",
),
url(
r"^weekmail/article/(?P<article_id>[0-9]+)/edit$",
WeekmailArticleEditView.as_view(),
name="weekmail_article_edit",
),
url(r"^news$", NewsListView.as_view(), name="news_list"),
url(r"^news/admin$", NewsAdminListView.as_view(), name="news_admin_list"),
url(r"^news/create$", NewsCreateView.as_view(), name="news_new"),
url(
r"^news/(?P<news_id>[0-9]+)/delete$",
NewsDeleteView.as_view(),
name="news_delete",
),
url(
r"^news/(?P<news_id>[0-9]+)/moderate$",
NewsModerateView.as_view(),
name="news_moderate",
),
url(r"^news/(?P<news_id>[0-9]+)/edit$", NewsEditView.as_view(), name="news_edit"),
url(r"^news/(?P<news_id>[0-9]+)$", NewsDetailView.as_view(), name="news_detail"),
url(r"^mailings$", MailingListAdminView.as_view(), name="mailing_admin"),
url(
r"^mailings/(?P<mailing_id>[0-9]+)/moderate$",
MailingModerateView.as_view(),
name="mailing_moderate",
),
url(
r"^mailings/(?P<mailing_id>[0-9]+)/delete$",
MailingDeleteView.as_view(redirect_page="com:mailing_admin"),
name="mailing_delete",
),
url(r"^poster$", PosterListView.as_view(), name="poster_list"),
url(r"^poster/create$", PosterCreateView.as_view(), name="poster_create"),
url(
r"^poster/(?P<poster_id>[0-9]+)/edit$",
PosterEditView.as_view(),
name="poster_edit",
),
url(
r"^poster/(?P<poster_id>[0-9]+)/delete$",
PosterDeleteView.as_view(),
name="poster_delete",
),
url(
r"^poster/moderate$",
PosterModerateListView.as_view(),
name="poster_moderate_list",
),
url(
r"^poster/(?P<object_id>[0-9]+)/moderate$",
PosterModerateView.as_view(),
name="poster_moderate",
),
url(r"^screen$", ScreenListView.as_view(), name="screen_list"),
url(r"^screen/create$", ScreenCreateView.as_view(), name="screen_create"),
url(
r"^screen/(?P<screen_id>[0-9]+)/slideshow$",
ScreenSlideshowView.as_view(),
name="screen_slideshow",
),
url(
r"^screen/(?P<screen_id>[0-9]+)/edit$",
ScreenEditView.as_view(),
name="screen_edit",
),
url(
r"^screen/(?P<screen_id>[0-9]+)/delete$",
ScreenDeleteView.as_view(),
name="screen_delete",
),
]

View File

@ -41,7 +41,14 @@ from django import forms
from datetime import timedelta
from com.models import Sith, News, NewsDate, Weekmail, WeekmailArticle, Screen, Poster
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
from core.views import (
CanViewMixin,
CanEditMixin,
CanEditPropMixin,
TabedViewMixin,
CanCreateMixin,
QuickNotifMixin,
)
from core.views.forms import SelectDateTime
from core.models import Notification, RealGroup, User
from club.models import Club, Mailing
@ -55,23 +62,40 @@ sith = Sith.objects.first
class PosterForm(forms.ModelForm):
class Meta:
model = Poster
fields = ['name', 'file', 'club', 'screens', 'date_begin', 'date_end', 'display_time']
widgets = {
'screens': forms.CheckboxSelectMultiple,
}
fields = [
"name",
"file",
"club",
"screens",
"date_begin",
"date_end",
"display_time",
]
widgets = {"screens": forms.CheckboxSelectMultiple}
date_begin = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Start date"),
widget=SelectDateTime, required=True, initial=timezone.now().strftime("%Y-%m-%d %H:%M:%S"))
date_end = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"),
widget=SelectDateTime, required=False)
date_begin = forms.DateTimeField(
["%Y-%m-%d %H:%M:%S"],
label=_("Start date"),
widget=SelectDateTime,
required=True,
initial=timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
)
date_end = forms.DateTimeField(
["%Y-%m-%d %H:%M:%S"],
label=_("End date"),
widget=SelectDateTime,
required=False,
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
self.user = kwargs.pop("user", None)
super(PosterForm, self).__init__(*args, **kwargs)
if self.user:
if not self.user.is_com_admin:
self.fields['club'].queryset = Club.objects.filter(id__in=self.user.clubs_with_rights)
self.fields.pop('display_time')
self.fields["club"].queryset = Club.objects.filter(
id__in=self.user.clubs_with_rights
)
self.fields.pop("display_time")
class ComTabsMixin(TabedViewMixin):
@ -80,51 +104,54 @@ class ComTabsMixin(TabedViewMixin):
def get_list_of_tabs(self):
tab_list = []
tab_list.append({
'url': reverse('com:weekmail'),
'slug': 'weekmail',
'name': _("Weekmail"),
})
tab_list.append({
'url': reverse('com:weekmail_destinations'),
'slug': 'weekmail_destinations',
'name': _("Weekmail destinations"),
})
tab_list.append({
'url': reverse('com:index_edit'),
'slug': 'index',
'name': _("Index page"),
})
tab_list.append({
'url': reverse('com:info_edit'),
'slug': 'info',
'name': _("Info message"),
})
tab_list.append({
'url': reverse('com:alert_edit'),
'slug': 'alert',
'name': _("Alert message"),
})
tab_list.append({
'url': reverse('com:mailing_admin'),
'slug': 'mailings',
'name': _("Mailing lists administration"),
})
tab_list.append({
'url': reverse('com:poster_list'),
'slug': 'posters',
'name': _("Posters list"),
})
tab_list.append({
'url': reverse('com:screen_list'),
'slug': 'screens',
'name': _("Screens list"),
})
tab_list.append(
{"url": reverse("com:weekmail"), "slug": "weekmail", "name": _("Weekmail")}
)
tab_list.append(
{
"url": reverse("com:weekmail_destinations"),
"slug": "weekmail_destinations",
"name": _("Weekmail destinations"),
}
)
tab_list.append(
{"url": reverse("com:index_edit"), "slug": "index", "name": _("Index page")}
)
tab_list.append(
{"url": reverse("com:info_edit"), "slug": "info", "name": _("Info message")}
)
tab_list.append(
{
"url": reverse("com:alert_edit"),
"slug": "alert",
"name": _("Alert message"),
}
)
tab_list.append(
{
"url": reverse("com:mailing_admin"),
"slug": "mailings",
"name": _("Mailing lists administration"),
}
)
tab_list.append(
{
"url": reverse("com:poster_list"),
"slug": "posters",
"name": _("Posters list"),
}
)
tab_list.append(
{
"url": reverse("com:screen_list"),
"slug": "screens",
"name": _("Screens list"),
}
)
return tab_list
class IsComAdminMixin(View):
def dispatch(self, request, *args, **kwargs):
if not (request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)):
raise PermissionDenied
@ -133,34 +160,35 @@ class IsComAdminMixin(View):
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
model = Sith
template_name = 'core/edit.jinja'
template_name = "core/edit.jinja"
def get_object(self, queryset=None):
return Sith.objects.first()
class AlertMsgEditView(ComEditView):
fields = ['alert_msg']
fields = ["alert_msg"]
current_tab = "alert"
success_url = reverse_lazy('com:alert_edit')
success_url = reverse_lazy("com:alert_edit")
class InfoMsgEditView(ComEditView):
fields = ['info_msg']
fields = ["info_msg"]
current_tab = "info"
success_url = reverse_lazy('com:info_edit')
success_url = reverse_lazy("com:info_edit")
class IndexEditView(ComEditView):
fields = ['index_page']
fields = ["index_page"]
current_tab = "index"
success_url = reverse_lazy('com:index_edit')
success_url = reverse_lazy("com:index_edit")
class WeekmailDestinationEditView(ComEditView):
fields = ['weekmail_destinations']
fields = ["weekmail_destinations"]
current_tab = "weekmail_destinations"
success_url = reverse_lazy('com:weekmail_destinations')
success_url = reverse_lazy("com:weekmail_destinations")
# News
@ -168,43 +196,64 @@ class WeekmailDestinationEditView(ComEditView):
class NewsForm(forms.ModelForm):
class Meta:
model = News
fields = ['type', 'title', 'club', 'summary', 'content', 'author']
widgets = {
'author': forms.HiddenInput,
'type': forms.RadioSelect,
}
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)
until = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Until"), widget=SelectDateTime, required=False)
fields = ["type", "title", "club", "summary", "content", "author"]
widgets = {"author": forms.HiddenInput, "type": forms.RadioSelect}
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,
)
until = forms.DateTimeField(
["%Y-%m-%d %H:%M:%S"], label=_("Until"), widget=SelectDateTime, required=False
)
automoderation = forms.BooleanField(label=_("Automoderation"), required=False)
def clean(self):
self.cleaned_data = super(NewsForm, self).clean()
if self.cleaned_data['type'] != "NOTICE":
if not self.cleaned_data['start_date']:
self.add_error('start_date', ValidationError(_("This field is required.")))
if not self.cleaned_data['end_date']:
self.add_error('end_date', ValidationError(_("This field is required.")))
if self.cleaned_data['start_date'] > self.cleaned_data['end_date']:
self.add_error('end_date', ValidationError(_("You crazy? You can not finish an event before starting it.")))
if self.cleaned_data['type'] == "WEEKLY" and not self.cleaned_data['until']:
self.add_error('until', ValidationError(_("This field is required.")))
if self.cleaned_data["type"] != "NOTICE":
if not self.cleaned_data["start_date"]:
self.add_error(
"start_date", ValidationError(_("This field is required."))
)
if not self.cleaned_data["end_date"]:
self.add_error(
"end_date", ValidationError(_("This field is required."))
)
if self.cleaned_data["start_date"] > self.cleaned_data["end_date"]:
self.add_error(
"end_date",
ValidationError(
_("You crazy? You can not finish an event before starting it.")
),
)
if self.cleaned_data["type"] == "WEEKLY" and not self.cleaned_data["until"]:
self.add_error("until", ValidationError(_("This field is required.")))
return self.cleaned_data
def save(self):
ret = super(NewsForm, self).save()
self.instance.dates.all().delete()
if self.instance.type == "EVENT" or self.instance.type == "CALL":
NewsDate(start_date=self.cleaned_data['start_date'],
end_date=self.cleaned_data['end_date'],
news=self.instance).save()
NewsDate(
start_date=self.cleaned_data["start_date"],
end_date=self.cleaned_data["end_date"],
news=self.instance,
).save()
elif self.instance.type == "WEEKLY":
start_date = self.cleaned_data['start_date']
end_date = self.cleaned_data['end_date']
while start_date <= self.cleaned_data['until']:
NewsDate(start_date=start_date,
end_date=end_date,
news=self.instance).save()
start_date = self.cleaned_data["start_date"]
end_date = self.cleaned_data["end_date"]
while start_date <= self.cleaned_data["until"]:
NewsDate(
start_date=start_date, end_date=end_date, news=self.instance
).save()
start_date += timedelta(days=7)
end_date += timedelta(days=7)
return ret
@ -213,59 +262,81 @@ class NewsForm(forms.ModelForm):
class NewsEditView(CanEditMixin, UpdateView):
model = News
form_class = NewsForm
template_name = 'com/news_edit.jinja'
pk_url_kwarg = 'news_id'
template_name = "com/news_edit.jinja"
pk_url_kwarg = "news_id"
def get_initial(self):
init = {}
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
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
return init
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid() and 'preview' not in request.POST.keys():
if form.is_valid() and "preview" not in request.POST.keys():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
self.object = form.save()
if form.cleaned_data['automoderation'] and self.request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID):
if form.cleaned_data["automoderation"] and self.request.user.is_in_group(
settings.SITH_GROUP_COM_ADMIN_ID
):
self.object.moderator = self.request.user
self.object.is_moderated = True
self.object.save()
else:
self.object.is_moderated = False
self.object.save()
for u in RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first().users.all():
if not u.notifications.filter(type="NEWS_MODERATION", viewed=False).exists():
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
for u in (
RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID)
.first()
.users.all()
):
if not u.notifications.filter(
type="NEWS_MODERATION", viewed=False
).exists():
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)
class NewsCreateView(CanCreateMixin, CreateView):
model = News
form_class = NewsForm
template_name = 'com/news_edit.jinja'
template_name = "com/news_edit.jinja"
def get_initial(self):
init = {'author': self.request.user}
init = {"author": self.request.user}
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
return init
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid() and 'preview' not in request.POST.keys():
if form.is_valid() and "preview" not in request.POST.keys():
return self.form_valid(form)
else:
self.object = form.instance
@ -273,176 +344,216 @@ class NewsCreateView(CanCreateMixin, CreateView):
def form_valid(self, form):
self.object = form.save()
if form.cleaned_data['automoderation'] and self.request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID):
if form.cleaned_data["automoderation"] and self.request.user.is_in_group(
settings.SITH_GROUP_COM_ADMIN_ID
):
self.object.moderator = self.request.user
self.object.is_moderated = True
self.object.save()
else:
for u in RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first().users.all():
if not u.notifications.filter(type="NEWS_MODERATION", viewed=False).exists():
Notification(user=u, url=reverse("com:news_admin_list"), type="NEWS_MODERATION").save()
for u in (
RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID)
.first()
.users.all()
):
if not u.notifications.filter(
type="NEWS_MODERATION", viewed=False
).exists():
Notification(
user=u,
url=reverse("com:news_admin_list"),
type="NEWS_MODERATION",
).save()
return super(NewsCreateView, self).form_valid(form)
class NewsDeleteView(CanEditMixin, DeleteView):
model = News
pk_url_kwarg = 'news_id'
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('com:news_admin_list')
pk_url_kwarg = "news_id"
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("com:news_admin_list")
class NewsModerateView(CanEditMixin, SingleObjectMixin):
model = News
pk_url_kwarg = 'news_id'
pk_url_kwarg = "news_id"
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if 'remove' in request.GET.keys():
if "remove" in request.GET.keys():
self.object.is_moderated = False
else:
self.object.is_moderated = True
self.object.moderator = request.user
self.object.save()
if 'next' in self.request.GET.keys():
return redirect(self.request.GET['next'])
return redirect('com:news_admin_list')
if "next" in self.request.GET.keys():
return redirect(self.request.GET["next"])
return redirect("com:news_admin_list")
class NewsAdminListView(CanEditMixin, ListView):
model = News
template_name = 'com/news_admin_list.jinja'
template_name = "com/news_admin_list.jinja"
queryset = News.objects.all()
class NewsListView(CanViewMixin, ListView):
model = News
template_name = 'com/news_list.jinja'
template_name = "com/news_list.jinja"
queryset = News.objects.filter(is_moderated=True)
def get_context_data(self, **kwargs):
kwargs = super(NewsListView, self).get_context_data(**kwargs)
kwargs['NewsDate'] = NewsDate
kwargs['timedelta'] = timedelta
kwargs['birthdays'] = User.objects\
.filter(date_of_birth__month=timezone.now().month, date_of_birth__day=timezone.now().day)\
.filter(role__in=['STUDENT', 'FORMER STUDENT'])\
.order_by('-date_of_birth')
kwargs["NewsDate"] = NewsDate
kwargs["timedelta"] = timedelta
kwargs["birthdays"] = (
User.objects.filter(
date_of_birth__month=timezone.now().month,
date_of_birth__day=timezone.now().day,
)
.filter(role__in=["STUDENT", "FORMER STUDENT"])
.order_by("-date_of_birth")
)
return kwargs
class NewsDetailView(CanViewMixin, DetailView):
model = News
template_name = 'com/news_detail.jinja'
pk_url_kwarg = 'news_id'
template_name = "com/news_detail.jinja"
pk_url_kwarg = "news_id"
# Weekmail
class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
model = Weekmail
template_name = 'com/weekmail_preview.jinja'
success_url = reverse_lazy('com:weekmail')
template_name = "com/weekmail_preview.jinja"
success_url = reverse_lazy("com:weekmail")
current_tab = "weekmail"
def post(self, request, *args, **kwargs):
self.object = self.get_object()
try:
if request.POST['send'] == "validate":
if request.POST["send"] == "validate":
self.object.send()
return HttpResponseRedirect(reverse('com:weekmail') + "?qn_weekmail_send_success")
return HttpResponseRedirect(
reverse("com:weekmail") + "?qn_weekmail_send_success"
)
except:
pass
return super(WeekmailEditView, self).get(request, *args, **kwargs)
def get_object(self, queryset=None):
return self.model.objects.filter(sent=False).order_by('-id').first()
return self.model.objects.filter(sent=False).order_by("-id").first()
def get_context_data(self, **kwargs):
"""Add rendered weekmail"""
kwargs = super(WeekmailPreviewView, self).get_context_data(**kwargs)
kwargs['weekmail_rendered'] = self.object.render_html()
kwargs["weekmail_rendered"] = self.object.render_html()
return kwargs
class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
model = Weekmail
template_name = 'com/weekmail.jinja'
form_class = modelform_factory(Weekmail, fields=['title', 'intro', 'joke', 'protip', 'conclusion'],
help_texts={'title': _("Delete and save to regenerate")})
success_url = reverse_lazy('com:weekmail')
template_name = "com/weekmail.jinja"
form_class = modelform_factory(
Weekmail,
fields=["title", "intro", "joke", "protip", "conclusion"],
help_texts={"title": _("Delete and save to regenerate")},
)
success_url = reverse_lazy("com:weekmail")
current_tab = "weekmail"
def get_object(self, queryset=None):
weekmail = self.model.objects.filter(sent=False).order_by('-id').first()
weekmail = self.model.objects.filter(sent=False).order_by("-id").first()
if not weekmail.title:
now = timezone.now()
weekmail.title = _("Weekmail of the ") + (now + timedelta(days=6 - now.weekday())).strftime('%d/%m/%Y')
weekmail.title = _("Weekmail of the ") + (
now + timedelta(days=6 - now.weekday())
).strftime("%d/%m/%Y")
weekmail.save()
return weekmail
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if 'up_article' in request.GET.keys():
art = get_object_or_404(WeekmailArticle, id=request.GET['up_article'], weekmail=self.object)
prev_art = self.object.articles.order_by('rank').filter(rank__lt=art.rank).last()
if "up_article" in request.GET.keys():
art = get_object_or_404(
WeekmailArticle, id=request.GET["up_article"], weekmail=self.object
)
prev_art = (
self.object.articles.order_by("rank").filter(rank__lt=art.rank).last()
)
if prev_art:
art.rank, prev_art.rank = prev_art.rank, art.rank
art.save()
prev_art.save()
self.quick_notif_list += ['qn_success']
if 'down_article' in request.GET.keys():
art = get_object_or_404(WeekmailArticle, id=request.GET['down_article'], weekmail=self.object)
next_art = self.object.articles.order_by('rank').filter(rank__gt=art.rank).first()
self.quick_notif_list += ["qn_success"]
if "down_article" in request.GET.keys():
art = get_object_or_404(
WeekmailArticle, id=request.GET["down_article"], weekmail=self.object
)
next_art = (
self.object.articles.order_by("rank").filter(rank__gt=art.rank).first()
)
if next_art:
art.rank, next_art.rank = next_art.rank, art.rank
art.save()
next_art.save()
self.quick_notif_list += ['qn_success']
if 'add_article' in request.GET.keys():
art = get_object_or_404(WeekmailArticle, id=request.GET['add_article'], weekmail=None)
self.quick_notif_list += ["qn_success"]
if "add_article" in request.GET.keys():
art = get_object_or_404(
WeekmailArticle, id=request.GET["add_article"], weekmail=None
)
art.weekmail = self.object
art.rank = self.object.articles.aggregate(Max('rank'))['rank__max'] or 0
art.rank = self.object.articles.aggregate(Max("rank"))["rank__max"] or 0
art.rank += 1
art.save()
self.quick_notif_list += ['qn_success']
if 'del_article' in request.GET.keys():
art = get_object_or_404(WeekmailArticle, id=request.GET['del_article'], weekmail=self.object)
self.quick_notif_list += ["qn_success"]
if "del_article" in request.GET.keys():
art = get_object_or_404(
WeekmailArticle, id=request.GET["del_article"], weekmail=self.object
)
art.weekmail = None
art.rank = -1
art.save()
self.quick_notif_list += ['qn_success']
self.quick_notif_list += ["qn_success"]
return super(WeekmailEditView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""Add orphan articles """
kwargs = super(WeekmailEditView, self).get_context_data(**kwargs)
kwargs['orphans'] = WeekmailArticle.objects.filter(weekmail=None)
kwargs["orphans"] = WeekmailArticle.objects.filter(weekmail=None)
return kwargs
class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
class WeekmailArticleEditView(
ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView
):
"""Edit an article"""
model = WeekmailArticle
fields = ['title', 'club', 'content']
fields = ["title", "club", "content"]
pk_url_kwarg = "article_id"
template_name = 'core/edit.jinja'
success_url = reverse_lazy('com:weekmail')
template_name = "core/edit.jinja"
success_url = reverse_lazy("com:weekmail")
quick_notif_url_arg = "qn_weekmail_article_edit"
current_tab = "weekmail"
class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
"""Post an article"""
model = WeekmailArticle
fields = ['title', 'club', 'content']
template_name = 'core/create.jinja'
success_url = reverse_lazy('core:user_tools')
fields = ["title", "club", "content"]
template_name = "core/create.jinja"
success_url = reverse_lazy("core:user_tools")
quick_notif_url_arg = "qn_weekmail_new_article"
def get_initial(self):
init = {}
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
return init
@ -456,8 +567,15 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
if m.role <= settings.SITH_MAXIMUM_FREE_ROLE:
raise
except:
form.add_error('club', ValidationError(_("You must be a board member of the selected club to post in the Weekmail.")))
if form.is_valid() and not 'preview' in request.POST.keys():
form.add_error(
"club",
ValidationError(
_(
"You must be a board member of the selected club to post in the Weekmail."
)
),
)
if form.is_valid() and not "preview" in request.POST.keys():
return self.form_valid(form)
else:
return self.form_invalid(form)
@ -469,9 +587,10 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
"""Delete an article"""
model = WeekmailArticle
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('com:weekmail')
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("com:weekmail")
pk_url_kwarg = "article_id"
@ -481,40 +600,43 @@ class MailingListAdminView(ComTabsMixin, ListView):
current_tab = "mailings"
def dispatch(self, request, *args, **kwargs):
if not (request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or request.user.is_root):
if not (
request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
or request.user.is_root
):
raise PermissionDenied
return super(MailingListAdminView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
kwargs = super(MailingListAdminView, self).get_context_data(**kwargs)
kwargs['moderated'] = self.get_queryset().filter(is_moderated=True).all()
kwargs['unmoderated'] = self.get_queryset().filter(is_moderated=False).all()
kwargs['has_moderated'] = len(kwargs['moderated']) > 0
kwargs['has_unmoderated'] = len(kwargs['unmoderated']) > 0
kwargs["moderated"] = self.get_queryset().filter(is_moderated=True).all()
kwargs["unmoderated"] = self.get_queryset().filter(is_moderated=False).all()
kwargs["has_moderated"] = len(kwargs["moderated"]) > 0
kwargs["has_unmoderated"] = len(kwargs["unmoderated"]) > 0
return kwargs
class MailingModerateView(View):
def get(self, request, *args, **kwargs):
mailing = get_object_or_404(Mailing, pk=kwargs['mailing_id'])
mailing = get_object_or_404(Mailing, pk=kwargs["mailing_id"])
if mailing.can_moderate(request.user):
mailing.is_moderated = True
mailing.moderator = request.user
mailing.save()
return redirect('com:mailing_admin')
return redirect("com:mailing_admin")
raise PermissionDenied
class PosterListBaseView(ListView):
"""List communication posters"""
current_tab = "posters"
model = Poster
template_name = 'com/poster_list.jinja'
template_name = "com/poster_list.jinja"
def dispatch(self, request, *args, **kwargs):
club_id = kwargs.pop('club_id', None)
club_id = kwargs.pop("club_id", None)
self.club = None
if club_id:
self.club = get_object_or_404(Club, pk=club_id)
@ -522,40 +644,41 @@ class PosterListBaseView(ListView):
def get_queryset(self):
if self.request.user.is_com_admin:
return Poster.objects.all().order_by('-date_begin')
return Poster.objects.all().order_by("-date_begin")
else:
return Poster.objects.filter(club=self.club.id)
def get_context_data(self, **kwargs):
kwargs = super(PosterListBaseView, self).get_context_data(**kwargs)
if not self.request.user.is_com_admin:
kwargs['club'] = self.club
kwargs["club"] = self.club
return kwargs
class PosterCreateBaseView(CreateView):
"""Create communication poster"""
current_tab = "posters"
form_class = PosterForm
template_name = 'core/create.jinja'
template_name = "core/create.jinja"
def get_queryset(self):
return Poster.objects.all()
def dispatch(self, request, *args, **kwargs):
if 'club_id' in kwargs:
self.club = get_object_or_404(Club, pk=kwargs['club_id'])
if "club_id" in kwargs:
self.club = get_object_or_404(Club, pk=kwargs["club_id"])
return super(PosterCreateBaseView, self).dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(PosterCreateBaseView, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
kwargs.update({"user": self.request.user})
return kwargs
def get_context_data(self, **kwargs):
kwargs = super(PosterCreateBaseView, self).get_context_data(**kwargs)
if not self.request.user.is_com_admin:
kwargs['club'] = self.club
kwargs["club"] = self.club
return kwargs
def form_valid(self, form):
@ -566,27 +689,28 @@ class PosterCreateBaseView(CreateView):
class PosterEditBaseView(UpdateView):
"""Edit communication poster"""
pk_url_kwarg = "poster_id"
current_tab = "posters"
form_class = PosterForm
template_name = 'com/poster_edit.jinja'
template_name = "com/poster_edit.jinja"
def get_initial(self):
init = {}
try:
init['date_begin'] = self.object.date_begin.strftime('%Y-%m-%d %H:%M:%S')
init["date_begin"] = self.object.date_begin.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
pass
try:
init['date_end'] = self.object.date_end.strftime('%Y-%m-%d %H:%M:%S')
init["date_end"] = self.object.date_end.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
pass
return init
def dispatch(self, request, *args, **kwargs):
if 'club_id' in kwargs and kwargs['club_id']:
if "club_id" in kwargs and kwargs["club_id"]:
try:
self.club = Club.objects.get(pk=kwargs['club_id'])
self.club = Club.objects.get(pk=kwargs["club_id"])
except Club.DoesNotExist:
raise PermissionDenied
return super(PosterEditBaseView, self).dispatch(request, *args, **kwargs)
@ -596,13 +720,13 @@ class PosterEditBaseView(UpdateView):
def get_form_kwargs(self):
kwargs = super(PosterEditBaseView, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
kwargs.update({"user": self.request.user})
return kwargs
def get_context_data(self, **kwargs):
kwargs = super(PosterEditBaseView, self).get_context_data(**kwargs)
if not self.request.user.is_com_admin:
kwargs['club'] = self.club
kwargs["club"] = self.club
return kwargs
def form_valid(self, form):
@ -613,15 +737,16 @@ class PosterEditBaseView(UpdateView):
class PosterDeleteBaseView(DeleteView):
"""Edit communication poster"""
pk_url_kwarg = "poster_id"
current_tab = "posters"
model = Poster
template_name = 'core/delete_confirm.jinja'
template_name = "core/delete_confirm.jinja"
def dispatch(self, request, *args, **kwargs):
if 'club_id' in kwargs and kwargs['club_id']:
if "club_id" in kwargs and kwargs["club_id"]:
try:
self.club = Club.objects.get(pk=kwargs['club_id'])
self.club = Club.objects.get(pk=kwargs["club_id"])
except Club.DoesNotExist:
raise PermissionDenied
return super(PosterDeleteBaseView, self).dispatch(request, *args, **kwargs)
@ -632,107 +757,117 @@ class PosterListView(IsComAdminMixin, ComTabsMixin, PosterListBaseView):
def get_context_data(self, **kwargs):
kwargs = super(PosterListView, self).get_context_data(**kwargs)
kwargs['app'] = "com"
kwargs["app"] = "com"
return kwargs
class PosterCreateView(IsComAdminMixin, ComTabsMixin, PosterCreateBaseView):
"""Create communication poster"""
success_url = reverse_lazy('com:poster_list')
success_url = reverse_lazy("com:poster_list")
def get_context_data(self, **kwargs):
kwargs = super(PosterCreateView, self).get_context_data(**kwargs)
kwargs['app'] = "com"
kwargs["app"] = "com"
return kwargs
class PosterEditView(IsComAdminMixin, ComTabsMixin, PosterEditBaseView):
"""Edit communication poster"""
success_url = reverse_lazy('com:poster_list')
success_url = reverse_lazy("com:poster_list")
def get_context_data(self, **kwargs):
kwargs = super(PosterEditView, self).get_context_data(**kwargs)
kwargs['app'] = "com"
kwargs["app"] = "com"
return kwargs
class PosterDeleteView(IsComAdminMixin, ComTabsMixin, PosterDeleteBaseView):
"""Delete communication poster"""
success_url = reverse_lazy('com:poster_list')
success_url = reverse_lazy("com:poster_list")
class PosterModerateListView(IsComAdminMixin, ComTabsMixin, ListView):
"""Moderate list communication poster"""
current_tab = "posters"
model = Poster
template_name = 'com/poster_moderate.jinja'
template_name = "com/poster_moderate.jinja"
queryset = Poster.objects.filter(is_moderated=False).all()
def get_context_data(self, **kwargs):
kwargs = super(PosterModerateListView, self).get_context_data(**kwargs)
kwargs['app'] = "com"
kwargs["app"] = "com"
return kwargs
class PosterModerateView(IsComAdminMixin, ComTabsMixin, View):
"""Moderate communication poster"""
def get(self, request, *args, **kwargs):
obj = get_object_or_404(Poster, pk=kwargs['object_id'])
obj = get_object_or_404(Poster, pk=kwargs["object_id"])
if obj.can_be_moderated_by(request.user):
obj.is_moderated = True
obj.moderator = request.user
obj.save()
return redirect('com:poster_moderate_list')
return redirect("com:poster_moderate_list")
raise PermissionDenied
def get_context_data(self, **kwargs):
kwargs = super(PosterModerateListView, self).get_context_data(**kwargs)
kwargs['app'] = "com"
kwargs["app"] = "com"
return kwargs
class ScreenListView(IsComAdminMixin, ComTabsMixin, ListView):
"""List communication screens"""
current_tab = "screens"
model = Screen
template_name = 'com/screen_list.jinja'
template_name = "com/screen_list.jinja"
class ScreenSlideshowView(DetailView):
"""Slideshow of actives posters"""
pk_url_kwarg = "screen_id"
model = Screen
template_name = 'com/screen_slideshow.jinja'
template_name = "com/screen_slideshow.jinja"
def get_context_data(self, **kwargs):
kwargs = super(ScreenSlideshowView, self).get_context_data(**kwargs)
kwargs['posters'] = self.object.active_posters()
kwargs["posters"] = self.object.active_posters()
return kwargs
class ScreenCreateView(IsComAdminMixin, ComTabsMixin, CreateView):
"""Create communication screen"""
current_tab = "screens"
model = Screen
fields = ['name', ]
template_name = 'core/create.jinja'
success_url = reverse_lazy('com:screen_list')
fields = ["name"]
template_name = "core/create.jinja"
success_url = reverse_lazy("com:screen_list")
class ScreenEditView(IsComAdminMixin, ComTabsMixin, UpdateView):
"""Edit communication screen"""
pk_url_kwarg = "screen_id"
current_tab = "screens"
model = Screen
fields = ['name', ]
template_name = 'com/screen_edit.jinja'
success_url = reverse_lazy('com:screen_list')
fields = ["name"]
template_name = "com/screen_edit.jinja"
success_url = reverse_lazy("com:screen_list")
class ScreenDeleteView(IsComAdminMixin, ComTabsMixin, DeleteView):
"""Delete communication screen"""
pk_url_kwarg = "screen_id"
current_tab = "screens"
model = Screen
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('com:screen_list')
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("com:screen_list")

View File

@ -22,4 +22,4 @@
#
#
default_app_config = 'core.apps.SithConfig'
default_app_config = "core.apps.SithConfig"

View File

@ -32,30 +32,38 @@ from haystack.admin import SearchModelAdmin
admin.site.unregister(AuthGroup)
admin.site.register(RealGroup)
class UserAdmin(SearchModelAdmin):
list_display = ["first_name", "last_name", "username", "email", "nick_name"]
form = make_ajax_form(User, {
'godfathers': 'users',
'home': 'files', # ManyToManyField
'profile_pict': 'files', # ManyToManyField
'avatar_pict': 'files', # ManyToManyField
'scrub_pict': 'files', # ManyToManyField
})
form = make_ajax_form(
User,
{
"godfathers": "users",
"home": "files", # ManyToManyField
"profile_pict": "files", # ManyToManyField
"avatar_pict": "files", # ManyToManyField
"scrub_pict": "files", # ManyToManyField
},
)
search_fields = ["first_name", "last_name", "username"]
admin.site.register(User, UserAdmin)
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
form = make_ajax_form(Page, {
'lock_user': 'users',
'owner_group': 'groups',
'edit_groups': 'groups',
'view_groups': 'groups',
})
form = make_ajax_form(
Page,
{
"lock_user": "users",
"owner_group": "groups",
"edit_groups": "groups",
"view_groups": "groups",
},
)
@admin.register(SithFile)
class SithFileAdmin(admin.ModelAdmin):
form = make_ajax_form(SithFile, {
'parent': 'files', # ManyToManyField
})
form = make_ajax_form(SithFile, {"parent": "files"}) # ManyToManyField

View File

@ -29,7 +29,7 @@ from django.core.signals import request_started
class SithConfig(AppConfig):
name = 'core'
name = "core"
verbose_name = "Core app of the Sith"
def ready(self):
@ -47,6 +47,12 @@ class SithConfig(AppConfig):
Forum._club_memberships = {}
print("Connecting signals!", file=sys.stderr)
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_groups, weak=False, dispatch_uid="clear_cached_groups"
)
request_started.connect(
clear_cached_memberships,
weak=False,
dispatch_uid="clear_cached_memberships",
)
# TODO: there may be a need to add more cache clearing

View File

@ -33,9 +33,11 @@ from accounting.models import ClubAccount, Company
def check_token(request):
return ('counter_token' in request.session.keys() and
request.session['counter_token'] and
Counter.objects.filter(token=request.session['counter_token']).exists())
return (
"counter_token" in request.session.keys()
and request.session["counter_token"]
and Counter.objects.filter(token=request.session["counter_token"]).exists()
)
class RightManagedLookupChannel(LookupChannel):
@ -44,7 +46,7 @@ class RightManagedLookupChannel(LookupChannel):
raise PermissionDenied
@register('users')
@register("users")
class UsersLookup(RightManagedLookupChannel):
model = User
@ -58,7 +60,7 @@ class UsersLookup(RightManagedLookupChannel):
return item.get_display_name()
@register('groups')
@register("groups")
class GroupsLookup(RightManagedLookupChannel):
model = Group
@ -72,7 +74,7 @@ class GroupsLookup(RightManagedLookupChannel):
return item.name
@register('clubs')
@register("clubs")
class ClubLookup(RightManagedLookupChannel):
model = Club
@ -86,7 +88,7 @@ class ClubLookup(RightManagedLookupChannel):
return item.name
@register('counters')
@register("counters")
class CountersLookup(RightManagedLookupChannel):
model = Counter
@ -97,19 +99,21 @@ class CountersLookup(RightManagedLookupChannel):
return item.name
@register('products')
@register("products")
class ProductsLookup(RightManagedLookupChannel):
model = Product
def get_query(self, q, request):
return (self.model.objects.filter(name__icontains=q) |
self.model.objects.filter(code__icontains=q)).filter(archived=False)[:50]
return (
self.model.objects.filter(name__icontains=q)
| self.model.objects.filter(code__icontains=q)
).filter(archived=False)[:50]
def format_item_display(self, item):
return "%s (%s)" % (item.name, item.code)
@register('files')
@register("files")
class SithFileLookup(RightManagedLookupChannel):
model = SithFile
@ -117,7 +121,7 @@ class SithFileLookup(RightManagedLookupChannel):
return self.model.objects.filter(name__icontains=q)[:50]
@register('club_accounts')
@register("club_accounts")
class ClubAccountLookup(RightManagedLookupChannel):
model = ClubAccount
@ -128,7 +132,7 @@ class ClubAccountLookup(RightManagedLookupChannel):
return item.name
@register('companies')
@register("companies")
class CompaniesLookup(RightManagedLookupChannel):
model = Company

View File

@ -21,4 +21,3 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -33,10 +33,14 @@ class Command(BaseCommand):
help = "Recursively check the file system with respect to the DB"
def add_arguments(self, parser):
parser.add_argument('ids', metavar='ID', type=int, nargs='+', help="The file IDs to process")
parser.add_argument(
"ids", metavar="ID", type=int, nargs="+", help="The file IDs to process"
)
def handle(self, *args, **options):
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
files = SithFile.objects.filter(id__in=options['ids']).all()
root_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
files = SithFile.objects.filter(id__in=options["ids"]).all()
for f in files:
f._check_fs()

View File

@ -33,15 +33,13 @@ class Command(BaseCommand):
"""
Compiles scss in static folder for production
"""
help = "Compile scss files from static folder"
def compile(self, filename):
args = {
"filename": filename,
"include_paths": settings.STATIC_ROOT,
}
args = {"filename": filename, "include_paths": settings.STATIC_ROOT}
if settings.SASS_PRECISION:
args['precision'] = settings.SASS_PRECISION
args["precision"] = settings.SASS_PRECISION
return sass.compile(**args)
def is_compilable(self, file, ext_list):
@ -54,7 +52,7 @@ class Command(BaseCommand):
file = os.path.join(folder, file)
if os.path.isdir(file):
self.exec_on_folder(file, func)
elif self.is_compilable(file, ['.scss']):
elif self.is_compilable(file, [".scss"]):
to_exec.append(file)
for file in to_exec:
@ -62,7 +60,7 @@ class Command(BaseCommand):
def compilescss(self, file):
print("compiling %s" % file)
with(open(file.replace('.scss', '.css'), "w")) as newfile:
with (open(file.replace(".scss", ".css"), "w")) as newfile:
newfile.write(self.compile(file))
def removescss(self, file):
@ -77,4 +75,6 @@ class Command(BaseCommand):
print("---- Removing scss files ----")
self.exec_on_folder(settings.STATIC_ROOT, self.removescss)
else:
print("No static folder avalaible, please use collectstatic before compiling scss")
print(
"No static folder avalaible, please use collectstatic before compiling scss"
)

View File

@ -27,11 +27,14 @@ from django.core.management.base import BaseCommand
from core.markdown import markdown
class Command(BaseCommand):
help = "Output the fully rendered doc/SYNTAX.md file"
def handle(self, *args, **options):
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
with open(os.path.join(root_path) + '/doc/SYNTAX.md', 'r') as md:
root_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
with open(os.path.join(root_path) + "/doc/SYNTAX.md", "r") as md:
result = markdown(md.read())
print(result, end='')
print(result, end="")

File diff suppressed because it is too large Load Diff

View File

@ -33,10 +33,14 @@ class Command(BaseCommand):
help = "Recursively repair the file system with respect to the DB"
def add_arguments(self, parser):
parser.add_argument('ids', metavar='ID', type=int, nargs='+', help="The file IDs to process")
parser.add_argument(
"ids", metavar="ID", type=int, nargs="+", help="The file IDs to process"
)
def handle(self, *args, **options):
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
files = SithFile.objects.filter(id__in=options['ids']).all()
root_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
files = SithFile.objects.filter(id__in=options["ids"]).all()
for f in files:
f._repair_fs()

View File

@ -31,22 +31,24 @@ class Command(BaseCommand):
help = "Set up a new instance of the Sith AE"
def add_arguments(self, parser):
parser.add_argument('--prod', action="store_true")
parser.add_argument("--prod", action="store_true")
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:
os.mkdir(os.path.join(root_path) + '/data')
os.mkdir(os.path.join(root_path) + "/data")
print("Data dir created")
except Exception as e:
repr(e)
try:
os.remove(os.path.join(root_path, 'db.sqlite3'))
os.remove(os.path.join(root_path, "db.sqlite3"))
print("db.sqlite3 deleted")
except Exception as e:
repr(e)
call_command('migrate')
if options['prod']:
call_command('populate', '--prod')
call_command("migrate")
if options["prod"]:
call_command("populate", "--prod")
else:
call_command('populate')
call_command("populate")

View File

@ -30,7 +30,7 @@ from django.core.urlresolvers import reverse
class SithRenderer(Renderer):
def file_link(self, id, suffix):
return reverse('core:file_detail', kwargs={'file_id': id}) + suffix
return reverse("core:file_detail", kwargs={"file_id": id}) + suffix
def exposant(self, text):
return """<sup>%s</sup>""" % text
@ -48,19 +48,19 @@ class SithRenderer(Renderer):
:param text: alt text of the image.
"""
style = None
if '?' in original_src:
src, params = original_src.rsplit('?', maxsplit=1)
m = re.search(r'(\d+%?)(x(\d+%?))?', params)
if "?" in original_src:
src, params = original_src.rsplit("?", maxsplit=1)
m = re.search(r"(\d+%?)(x(\d+%?))?", params)
if not m:
src = original_src
else:
width = m.group(1)
if not width.endswith('%'):
if not width.endswith("%"):
width += "px"
style = "width: %s; " % width
try:
height = m.group(3)
if not height.endswith('%'):
if not height.endswith("%"):
height += "px"
style += "height: %s; " % height
except:
@ -77,67 +77,57 @@ class SithRenderer(Renderer):
html = '<img src="%s" alt="%s"' % (src, text)
if style:
html = '%s style="%s"' % (html, style)
if self.options.get('use_xhtml'):
return '%s />' % html
return '%s>' % html
if self.options.get("use_xhtml"):
return "%s />" % html
return "%s>" % html
class SithInlineGrammar(InlineGrammar):
double_emphasis = re.compile(
r'^\*{2}([\s\S]+?)\*{2}(?!\*)' # **word**
)
emphasis = re.compile(
r'^\*((?:\*\*|[^\*])+?)\*(?!\*)' # *word*
)
underline = re.compile(
r'^_{2}([\s\S]+?)_{2}(?!_)' # __word__
)
exposant = re.compile(
r'^<sup>([\s\S]+?)</sup>' # <sup>text</sup>
)
indice = re.compile(
r'^<sub>([\s\S]+?)</sub>' # <sub>text</sub>
)
double_emphasis = re.compile(r"^\*{2}([\s\S]+?)\*{2}(?!\*)") # **word**
emphasis = re.compile(r"^\*((?:\*\*|[^\*])+?)\*(?!\*)") # *word*
underline = re.compile(r"^_{2}([\s\S]+?)_{2}(?!_)") # __word__
exposant = re.compile(r"^<sup>([\s\S]+?)</sup>") # <sup>text</sup>
indice = re.compile(r"^<sub>([\s\S]+?)</sub>") # <sub>text</sub>
class SithInlineLexer(InlineLexer):
grammar_class = SithInlineGrammar
default_rules = [
'escape',
"escape",
# 'inline_html',
'autolink',
'url',
'footnote',
'link',
'reflink',
'nolink',
'exposant',
'double_emphasis',
'emphasis',
'underline',
'indice',
'code',
'linebreak',
'strikethrough',
'text',
"autolink",
"url",
"footnote",
"link",
"reflink",
"nolink",
"exposant",
"double_emphasis",
"emphasis",
"underline",
"indice",
"code",
"linebreak",
"strikethrough",
"text",
]
inline_html_rules = [
'escape',
'autolink',
'url',
'link',
'reflink',
'nolink',
'exposant',
'double_emphasis',
'emphasis',
'underline',
'indice',
'code',
'linebreak',
'strikethrough',
'text',
"escape",
"autolink",
"url",
"link",
"reflink",
"nolink",
"exposant",
"double_emphasis",
"emphasis",
"underline",
"indice",
"code",
"linebreak",
"strikethrough",
"text",
]
def output_underline(self, m):
@ -166,22 +156,18 @@ class SithInlineLexer(InlineLexer):
def _process_link(self, m, link, title=None):
try: # Add page:// support for links
page = re.compile(
r'^page://(\S*)' # page://nom_de_ma_page
)
page = re.compile(r"^page://(\S*)") # page://nom_de_ma_page
match = page.search(link)
page = match.group(1) or ""
link = reverse('core:page', kwargs={'page_name': page})
link = reverse("core:page", kwargs={"page_name": page})
except:
pass
try: # Add file:// support for links
file_link = re.compile(
r'^file://(\d*)/?(\S*)?' # file://4000/download
)
file_link = re.compile(r"^file://(\d*)/?(\S*)?") # file://4000/download
match = file_link.search(link)
id = match.group(1)
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
return super(SithInlineLexer, self)._process_link(m, link, title)
@ -194,6 +180,6 @@ markdown = Markdown(renderer, inline=inline)
if __name__ == "__main__":
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
with open(os.path.join(root_path) + '/doc/SYNTAX.md', 'r') as md:
with open(os.path.join(root_path) + "/doc/SYNTAX.md", "r") as md:
result = markdown(md.read())
print(result, end='')
print(result, end="")

View File

@ -26,14 +26,16 @@ import importlib
from django.conf import settings
from django.utils.functional import SimpleLazyObject
from django.contrib.auth import get_user
from django.contrib.auth.middleware import AuthenticationMiddleware as DjangoAuthenticationMiddleware
from django.contrib.auth.middleware import (
AuthenticationMiddleware as DjangoAuthenticationMiddleware,
)
module, klass = settings.AUTH_ANONYMOUS_MODEL.rsplit('.', 1)
module, klass = settings.AUTH_ANONYMOUS_MODEL.rsplit(".", 1)
AnonymousUser = getattr(importlib.import_module(module), klass)
def get_cached_user(request):
if not hasattr(request, '_cached_user'):
if not hasattr(request, "_cached_user"):
user = get_user(request)
if user.is_anonymous():
user = AnonymousUser(request)
@ -45,7 +47,7 @@ def get_cached_user(request):
class AuthenticationMiddleware(DjangoAuthenticationMiddleware):
def process_request(self, request):
assert hasattr(request, 'session'), (
assert hasattr(request, "session"), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE_CLASSES setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "

View File

@ -12,169 +12,559 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('auth', '0006_require_contenttypes_0002'),
]
dependencies = [("auth", "0006_require_contenttypes_0002")]
operations = [
migrations.CreateModel(
name='User',
name="User",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)),
('username', models.CharField(help_text='Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, max_length=254, error_messages={'unique': 'A user with that username already exists.'}, verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')])),
('first_name', models.CharField(max_length=64, verbose_name='first name')),
('last_name', models.CharField(max_length=64, verbose_name='last name')),
('email', models.EmailField(unique=True, max_length=254, verbose_name='email address')),
('date_of_birth', models.DateField(null=True, verbose_name='date of birth', blank=True)),
('nick_name', models.CharField(max_length=64, null=True, verbose_name='nick name', blank=True)),
('is_staff', models.BooleanField(help_text='Designates whether the user can log into this admin site.', verbose_name='staff status', default=False)),
('is_active', models.BooleanField(help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active', default=True)),
('date_joined', models.DateField(auto_now_add=True, verbose_name='date joined')),
('last_update', models.DateField(verbose_name='last update', auto_now=True)),
('is_superuser', models.BooleanField(help_text='Designates whether this user is a superuser. ', verbose_name='superuser', default=False)),
('sex', models.CharField(choices=[('MAN', 'Man'), ('WOMAN', 'Woman')], max_length=10, default='MAN', verbose_name='sex')),
('tshirt_size', models.CharField(choices=[('-', '-'), ('XS', 'XS'), ('S', 'S'), ('M', 'M'), ('L', 'L'), ('XL', 'XL'), ('XXL', 'XXL'), ('XXXL', 'XXXL')], max_length=5, default='-', verbose_name='tshirt size')),
('role', models.CharField(choices=[('STUDENT', 'Student'), ('ADMINISTRATIVE', 'Administrative agent'), ('TEACHER', 'Teacher'), ('AGENT', 'Agent'), ('DOCTOR', 'Doctor'), ('FORMER STUDENT', 'Former student'), ('SERVICE', 'Service')], max_length=15, blank=True, verbose_name='role', default='')),
('department', models.CharField(choices=[('TC', 'TC'), ('IMSI', 'IMSI'), ('IMAP', 'IMAP'), ('INFO', 'INFO'), ('GI', 'GI'), ('E', 'E'), ('EE', 'EE'), ('GESC', 'GESC'), ('GMC', 'GMC'), ('MC', 'MC'), ('EDIM', 'EDIM'), ('HUMA', 'Humanities'), ('NA', 'N/A')], max_length=15, blank=True, verbose_name='department', default='NA')),
('dpt_option', models.CharField(max_length=32, blank=True, verbose_name='dpt option', default='')),
('semester', models.CharField(max_length=5, blank=True, verbose_name='semester', default='')),
('quote', models.CharField(max_length=256, blank=True, verbose_name='quote', default='')),
('school', models.CharField(max_length=80, blank=True, verbose_name='school', default='')),
('promo', models.IntegerField(null=True, verbose_name='promo', validators=[core.models.validate_promo], blank=True)),
('forum_signature', models.TextField(max_length=256, blank=True, verbose_name='forum signature', default='')),
('second_email', models.EmailField(max_length=254, null=True, verbose_name='second email address', blank=True)),
('phone', phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, verbose_name='phone', blank=True)),
('parent_phone', phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, verbose_name='parent phone', blank=True)),
('address', models.CharField(max_length=128, blank=True, verbose_name='address', default='')),
('parent_address', models.CharField(max_length=128, blank=True, verbose_name='parent address', default='')),
('is_subscriber_viewable', models.BooleanField(verbose_name='is subscriber viewable', default=True)),
],
options={
'abstract': False,
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
null=True, verbose_name="last login", blank=True
),
),
(
"username",
models.CharField(
help_text="Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.",
unique=True,
max_length=254,
error_messages={
"unique": "A user with that username already exists."
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
verbose_name="username",
validators=[
django.core.validators.RegexValidator(
"^[\\w.@+-]+$",
"Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.",
)
],
),
migrations.CreateModel(
name='Group',
fields=[
('group_ptr', models.OneToOneField(primary_key=True, parent_link=True, serialize=False, to='auth.Group', auto_created=True)),
('is_meta', models.BooleanField(help_text='Whether a group is a meta group or not', verbose_name='meta group status', default=False)),
('description', models.CharField(max_length=60, verbose_name='description')),
),
(
"first_name",
models.CharField(max_length=64, verbose_name="first name"),
),
(
"last_name",
models.CharField(max_length=64, verbose_name="last name"),
),
(
"email",
models.EmailField(
unique=True, max_length=254, verbose_name="email address"
),
),
(
"date_of_birth",
models.DateField(
null=True, verbose_name="date of birth", blank=True
),
),
(
"nick_name",
models.CharField(
max_length=64, null=True, verbose_name="nick name", blank=True
),
),
(
"is_staff",
models.BooleanField(
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
default=False,
),
),
(
"is_active",
models.BooleanField(
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
default=True,
),
),
(
"date_joined",
models.DateField(auto_now_add=True, verbose_name="date joined"),
),
(
"last_update",
models.DateField(verbose_name="last update", auto_now=True),
),
(
"is_superuser",
models.BooleanField(
help_text="Designates whether this user is a superuser. ",
verbose_name="superuser",
default=False,
),
),
(
"sex",
models.CharField(
choices=[("MAN", "Man"), ("WOMAN", "Woman")],
max_length=10,
default="MAN",
verbose_name="sex",
),
),
(
"tshirt_size",
models.CharField(
choices=[
("-", "-"),
("XS", "XS"),
("S", "S"),
("M", "M"),
("L", "L"),
("XL", "XL"),
("XXL", "XXL"),
("XXXL", "XXXL"),
],
bases=('auth.group',),
max_length=5,
default="-",
verbose_name="tshirt size",
),
),
(
"role",
models.CharField(
choices=[
("STUDENT", "Student"),
("ADMINISTRATIVE", "Administrative agent"),
("TEACHER", "Teacher"),
("AGENT", "Agent"),
("DOCTOR", "Doctor"),
("FORMER STUDENT", "Former student"),
("SERVICE", "Service"),
],
max_length=15,
blank=True,
verbose_name="role",
default="",
),
),
(
"department",
models.CharField(
choices=[
("TC", "TC"),
("IMSI", "IMSI"),
("IMAP", "IMAP"),
("INFO", "INFO"),
("GI", "GI"),
("E", "E"),
("EE", "EE"),
("GESC", "GESC"),
("GMC", "GMC"),
("MC", "MC"),
("EDIM", "EDIM"),
("HUMA", "Humanities"),
("NA", "N/A"),
],
max_length=15,
blank=True,
verbose_name="department",
default="NA",
),
),
(
"dpt_option",
models.CharField(
max_length=32, blank=True, verbose_name="dpt option", default=""
),
),
(
"semester",
models.CharField(
max_length=5, blank=True, verbose_name="semester", default=""
),
),
(
"quote",
models.CharField(
max_length=256, blank=True, verbose_name="quote", default=""
),
),
(
"school",
models.CharField(
max_length=80, blank=True, verbose_name="school", default=""
),
),
(
"promo",
models.IntegerField(
null=True,
verbose_name="promo",
validators=[core.models.validate_promo],
blank=True,
),
),
(
"forum_signature",
models.TextField(
max_length=256,
blank=True,
verbose_name="forum signature",
default="",
),
),
(
"second_email",
models.EmailField(
max_length=254,
null=True,
verbose_name="second email address",
blank=True,
),
),
(
"phone",
phonenumber_field.modelfields.PhoneNumberField(
max_length=128, null=True, verbose_name="phone", blank=True
),
),
(
"parent_phone",
phonenumber_field.modelfields.PhoneNumberField(
max_length=128,
null=True,
verbose_name="parent phone",
blank=True,
),
),
(
"address",
models.CharField(
max_length=128, blank=True, verbose_name="address", default=""
),
),
(
"parent_address",
models.CharField(
max_length=128,
blank=True,
verbose_name="parent address",
default="",
),
),
(
"is_subscriber_viewable",
models.BooleanField(
verbose_name="is subscriber viewable", default=True
),
),
],
options={"abstract": False},
managers=[("objects", django.contrib.auth.models.UserManager())],
),
migrations.CreateModel(
name='Page',
name="Group",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=30, verbose_name='page name')),
('_full_name', models.CharField(max_length=255, blank=True, verbose_name='page name')),
('edit_groups', models.ManyToManyField(related_name='editable_page', to='core.Group', blank=True, verbose_name='edit group')),
('owner_group', models.ForeignKey(default=1, related_name='owned_page', verbose_name='owner group', to='core.Group')),
('parent', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, null=True, related_name='children', verbose_name='parent', to='core.Page', blank=True)),
('view_groups', models.ManyToManyField(related_name='viewable_page', to='core.Group', blank=True, verbose_name='view group')),
(
"group_ptr",
models.OneToOneField(
primary_key=True,
parent_link=True,
serialize=False,
to="auth.Group",
auto_created=True,
),
),
(
"is_meta",
models.BooleanField(
help_text="Whether a group is a meta group or not",
verbose_name="meta group status",
default=False,
),
),
(
"description",
models.CharField(max_length=60, verbose_name="description"),
),
],
bases=("auth.group",),
),
migrations.CreateModel(
name="Page",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=30, verbose_name="page name")),
(
"_full_name",
models.CharField(
max_length=255, blank=True, verbose_name="page name"
),
),
(
"edit_groups",
models.ManyToManyField(
related_name="editable_page",
to="core.Group",
blank=True,
verbose_name="edit group",
),
),
(
"owner_group",
models.ForeignKey(
default=1,
related_name="owned_page",
verbose_name="owner group",
to="core.Group",
),
),
(
"parent",
models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL,
null=True,
related_name="children",
verbose_name="parent",
to="core.Page",
blank=True,
),
),
(
"view_groups",
models.ManyToManyField(
related_name="viewable_page",
to="core.Group",
blank=True,
verbose_name="view group",
),
),
],
options={
'permissions': (('change_prop_page', "Can change the page's properties (groups, ...)"), ('view_page', 'Can view the page')),
"permissions": (
(
"change_prop_page",
"Can change the page's properties (groups, ...)",
),
("view_page", "Can view the page"),
)
},
),
migrations.CreateModel(
name='PageRev',
name="PageRev",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('revision', models.IntegerField(verbose_name='revision')),
('title', models.CharField(max_length=255, blank=True, verbose_name='page title')),
('content', models.TextField(blank=True, verbose_name='page content')),
('date', models.DateTimeField(verbose_name='date', auto_now=True)),
('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='page_rev')),
('page', models.ForeignKey(to='core.Page', related_name='revisions')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("revision", models.IntegerField(verbose_name="revision")),
(
"title",
models.CharField(
max_length=255, blank=True, verbose_name="page title"
),
),
("content", models.TextField(blank=True, verbose_name="page content")),
("date", models.DateTimeField(verbose_name="date", auto_now=True)),
(
"author",
models.ForeignKey(
to=settings.AUTH_USER_MODEL, related_name="page_rev"
),
),
("page", models.ForeignKey(to="core.Page", related_name="revisions")),
],
options={
'ordering': ['date'],
},
options={"ordering": ["date"]},
),
migrations.CreateModel(
name='Preferences',
name="Preferences",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('show_my_stats', models.BooleanField(help_text='Show your account statistics to others', verbose_name='define if we show a users stats', default=False)),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, related_name='preferences')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
(
"show_my_stats",
models.BooleanField(
help_text="Show your account statistics to others",
verbose_name="define if we show a users stats",
default=False,
),
),
(
"user",
models.OneToOneField(
to=settings.AUTH_USER_MODEL, related_name="preferences"
),
),
],
),
migrations.CreateModel(
name='SithFile',
name="SithFile",
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=30, verbose_name='file name')),
('file', models.FileField(upload_to=core.models.get_directory, null=True, verbose_name='file', blank=True)),
('is_folder', models.BooleanField(verbose_name='is folder', default=True)),
('mime_type', models.CharField(max_length=30, verbose_name='mime type')),
('size', models.IntegerField(default=0, verbose_name='size')),
('date', models.DateTimeField(verbose_name='date', auto_now=True)),
('edit_groups', models.ManyToManyField(related_name='editable_files', to='core.Group', blank=True, verbose_name='edit group')),
('owner', models.ForeignKey(verbose_name='owner', to=settings.AUTH_USER_MODEL, related_name='owned_files')),
('parent', models.ForeignKey(null=True, related_name='children', verbose_name='parent', to='core.SithFile', blank=True)),
('view_groups', models.ManyToManyField(related_name='viewable_files', to='core.Group', blank=True, verbose_name='view group')),
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=30, verbose_name="file name")),
(
"file",
models.FileField(
upload_to=core.models.get_directory,
null=True,
verbose_name="file",
blank=True,
),
),
(
"is_folder",
models.BooleanField(verbose_name="is folder", default=True),
),
(
"mime_type",
models.CharField(max_length=30, verbose_name="mime type"),
),
("size", models.IntegerField(default=0, verbose_name="size")),
("date", models.DateTimeField(verbose_name="date", auto_now=True)),
(
"edit_groups",
models.ManyToManyField(
related_name="editable_files",
to="core.Group",
blank=True,
verbose_name="edit group",
),
),
(
"owner",
models.ForeignKey(
verbose_name="owner",
to=settings.AUTH_USER_MODEL,
related_name="owned_files",
),
),
(
"parent",
models.ForeignKey(
null=True,
related_name="children",
verbose_name="parent",
to="core.SithFile",
blank=True,
),
),
(
"view_groups",
models.ManyToManyField(
related_name="viewable_files",
to="core.Group",
blank=True,
verbose_name="view group",
),
),
],
options={
'verbose_name': 'file',
},
options={"verbose_name": "file"},
),
migrations.AddField(
model_name='user',
name='avatar_pict',
field=models.OneToOneField(blank=True, on_delete=django.db.models.deletion.SET_NULL, null=True, related_name='avatar_of', verbose_name='avatar', to='core.SithFile'),
model_name="user",
name="avatar_pict",
field=models.OneToOneField(
blank=True,
on_delete=django.db.models.deletion.SET_NULL,
null=True,
related_name="avatar_of",
verbose_name="avatar",
to="core.SithFile",
),
),
migrations.AddField(
model_name='user',
name='home',
field=models.OneToOneField(blank=True, null=True, related_name='home_of', verbose_name='home', to='core.SithFile'),
model_name="user",
name="home",
field=models.OneToOneField(
blank=True,
null=True,
related_name="home_of",
verbose_name="home",
to="core.SithFile",
),
),
migrations.AddField(
model_name='user',
name='profile_pict',
field=models.OneToOneField(blank=True, on_delete=django.db.models.deletion.SET_NULL, null=True, related_name='profile_of', verbose_name='profile', to='core.SithFile'),
model_name="user",
name="profile_pict",
field=models.OneToOneField(
blank=True,
on_delete=django.db.models.deletion.SET_NULL,
null=True,
related_name="profile_of",
verbose_name="profile",
to="core.SithFile",
),
),
migrations.AddField(
model_name='user',
name='scrub_pict',
field=models.OneToOneField(blank=True, on_delete=django.db.models.deletion.SET_NULL, null=True, related_name='scrub_of', verbose_name='scrub', to='core.SithFile'),
model_name="user",
name="scrub_pict",
field=models.OneToOneField(
blank=True,
on_delete=django.db.models.deletion.SET_NULL,
null=True,
related_name="scrub_of",
verbose_name="scrub",
to="core.SithFile",
),
),
migrations.CreateModel(
name='MetaGroup',
fields=[
],
options={
'proxy': True,
},
bases=('core.group',),
managers=[
('objects', core.models.MetaGroupManager()),
],
name="MetaGroup",
fields=[],
options={"proxy": True},
bases=("core.group",),
managers=[("objects", core.models.MetaGroupManager())],
),
migrations.CreateModel(
name='RealGroup',
fields=[
],
options={
'proxy': True,
},
bases=('core.group',),
managers=[
('objects', core.models.RealGroupManager()),
],
name="RealGroup",
fields=[],
options={"proxy": True},
bases=("core.group",),
managers=[("objects", core.models.RealGroupManager())],
),
migrations.AlterUniqueTogether(
name='page',
unique_together=set([('name', 'parent')]),
name="page", unique_together=set([("name", "parent")])
),
migrations.AddField(
model_name='user',
name='groups',
field=models.ManyToManyField(to='core.RealGroup', blank=True, related_name='users'),
model_name="user",
name="groups",
field=models.ManyToManyField(
to="core.RealGroup", blank=True, related_name="users"
),
),
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
dependencies = [("core", "0001_initial")]
operations = [
migrations.AlterField(
model_name='sithfile',
name='name',
field=models.CharField(verbose_name='file name', max_length=256),
),
model_name="sithfile",
name="name",
field=models.CharField(verbose_name="file name", max_length=256),
)
]

View File

@ -7,14 +7,24 @@ import django.core.validators
class Migration(migrations.Migration):
dependencies = [
('core', '0002_auto_20160831_0144'),
]
dependencies = [("core", "0002_auto_20160831_0144")]
operations = [
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=254, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.+-]+$', 'Enter a valid username. This value may contain only letters, numbers and ./+/-/_ characters.')], help_text='Required. 254 characters or fewer. Letters, digits and ./+/-/_ only.', verbose_name='username'),
model_name="user",
name="username",
field=models.CharField(
error_messages={"unique": "A user with that username already exists."},
max_length=254,
unique=True,
validators=[
django.core.validators.RegexValidator(
"^[\\w.+-]+$",
"Enter a valid username. This value may contain only letters, numbers and ./+/-/_ characters.",
)
],
help_text="Required. 254 characters or fewer. Letters, digits and ./+/-/_ only.",
verbose_name="username",
),
)
]

View File

@ -7,14 +7,14 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('core', '0003_auto_20160902_1914'),
]
dependencies = [("core", "0003_auto_20160902_1914")]
operations = [
migrations.AddField(
model_name='user',
name='godfathers',
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, related_name='godchildren', blank=True),
model_name="user",
name="godfathers",
field=models.ManyToManyField(
to=settings.AUTH_USER_MODEL, related_name="godchildren", blank=True
),
)
]

View File

@ -7,19 +7,26 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('core', '0004_user_godfathers'),
]
dependencies = [("core", "0004_user_godfathers")]
operations = [
migrations.AddField(
model_name='page',
name='lock_timeout',
field=models.DateTimeField(verbose_name='lock_timeout', null=True, blank=True, default=None),
model_name="page",
name="lock_timeout",
field=models.DateTimeField(
verbose_name="lock_timeout", null=True, blank=True, default=None
),
),
migrations.AddField(
model_name='page',
name='lock_user',
field=models.ForeignKey(verbose_name='lock user', default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True, related_name='locked_pages'),
model_name="page",
name="lock_user",
field=models.ForeignKey(
verbose_name="lock user",
default=None,
blank=True,
to=settings.AUTH_USER_MODEL,
null=True,
related_name="locked_pages",
),
),
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_auto_20161105_1035'),
]
dependencies = [("core", "0005_auto_20161105_1035")]
operations = [
migrations.AddField(
model_name='sithfile',
name='is_moderated',
field=models.BooleanField(verbose_name='is moderated', default=False),
),
model_name="sithfile",
name="is_moderated",
field=models.BooleanField(verbose_name="is moderated", default=False),
)
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20161108_1703'),
]
dependencies = [("core", "0006_auto_20161108_1703")]
operations = [
migrations.AddField(
model_name='sithfile',
name='asked_for_removal',
field=models.BooleanField(default=False, verbose_name='asked for removal'),
),
model_name="sithfile",
name="asked_for_removal",
field=models.BooleanField(default=False, verbose_name="asked for removal"),
)
]

View File

@ -8,24 +8,39 @@ import core.models
class Migration(migrations.Migration):
dependencies = [
('core', '0008_sithfile_asked_for_removal'),
]
dependencies = [("core", "0008_sithfile_asked_for_removal")]
operations = [
migrations.AddField(
model_name='sithfile',
name='compressed',
field=models.FileField(upload_to=core.models.get_compressed_directory, null=True, verbose_name='compressed file', blank=True),
model_name="sithfile",
name="compressed",
field=models.FileField(
upload_to=core.models.get_compressed_directory,
null=True,
verbose_name="compressed file",
blank=True,
),
),
migrations.AddField(
model_name='sithfile',
name='thumbnail',
field=models.FileField(upload_to=core.models.get_thumbnail_directory, null=True, verbose_name='thumbnail', blank=True),
model_name="sithfile",
name="thumbnail",
field=models.FileField(
upload_to=core.models.get_thumbnail_directory,
null=True,
verbose_name="thumbnail",
blank=True,
),
),
migrations.AlterField(
model_name='user',
name='home',
field=models.OneToOneField(verbose_name='home', related_name='home_of', on_delete=django.db.models.deletion.SET_NULL, null=True, to='core.SithFile', blank=True),
model_name="user",
name="home",
field=models.OneToOneField(
verbose_name="home",
related_name="home_of",
on_delete=django.db.models.deletion.SET_NULL,
null=True,
to="core.SithFile",
blank=True,
),
),
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0009_auto_20161120_1155'),
]
dependencies = [("core", "0009_auto_20161120_1155")]
operations = [
migrations.AddField(
model_name='sithfile',
name='is_in_sas',
field=models.BooleanField(verbose_name='is in the SAS', default=False),
),
model_name="sithfile",
name="is_in_sas",
field=models.BooleanField(verbose_name="is in the SAS", default=False),
)
]

View File

@ -8,29 +8,47 @@ import core.models
class Migration(migrations.Migration):
dependencies = [
('core', '0010_sithfile_is_in_sas'),
]
dependencies = [("core", "0010_sithfile_is_in_sas")]
operations = [
migrations.AlterField(
model_name='sithfile',
name='compressed',
field=models.FileField(verbose_name='compressed file', upload_to=core.models.get_compressed_directory, null=True, blank=True, max_length=256),
model_name="sithfile",
name="compressed",
field=models.FileField(
verbose_name="compressed file",
upload_to=core.models.get_compressed_directory,
null=True,
blank=True,
max_length=256,
),
),
migrations.AlterField(
model_name='sithfile',
name='date',
field=models.DateTimeField(verbose_name='date', default=django.utils.timezone.now),
model_name="sithfile",
name="date",
field=models.DateTimeField(
verbose_name="date", default=django.utils.timezone.now
),
),
migrations.AlterField(
model_name='sithfile',
name='file',
field=models.FileField(verbose_name='file', upload_to=core.models.get_directory, null=True, blank=True, max_length=256),
model_name="sithfile",
name="file",
field=models.FileField(
verbose_name="file",
upload_to=core.models.get_directory,
null=True,
blank=True,
max_length=256,
),
),
migrations.AlterField(
model_name='sithfile',
name='thumbnail',
field=models.FileField(verbose_name='thumbnail', upload_to=core.models.get_thumbnail_directory, null=True, blank=True, max_length=256),
model_name="sithfile",
name="thumbnail",
field=models.FileField(
verbose_name="thumbnail",
upload_to=core.models.get_thumbnail_directory,
null=True,
blank=True,
max_length=256,
),
),
]

View File

@ -8,20 +8,49 @@ import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('core', '0011_auto_20161124_0848'),
]
dependencies = [("core", "0011_auto_20161124_0848")]
operations = [
migrations.CreateModel(
name='Notification',
name="Notification",
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('url', models.CharField(max_length=255, verbose_name='url')),
('text', models.CharField(max_length=512, verbose_name='text')),
('type', models.CharField(max_length=16, choices=[('FILE_MODERATION', 'File moderation'), ('SAS_MODERATION', 'SAS moderation'), ('NEW_PICTURES', 'New pictures')], verbose_name='text', null=True, blank=True)),
('date', models.DateTimeField(verbose_name='date', default=django.utils.timezone.now)),
('user', models.ForeignKey(related_name='notifications', to=settings.AUTH_USER_MODEL)),
],
(
"id",
models.AutoField(
primary_key=True,
verbose_name="ID",
auto_created=True,
serialize=False,
),
),
("url", models.CharField(max_length=255, verbose_name="url")),
("text", models.CharField(max_length=512, verbose_name="text")),
(
"type",
models.CharField(
max_length=16,
choices=[
("FILE_MODERATION", "File moderation"),
("SAS_MODERATION", "SAS moderation"),
("NEW_PICTURES", "New pictures"),
],
verbose_name="text",
null=True,
blank=True,
),
),
(
"date",
models.DateTimeField(
verbose_name="date", default=django.utils.timezone.now
),
),
(
"user",
models.ForeignKey(
related_name="notifications", to=settings.AUTH_USER_MODEL
),
),
],
)
]

View File

@ -6,23 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0012_notification'),
]
dependencies = [("core", "0012_notification")]
operations = [
migrations.RemoveField(
model_name='notification',
name='text',
migrations.RemoveField(model_name="notification", name="text"),
migrations.AddField(
model_name="notification",
name="param",
field=models.CharField(verbose_name="param", default="", max_length=128),
),
migrations.AddField(
model_name='notification',
name='param',
field=models.CharField(verbose_name='param', default='', max_length=128),
),
migrations.AddField(
model_name='notification',
name='viewed',
field=models.BooleanField(verbose_name='viewed', default=False),
model_name="notification",
name="viewed",
field=models.BooleanField(verbose_name="viewed", default=False),
),
]

View File

@ -6,14 +6,24 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20161209_2338'),
]
dependencies = [("core", "0013_auto_20161209_2338")]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(verbose_name='type', max_length=32, default='GENERIC', choices=[('FILE_MODERATION', 'New files to be moderated'), ('SAS_MODERATION', 'New pictures/album to be moderated in the SAS'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')]),
model_name="notification",
name="type",
field=models.CharField(
verbose_name="type",
max_length=32,
default="GENERIC",
choices=[
("FILE_MODERATION", "New files to be moderated"),
("SAS_MODERATION", "New pictures/album to be moderated in the SAS"),
("NEW_PICTURES", "You've been identified on some pictures"),
("REFILLING", "You just refilled of %s"),
("SELLING", "You just bought %s"),
("GENERIC", "You have a notification"),
],
),
)
]

View File

@ -7,15 +7,18 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('core', '0014_auto_20161210_0009'),
]
dependencies = [("core", "0014_auto_20161210_0009")]
operations = [
migrations.AddField(
model_name='sithfile',
name='moderator',
field=models.ForeignKey(related_name='moderated_files', verbose_name='owner', default=0, to=settings.AUTH_USER_MODEL),
preserve_default=False,
model_name="sithfile",
name="moderator",
field=models.ForeignKey(
related_name="moderated_files",
verbose_name="owner",
default=0,
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
)
]

View File

@ -7,14 +7,18 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('core', '0015_sithfile_moderator'),
]
dependencies = [("core", "0015_sithfile_moderator")]
operations = [
migrations.AlterField(
model_name='sithfile',
name='moderator',
field=models.ForeignKey(related_name='moderated_files', blank=True, null=True, to=settings.AUTH_USER_MODEL, verbose_name='owner'),
model_name="sithfile",
name="moderator",
field=models.ForeignKey(
related_name="moderated_files",
blank=True,
null=True,
to=settings.AUTH_USER_MODEL,
verbose_name="owner",
),
)
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0016_auto_20161212_1922'),
]
dependencies = [("core", "0016_auto_20161212_1922")]
operations = [
migrations.AlterField(
model_name='user',
name='last_update',
field=models.DateTimeField(verbose_name='last update', auto_now=True),
),
model_name="user",
name="last_update",
field=models.DateTimeField(verbose_name="last update", auto_now=True),
)
]

View File

@ -6,14 +6,25 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0017_auto_20161220_1626'),
]
dependencies = [("core", "0017_auto_20161220_1626")]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(choices=[('NEWS_MODERATION', 'A fresh new to be moderated'), ('FILE_MODERATION', 'New files to be moderated'), ('SAS_MODERATION', 'New pictures/album to be moderated in the SAS'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')], default='GENERIC', max_length=32, verbose_name='type'),
model_name="notification",
name="type",
field=models.CharField(
choices=[
("NEWS_MODERATION", "A fresh new to be moderated"),
("FILE_MODERATION", "New files to be moderated"),
("SAS_MODERATION", "New pictures/album to be moderated in the SAS"),
("NEW_PICTURES", "You've been identified on some pictures"),
("REFILLING", "You just refilled of %s"),
("SELLING", "You just bought %s"),
("GENERIC", "You have a notification"),
],
default="GENERIC",
max_length=32,
verbose_name="type",
),
)
]

View File

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0018_auto_20161224_0211'),
]
dependencies = [("core", "0018_auto_20161224_0211")]
operations = [
migrations.AddField(
model_name='preferences',
name='receive_weekmail',
field=models.BooleanField(default=False, verbose_name='do you want to receive the weekmail'),
model_name="preferences",
name="receive_weekmail",
field=models.BooleanField(
default=False, verbose_name="do you want to receive the weekmail"
),
)
]

View File

@ -7,18 +7,22 @@ import django.core.validators
class Migration(migrations.Migration):
dependencies = [
('core', '0019_preferences_receive_weekmail'),
]
dependencies = [("core", "0019_preferences_receive_weekmail")]
operations = [
migrations.AlterModelOptions(
name='group',
options={'ordering': ['name']},
),
migrations.AlterModelOptions(name="group", options={"ordering": ["name"]}),
migrations.AlterField(
model_name='page',
name='name',
field=models.CharField(validators=[django.core.validators.RegexValidator('^[A-z.+-]+$', 'Enter a valid page name. This value may contain only unaccented letters, numbers and ./+/-/_ characters.')], max_length=30, verbose_name='page unix name'),
model_name="page",
name="name",
field=models.CharField(
validators=[
django.core.validators.RegexValidator(
"^[A-z.+-]+$",
"Enter a valid page name. This value may contain only unaccented letters, numbers and ./+/-/_ characters.",
)
],
max_length=30,
verbose_name="page unix name",
),
),
]

View File

@ -6,14 +6,26 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0020_auto_20170324_0917'),
]
dependencies = [("core", "0020_auto_20170324_0917")]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(verbose_name='type', default='GENERIC', max_length=32, choices=[('MAILING_MODERATION', 'A new mailing list neet to be moderated'), ('NEWS_MODERATION', 'A fresh new to be moderated'), ('FILE_MODERATION', 'New files to be moderated'), ('SAS_MODERATION', 'New pictures/album to be moderated in the SAS'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')]),
model_name="notification",
name="type",
field=models.CharField(
verbose_name="type",
default="GENERIC",
max_length=32,
choices=[
("MAILING_MODERATION", "A new mailing list neet to be moderated"),
("NEWS_MODERATION", "A fresh new to be moderated"),
("FILE_MODERATION", "New files to be moderated"),
("SAS_MODERATION", "New pictures/album to be moderated in the SAS"),
("NEW_PICTURES", "You've been identified on some pictures"),
("REFILLING", "You just refilled of %s"),
("SELLING", "You just bought %s"),
("GENERIC", "You have a notification"),
],
),
)
]

View File

@ -6,14 +6,26 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0021_auto_20170822_1529'),
]
dependencies = [("core", "0021_auto_20170822_1529")]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(choices=[('MAILING_MODERATION', 'A new mailing list needs to be moderated'), ('NEWS_MODERATION', 'A fresh new to be moderated'), ('FILE_MODERATION', 'New files to be moderated'), ('SAS_MODERATION', 'New pictures/album to be moderated in the SAS'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')], default='GENERIC', max_length=32, verbose_name='type'),
model_name="notification",
name="type",
field=models.CharField(
choices=[
("MAILING_MODERATION", "A new mailing list needs to be moderated"),
("NEWS_MODERATION", "A fresh new to be moderated"),
("FILE_MODERATION", "New files to be moderated"),
("SAS_MODERATION", "New pictures/album to be moderated in the SAS"),
("NEW_PICTURES", "You've been identified on some pictures"),
("REFILLING", "You just refilled of %s"),
("SELLING", "You just bought %s"),
("GENERIC", "You have a notification"),
],
default="GENERIC",
max_length=32,
verbose_name="type",
),
)
]

View File

@ -7,29 +7,35 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('core', '0022_auto_20170822_2232'),
]
dependencies = [("core", "0022_auto_20170822_2232")]
operations = [
migrations.AddField(
model_name='preferences',
name='notify_on_click',
field=models.BooleanField(verbose_name='get a notification for every click', default=False),
model_name="preferences",
name="notify_on_click",
field=models.BooleanField(
verbose_name="get a notification for every click", default=False
),
),
migrations.AddField(
model_name='preferences',
name='notify_on_refill',
field=models.BooleanField(verbose_name='get a notification for every refilling', default=False),
model_name="preferences",
name="notify_on_refill",
field=models.BooleanField(
verbose_name="get a notification for every refilling", default=False
),
),
migrations.AlterField(
model_name='preferences',
name='show_my_stats',
field=models.BooleanField(verbose_name='show your stats to others', default=False),
model_name="preferences",
name="show_my_stats",
field=models.BooleanField(
verbose_name="show your stats to others", default=False
),
),
migrations.AlterField(
model_name='preferences',
name='user',
field=models.OneToOneField(related_name='_preferences', to=settings.AUTH_USER_MODEL),
model_name="preferences",
name="user",
field=models.OneToOneField(
related_name="_preferences", to=settings.AUTH_USER_MODEL
),
),
]

View File

@ -6,14 +6,26 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0023_auto_20170902_1226'),
]
dependencies = [("core", "0023_auto_20170902_1226")]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(choices=[('MAILING_MODERATION', 'A new mailing list needs to be moderated'), ('NEWS_MODERATION', 'There are %s fresh news to be moderated'), ('FILE_MODERATION', 'New files to be moderated'), ('SAS_MODERATION', 'New pictures/album to be moderated in the SAS'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')], verbose_name='type', default='GENERIC', max_length=32),
model_name="notification",
name="type",
field=models.CharField(
choices=[
("MAILING_MODERATION", "A new mailing list needs to be moderated"),
("NEWS_MODERATION", "There are %s fresh news to be moderated"),
("FILE_MODERATION", "New files to be moderated"),
("SAS_MODERATION", "New pictures/album to be moderated in the SAS"),
("NEW_PICTURES", "You've been identified on some pictures"),
("REFILLING", "You just refilled of %s"),
("SELLING", "You just bought %s"),
("GENERIC", "You have a notification"),
],
verbose_name="type",
default="GENERIC",
max_length=32,
),
)
]

View File

@ -7,14 +7,21 @@ import django.core.validators
class Migration(migrations.Migration):
dependencies = [
('core', '0024_auto_20170906_1317'),
]
dependencies = [("core", "0024_auto_20170906_1317")]
operations = [
migrations.AlterField(
model_name='page',
name='name',
field=models.CharField(max_length=30, verbose_name='page unix name', validators=[django.core.validators.RegexValidator('^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$', 'Enter a valid page name. This value may contain only unaccented letters, numbers and ./+/-/_ characters.')]),
model_name="page",
name="name",
field=models.CharField(
max_length=30,
verbose_name="page unix name",
validators=[
django.core.validators.RegexValidator(
"^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$",
"Enter a valid page name. This value may contain only unaccented letters, numbers and ./+/-/_ characters.",
)
],
),
)
]

View File

@ -6,14 +6,29 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0025_auto_20170919_1521'),
]
dependencies = [("core", "0025_auto_20170919_1521")]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(choices=[('MAILING_MODERATION', 'A new mailing list needs to be moderated'), ('NEWS_MODERATION', 'There are %s fresh news to be moderated'), ('FILE_MODERATION', 'New files to be moderated'), ('SAS_MODERATION', 'There are %s pictures to be moderated in the SAS'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')], verbose_name='type', max_length=32, default='GENERIC'),
model_name="notification",
name="type",
field=models.CharField(
choices=[
("MAILING_MODERATION", "A new mailing list needs to be moderated"),
("NEWS_MODERATION", "There are %s fresh news to be moderated"),
("FILE_MODERATION", "New files to be moderated"),
(
"SAS_MODERATION",
"There are %s pictures to be moderated in the SAS",
),
("NEW_PICTURES", "You've been identified on some pictures"),
("REFILLING", "You just refilled of %s"),
("SELLING", "You just bought %s"),
("GENERIC", "You have a notification"),
],
verbose_name="type",
max_length=32,
default="GENERIC",
),
)
]

View File

@ -8,18 +8,34 @@ import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('core', '0026_auto_20170926_1512'),
]
dependencies = [("core", "0026_auto_20170926_1512")]
operations = [
migrations.CreateModel(
name='Gift',
name="Gift",
fields=[
('id', models.AutoField(primary_key=True, auto_created=True, verbose_name='ID', serialize=False)),
('label', models.CharField(max_length=255, verbose_name='label')),
('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date')),
('user', models.ForeignKey(related_name='gifts', to=settings.AUTH_USER_MODEL)),
],
(
"id",
models.AutoField(
primary_key=True,
auto_created=True,
verbose_name="ID",
serialize=False,
),
),
("label", models.CharField(max_length=255, verbose_name="label")),
(
"date",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date"
),
),
(
"user",
models.ForeignKey(
related_name="gifts", to=settings.AUTH_USER_MODEL
),
),
],
)
]

View File

@ -6,14 +6,30 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0027_gift'),
]
dependencies = [("core", "0027_gift")]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(default='GENERIC', verbose_name='type', max_length=32, choices=[('POSTER_MODERATION', 'A new poster needs to be moderated'), ('MAILING_MODERATION', 'A new mailing list needs to be moderated'), ('NEWS_MODERATION', 'There are %s fresh news to be moderated'), ('FILE_MODERATION', 'New files to be moderated'), ('SAS_MODERATION', 'There are %s pictures to be moderated in the SAS'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')]),
model_name="notification",
name="type",
field=models.CharField(
default="GENERIC",
verbose_name="type",
max_length=32,
choices=[
("POSTER_MODERATION", "A new poster needs to be moderated"),
("MAILING_MODERATION", "A new mailing list needs to be moderated"),
("NEWS_MODERATION", "There are %s fresh news to be moderated"),
("FILE_MODERATION", "New files to be moderated"),
(
"SAS_MODERATION",
"There are %s pictures to be moderated in the SAS",
),
("NEW_PICTURES", "You've been identified on some pictures"),
("REFILLING", "You just refilled of %s"),
("SELLING", "You just bought %s"),
("GENERIC", "You have a notification"),
],
),
)
]

View File

@ -7,14 +7,17 @@ import core.models
class Migration(migrations.Migration):
dependencies = [
('core', '0028_auto_20171216_2044'),
]
dependencies = [("core", "0028_auto_20171216_2044")]
operations = [
migrations.AlterField(
model_name='page',
name='owner_group',
field=models.ForeignKey(verbose_name='owner group', default=core.models.Page.get_default_owner_group, related_name='owned_page', to='core.Group'),
model_name="page",
name="owner_group",
field=models.ForeignKey(
verbose_name="owner group",
default=core.models.Page.get_default_owner_group,
related_name="owned_page",
to="core.Group",
),
)
]

File diff suppressed because it is too large Load Diff

View File

@ -46,5 +46,5 @@ class PsqlRunOnly(migrations.RunSQL):
"""
def _run_sql(self, schema_editor, sqls):
if connection.vendor == 'postgresql':
if connection.vendor == "postgresql":
super(PsqlRunOnly, self)._run_sql(schema_editor, sqls)

View File

@ -34,21 +34,20 @@ class ScssFinder(FileSystemFinder):
"""
Find static *.css files compiled on the fly
"""
locations = []
def __init__(self, apps=None, *args, **kwargs):
location = settings.STATIC_ROOT
if not os.path.isdir(location):
return
self.locations = [
('', location),
]
self.locations = [("", location)]
self.storages = OrderedDict()
filesystem_storage = FileSystemStorage(location=location)
filesystem_storage.prefix = self.locations[0][0]
self.storages[location] = filesystem_storage
def find(self, path, all=False):
if path.endswith('.css'):
if path.endswith(".css"):
return super(ScssFinder, self).find(path, all)
return []

View File

@ -39,7 +39,8 @@ class ScssProcessor(object):
Else : give the path of the corresponding css supposed to already be compiled
Don't forget to use compilestatics to compile scss for production
"""
prefix = iri_to_uri(getattr(settings, 'STATIC_URL', '/static/'))
prefix = iri_to_uri(getattr(settings, "STATIC_URL", "/static/"))
storage = ScssFileStorage()
scss_extensions = [".scss"]
@ -63,7 +64,7 @@ class ScssProcessor(object):
"include_paths": settings.SASS_INCLUDE_FOLDERS,
}
if settings.SASS_PRECISION:
compile_args['precision'] = settings.SASS_PRECISION
compile_args["precision"] = settings.SASS_PRECISION
content = sass.compile(**compile_args)
content = force_bytes(content)

View File

@ -21,4 +21,3 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -38,11 +38,11 @@ register = template.Library()
@register.filter(is_safe=False)
@stringfilter
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(name='phonenumber')
def phonenumber(value, country='FR',
format=phonenumbers.PhoneNumberFormat.NATIONAL):
@register.filter(name="phonenumber")
def phonenumber(value, country="FR", format=phonenumbers.PhoneNumberFormat.NATIONAL):
"""
This filter is kindly borrowed from https://github.com/foundertherapy/django-phonenumber-filter
"""
@ -53,13 +53,37 @@ def phonenumber(value, country='FR',
except phonenumbers.NumberParseException as e:
return value
@register.filter()
@stringfilter
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.
"""
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
for py, php in python2PHP.items():

View File

@ -49,203 +49,261 @@ class UserRegistrationTest(TestCase):
Should register a user correctly
"""
c = Client()
response = c.post(reverse('core:register'), {'first_name': 'Guy',
'last_name': 'Carlier',
'email': 'guy@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(
reverse("core:register"),
{
"first_name": "Guy",
"last_name": "Carlier",
"email": "guy@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
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))
def test_register_user_form_fail_password(self):
"""
Should not register a user correctly
"""
c = Client()
response = c.post(reverse('core:register'), {'first_name': 'Guy',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop2',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(
reverse("core:register"),
{
"first_name": "Guy",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop2",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
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))
def test_register_user_form_fail_email(self):
"""
Should not register a user correctly
"""
c = Client()
response = c.post(reverse('core:register'), {'first_name': 'Guy',
'last_name': 'Carlier',
'email': 'bibou.git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(
reverse("core:register"),
{
"first_name": "Guy",
"last_name": "Carlier",
"email": "bibou.git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
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))
def test_register_user_form_fail_missing_name(self):
"""
Should not register a user correctly
"""
c = Client()
response = c.post(reverse('core:register'), {'first_name': 'Guy',
'last_name': '',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(
reverse("core:register"),
{
"first_name": "Guy",
"last_name": "",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
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))
def test_register_user_form_fail_missing_date_of_birth(self):
"""
Should not register a user correctly
"""
c = Client()
response = c.post(reverse('core:register'), {'first_name': '',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(
reverse("core:register"),
{
"first_name": "",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
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))
def test_register_user_form_fail_missing_first_name(self):
"""
Should not register a user correctly
"""
c = Client()
response = c.post(reverse('core:register'), {'first_name': '',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(
reverse("core:register"),
{
"first_name": "",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
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))
def test_register_user_form_fail_wrong_captcha(self):
"""
Should not register a user correctly
"""
c = Client()
response = c.post(reverse('core:register'), {'first_name': 'Bibou',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'WRONG_CAPTCHA'
})
response = c.post(
reverse("core:register"),
{
"first_name": "Bibou",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "WRONG_CAPTCHA",
},
)
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))
def test_register_user_form_fail_already_exists(self):
"""
Should not register a user correctly
"""
c = Client()
c.post(reverse('core:register'), {'first_name': 'Guy',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(reverse('core:register'), {'first_name': 'Bibou',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
c.post(
reverse("core:register"),
{
"first_name": "Guy",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
response = c.post(
reverse("core:register"),
{
"first_name": "Bibou",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
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))
def test_login_success(self):
"""
Should login a user correctly
"""
c = Client()
c.post(reverse('core:register'), {'first_name': 'Guy',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(reverse('core:login'), {'username': 'gcarlier', 'password': 'plop'})
c.post(
reverse("core:register"),
{
"first_name": "Guy",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
response = c.post(
reverse("core:login"), {"username": "gcarlier", "password": "plop"}
)
self.assertTrue(response.status_code == 302)
#self.assertTrue('Hello, world' in str(response.content))
# self.assertTrue('Hello, world' in str(response.content))
def test_login_fail(self):
"""
Should not login a user correctly
"""
c = Client()
c.post(reverse('core:register'), {'first_name': 'Guy',
'last_name': 'Carlier',
'email': 'bibou@git.an',
'date_of_birth': '12/6/1942',
'password1': 'plop',
'password2': 'plop',
'captcha_0': 'dummy-value',
'captcha_1': 'PASSED'
})
response = c.post(reverse('core:login'), {'username': 'gcarlier', 'password': 'guy'})
c.post(
reverse("core:register"),
{
"first_name": "Guy",
"last_name": "Carlier",
"email": "bibou@git.an",
"date_of_birth": "12/6/1942",
"password1": "plop",
"password2": "plop",
"captcha_0": "dummy-value",
"captcha_1": "PASSED",
},
)
response = c.post(
reverse("core:login"), {"username": "gcarlier", "password": "guy"}
)
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 MarkdownTest(TestCase):
def test_full_markdown_syntax(self):
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
with open(os.path.join(root_path) + '/doc/SYNTAX.md', 'r') as md_file:
with open(os.path.join(root_path) + "/doc/SYNTAX.md", "r") as md_file:
md = md_file.read()
with open(os.path.join(root_path) + '/doc/SYNTAX.html', 'r') as html_file:
with open(os.path.join(root_path) + "/doc/SYNTAX.html", "r") as html_file:
html = html_file.read()
result = markdown(md)
self.assertTrue(result == html)
class PageHandlingTest(TestCase):
def setUp(self):
try:
Group.objects.create(name="root")
u = User(username='root', last_name="", first_name="Bibou",
u = User(
username="root",
last_name="",
first_name="Bibou",
email="ae.info@utbm.fr",
date_of_birth="1942-06-12",
is_superuser=True, is_staff=True)
is_superuser=True,
is_staff=True,
)
u.set_password("plop")
u.save()
self.client.login(username='root', password='plop')
self.client.login(username="root", password="plop")
except Exception as e:
print(e)
@ -253,12 +311,10 @@ class PageHandlingTest(TestCase):
"""
Should create a page correctly
"""
self.client.post(reverse('core:page_new'), {
'parent': '',
'name': 'guy',
'owner_group': 1,
})
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy'}))
self.client.post(
reverse("core:page_new"), {"parent": "", "name": "guy", "owner_group": 1}
)
response = self.client.get(reverse("core:page", kwargs={"page_name": "guy"}))
self.assertTrue(response.status_code == 200)
self.assertTrue('<a href="/page/guy/hist">' in str(response.content))
@ -266,17 +322,16 @@ class PageHandlingTest(TestCase):
"""
Should create a page correctly
"""
self.client.post(reverse('core:page_new'), {
'parent': '',
'name': 'guy',
'owner_group': '1',
})
response = self.client.post(reverse('core:page_new'), {
'parent': '1',
'name': 'bibou',
'owner_group': '1',
})
response = self.client.get(reverse('core:page', kwargs={'page_name': 'guy/bibou'}))
self.client.post(
reverse("core:page_new"), {"parent": "", "name": "guy", "owner_group": "1"}
)
response = self.client.post(
reverse("core:page_new"),
{"parent": "1", "name": "bibou", "owner_group": "1"},
)
response = self.client.get(
reverse("core:page", kwargs={"page_name": "guy/bibou"})
)
self.assertTrue(response.status_code == 200)
self.assertTrue('<a href="/page/guy/bibou/">' in str(response.content))
@ -286,17 +341,24 @@ class PageHandlingTest(TestCase):
"""
parent = Page(name="guy", owner_group=Group.objects.filter(id=1).first())
parent.save(force_lock=True)
page = Page(name="bibou", owner_group=Group.objects.filter(id=1).first(), parent=parent)
page = Page(
name="bibou", owner_group=Group.objects.filter(id=1).first(), parent=parent
)
page.save(force_lock=True)
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('<a href="/page/guy/bibou/edit">\\xc3\\x89diter</a>' in str(response.content))
self.assertTrue(
'<a href="/page/guy/bibou/edit">\\xc3\\x89diter</a>'
in str(response.content)
)
def test_access_page_not_found(self):
"""
Should not display a page correctly
"""
response = self.client.get(reverse('core:page', kwargs={'page_name': 'swagg'}))
response = self.client.get(reverse("core:page", kwargs={"page_name": "swagg"}))
response = self.client.get("/page/swagg/")
self.assertTrue(response.status_code == 200)
self.assertTrue('<a href="/page/create?page=swagg">' in str(response.content))
@ -305,15 +367,14 @@ class PageHandlingTest(TestCase):
"""
Should format the markdown and escape html correctly
"""
self.client.post(reverse('core:page_new'), {
'parent': '',
'name': 'guy',
'owner_group': '1',
})
self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
'title': 'Bibou',
'content':
'''Guy *bibou*
self.client.post(
reverse("core:page_new"), {"parent": "", "name": "guy", "owner_group": "1"}
)
self.client.post(
reverse("core:page_edit", kwargs={"page_name": "guy"}),
{
"title": "Bibou",
"content": """Guy *bibou*
http://git.an
@ -322,13 +383,18 @@ http://git.an
<guy>Bibou</guy>
<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('<p>Guy <em>bibou</em></p>\\n<p><a href="http://git.an">http://git.an</a></p>\\n' +
'<h1>Swag</h1>\\n&lt;guy&gt;Bibou&lt;/guy&gt;' +
"&lt;script&gt;alert(\\'Guy\\');&lt;/script&gt;" in str(response.content))
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&lt;guy&gt;Bibou&lt;/guy&gt;"
+ "&lt;script&gt;alert(\\'Guy\\');&lt;/script&gt;"
in str(response.content)
)
# TODO: many tests on the pages:
# - renaming a page
@ -341,23 +407,33 @@ class FileHandlingTest(TestCase):
try:
call_command("populate")
self.subscriber = User.objects.filter(username="subscriber").first()
self.client.login(username='subscriber', password='plop')
self.client.login(username="subscriber", password="plop")
except Exception as e:
print(e)
def test_create_folder_home(self):
response = self.client.post(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
{"folder_name": "GUY_folder_test"})
response = self.client.post(
reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
{"folder_name": "GUY_folder_test"},
)
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("GUY_folder_test</a>" in str(response.content))
def test_upload_file_home(self):
with open("/bin/ls", "rb") as f:
response = self.client.post(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
{"file_field": f})
response = self.client.post(
reverse(
"core:file_detail", kwargs={"file_id": self.subscriber.home.id}
),
{"file_field": f},
)
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("ls</a>" in str(response.content))

View File

@ -28,73 +28,181 @@ from django.conf.urls import url
from core.views import *
urlpatterns = [
url(r'^$', index, name='index'),
url(r'^to_markdown$', ToMarkdownView.as_view(), name='to_markdown'),
url(r'^notifications$', NotificationList.as_view(), name='notification_list'),
url(r'^notification/(?P<notif_id>[0-9]+)$', notification, name='notification'),
url(r"^$", index, name="index"),
url(r"^to_markdown$", ToMarkdownView.as_view(), name="to_markdown"),
url(r"^notifications$", NotificationList.as_view(), name="notification_list"),
url(r"^notification/(?P<notif_id>[0-9]+)$", notification, name="notification"),
# Search
url(r'^search/$', search_view, name='search'),
url(r'^search_json/$', search_json, name='search_json'),
url(r'^search_user/$', search_user_json, name='search_user'),
url(r"^search/$", search_view, name="search"),
url(r"^search_json/$", search_json, name="search_json"),
url(r"^search_user/$", search_user_json, name="search_user"),
# Login and co
url(r'^login/$', login, name='login'),
url(r'^logout/$', logout, name='logout'),
url(r'^password_change/$', password_change, name='password_change'),
url(r'^password_change/(?P<user_id>[0-9]+)$', password_root_change, name='password_root_change'),
url(r'^password_change/done$', password_change_done, name='password_change_done'),
url(r'^password_reset/$', password_reset, name='password_reset'),
url(r'^password_reset/done$', password_reset_done, name='password_reset_done'),
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', password_reset_confirm, name='password_reset_confirm'),
url(r'^reset/done/$', password_reset_complete, name='password_reset_complete'),
url(r'^register$', register, name='register'),
url(r"^login/$", login, name="login"),
url(r"^logout/$", logout, name="logout"),
url(r"^password_change/$", password_change, name="password_change"),
url(
r"^password_change/(?P<user_id>[0-9]+)$",
password_root_change,
name="password_root_change",
),
url(r"^password_change/done$", password_change_done, name="password_change_done"),
url(r"^password_reset/$", password_reset, name="password_reset"),
url(r"^password_reset/done$", password_reset_done, name="password_reset_done"),
url(
r"^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$",
password_reset_confirm,
name="password_reset_confirm",
),
url(r"^reset/done/$", password_reset_complete, name="password_reset_complete"),
url(r"^register$", register, name="register"),
# Group handling
url(r'^group/$', GroupListView.as_view(), name='group_list'),
url(r'^group/new$', GroupCreateView.as_view(), name='group_new'),
url(r'^group/(?P<group_id>[0-9]+)/$', GroupEditView.as_view(), name='group_edit'),
url(r'^group/(?P<group_id>[0-9]+)/delete$', GroupDeleteView.as_view(), name='group_delete'),
url(r"^group/$", GroupListView.as_view(), name="group_list"),
url(r"^group/new$", GroupCreateView.as_view(), name="group_new"),
url(r"^group/(?P<group_id>[0-9]+)/$", GroupEditView.as_view(), name="group_edit"),
url(
r"^group/(?P<group_id>[0-9]+)/delete$",
GroupDeleteView.as_view(),
name="group_delete",
),
# User views
url(r'^user/$', UserListView.as_view(), name='user_list'),
url(r'^user/(?P<user_id>[0-9]+)/mini$', UserMiniView.as_view(), name='user_profile_mini'),
url(r'^user/(?P<user_id>[0-9]+)/$', UserView.as_view(), name='user_profile'),
url(r'^user/(?P<user_id>[0-9]+)/pictures$', UserPicturesView.as_view(), name='user_pictures'),
url(r'^user/(?P<user_id>[0-9]+)/godfathers$', UserGodfathersView.as_view(), name='user_godfathers'),
url(r'^user/(?P<user_id>[0-9]+)/godfathers/tree$', UserGodfathersTreeView.as_view(), name='user_godfathers_tree'),
url(r'^user/(?P<user_id>[0-9]+)/godfathers/tree/pict$', UserGodfathersTreePictureView.as_view(), name='user_godfathers_tree_pict'),
url(r'^user/(?P<user_id>[0-9]+)/godfathers/(?P<godfather_id>[0-9]+)/(?P<is_father>(True)|(False))/delete$', DeleteUserGodfathers, name='user_godfathers_delete'),
url(r'^user/(?P<user_id>[0-9]+)/edit$', UserUpdateProfileView.as_view(), name='user_edit'),
url(r'^user/(?P<user_id>[0-9]+)/profile_upload$', UserUploadProfilePictView.as_view(), name='user_profile_upload'),
url(r'^user/(?P<user_id>[0-9]+)/clubs$', UserClubView.as_view(), name='user_clubs'),
url(r'^user/(?P<user_id>[0-9]+)/prefs$', UserPreferencesView.as_view(), name='user_prefs'),
url(r'^user/(?P<user_id>[0-9]+)/groups$', UserUpdateGroupView.as_view(), name='user_groups'),
url(r'^user/tools/$', UserToolsView.as_view(), name='user_tools'),
url(r'^user/(?P<user_id>[0-9]+)/account$', UserAccountView.as_view(), name='user_account'),
url(r'^user/(?P<user_id>[0-9]+)/account/(?P<year>[0-9]+)/(?P<month>[0-9]+)$', UserAccountDetailView.as_view(), name='user_account_detail'),
url(r'^user/(?P<user_id>[0-9]+)/stats$', UserStatsView.as_view(), name='user_stats'),
url(r'^user/(?P<user_id>[0-9]+)/gift/create$', GiftCreateView.as_view(), name='user_gift_create'),
url(r'^user/(?P<user_id>[0-9]+)/gift/delete/(?P<gift_id>[0-9]+)/$', GiftDeleteView.as_view(), name='user_gift_delete'),
url(r"^user/$", UserListView.as_view(), name="user_list"),
url(
r"^user/(?P<user_id>[0-9]+)/mini$",
UserMiniView.as_view(),
name="user_profile_mini",
),
url(r"^user/(?P<user_id>[0-9]+)/$", UserView.as_view(), name="user_profile"),
url(
r"^user/(?P<user_id>[0-9]+)/pictures$",
UserPicturesView.as_view(),
name="user_pictures",
),
url(
r"^user/(?P<user_id>[0-9]+)/godfathers$",
UserGodfathersView.as_view(),
name="user_godfathers",
),
url(
r"^user/(?P<user_id>[0-9]+)/godfathers/tree$",
UserGodfathersTreeView.as_view(),
name="user_godfathers_tree",
),
url(
r"^user/(?P<user_id>[0-9]+)/godfathers/tree/pict$",
UserGodfathersTreePictureView.as_view(),
name="user_godfathers_tree_pict",
),
url(
r"^user/(?P<user_id>[0-9]+)/godfathers/(?P<godfather_id>[0-9]+)/(?P<is_father>(True)|(False))/delete$",
DeleteUserGodfathers,
name="user_godfathers_delete",
),
url(
r"^user/(?P<user_id>[0-9]+)/edit$",
UserUpdateProfileView.as_view(),
name="user_edit",
),
url(
r"^user/(?P<user_id>[0-9]+)/profile_upload$",
UserUploadProfilePictView.as_view(),
name="user_profile_upload",
),
url(r"^user/(?P<user_id>[0-9]+)/clubs$", UserClubView.as_view(), name="user_clubs"),
url(
r"^user/(?P<user_id>[0-9]+)/prefs$",
UserPreferencesView.as_view(),
name="user_prefs",
),
url(
r"^user/(?P<user_id>[0-9]+)/groups$",
UserUpdateGroupView.as_view(),
name="user_groups",
),
url(r"^user/tools/$", UserToolsView.as_view(), name="user_tools"),
url(
r"^user/(?P<user_id>[0-9]+)/account$",
UserAccountView.as_view(),
name="user_account",
),
url(
r"^user/(?P<user_id>[0-9]+)/account/(?P<year>[0-9]+)/(?P<month>[0-9]+)$",
UserAccountDetailView.as_view(),
name="user_account_detail",
),
url(
r"^user/(?P<user_id>[0-9]+)/stats$", UserStatsView.as_view(), name="user_stats"
),
url(
r"^user/(?P<user_id>[0-9]+)/gift/create$",
GiftCreateView.as_view(),
name="user_gift_create",
),
url(
r"^user/(?P<user_id>[0-9]+)/gift/delete/(?P<gift_id>[0-9]+)/$",
GiftDeleteView.as_view(),
name="user_gift_delete",
),
# File views
# url(r'^file/add/(?P<popup>popup)?$', FileCreateView.as_view(), name='file_new'),
url(r'^file/(?P<popup>popup)?$', FileListView.as_view(), name='file_list'),
url(r'^file/(?P<file_id>[0-9]+)/(?P<popup>popup)?$', FileView.as_view(), name='file_detail'),
url(r'^file/(?P<file_id>[0-9]+)/edit/(?P<popup>popup)?$', FileEditView.as_view(), name='file_edit'),
url(r'^file/(?P<file_id>[0-9]+)/prop/(?P<popup>popup)?$', FileEditPropView.as_view(), name='file_prop'),
url(r'^file/(?P<file_id>[0-9]+)/delete/(?P<popup>popup)?$', FileDeleteView.as_view(), name='file_delete'),
url(r'^file/moderation$', FileModerationView.as_view(), name='file_moderation'),
url(r'^file/(?P<file_id>[0-9]+)/moderate$', FileModerateView.as_view(), name='file_moderate'),
url(r'^file/(?P<file_id>[0-9]+)/download$', send_file, name='download'),
url(r"^file/(?P<popup>popup)?$", FileListView.as_view(), name="file_list"),
url(
r"^file/(?P<file_id>[0-9]+)/(?P<popup>popup)?$",
FileView.as_view(),
name="file_detail",
),
url(
r"^file/(?P<file_id>[0-9]+)/edit/(?P<popup>popup)?$",
FileEditView.as_view(),
name="file_edit",
),
url(
r"^file/(?P<file_id>[0-9]+)/prop/(?P<popup>popup)?$",
FileEditPropView.as_view(),
name="file_prop",
),
url(
r"^file/(?P<file_id>[0-9]+)/delete/(?P<popup>popup)?$",
FileDeleteView.as_view(),
name="file_delete",
),
url(r"^file/moderation$", FileModerationView.as_view(), name="file_moderation"),
url(
r"^file/(?P<file_id>[0-9]+)/moderate$",
FileModerateView.as_view(),
name="file_moderate",
),
url(r"^file/(?P<file_id>[0-9]+)/download$", send_file, name="download"),
# Page views
url(r'^page/$', PageListView.as_view(), name='page_list'),
url(r'^page/create$', PageCreateView.as_view(), name='page_new'),
url(r'^page/(?P<page_id>[0-9]*)/delete$', PageDeleteView.as_view(), name='page_delete'),
url(r'^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/edit$', PageEditView.as_view(), name='page_edit'),
url(r'^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/prop$', PagePropView.as_view(), name='page_prop'),
url(r'^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/hist$', PageHistView.as_view(), name='page_hist'),
url(r'^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/rev/(?P<rev>[0-9]+)/', PageRevView.as_view(), name='page_rev'),
url(r'^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/$', PageView.as_view(), name='page'),
url(r"^page/$", PageListView.as_view(), name="page_list"),
url(r"^page/create$", PageCreateView.as_view(), name="page_new"),
url(
r"^page/(?P<page_id>[0-9]*)/delete$",
PageDeleteView.as_view(),
name="page_delete",
),
url(
r"^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/edit$",
PageEditView.as_view(),
name="page_edit",
),
url(
r"^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/prop$",
PagePropView.as_view(),
name="page_prop",
),
url(
r"^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/hist$",
PageHistView.as_view(),
name="page_hist",
),
url(
r"^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/rev/(?P<rev>[0-9]+)/",
PageRevView.as_view(),
name="page_rev",
),
url(
r"^page/(?P<page_name>([/a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])+)/$",
PageView.as_view(),
name="page",
),
]

Some files were not shown because too many files have changed in this diff Show More