Merge branch 'develop' into 'master'
Prod deploy: Webhooks (new TG bot setup) See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!55
This commit is contained in:
@@ -3,7 +3,6 @@ SENTRY_DSN=
|
||||
HOST=api.dev.sahkoinsinoorikilta.fi
|
||||
DEBUG=True
|
||||
SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
|
||||
TG_BOT_TOKEN=
|
||||
DB_NAME=postgres
|
||||
DB_USER=postgres
|
||||
DB_PASSWD=postgres
|
||||
|
||||
@@ -3,7 +3,6 @@ DEPLOY_ENV=local
|
||||
HOST=localhost
|
||||
DEBUG=True
|
||||
SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
|
||||
TG_BOT_TOKEN=
|
||||
DB_NAME=postgres
|
||||
DB_USER=postgres
|
||||
DB_PASSWD=postgres
|
||||
|
||||
+3
-3
@@ -53,8 +53,8 @@ lint:py:
|
||||
stage: lint
|
||||
needs: []
|
||||
script:
|
||||
- pip install pycodestyle
|
||||
- pycodestyle --config=pycodestyle.cfg --count .
|
||||
- pip install black==21.12b0
|
||||
- black --check .
|
||||
|
||||
lint:js:
|
||||
image: node:14
|
||||
@@ -114,7 +114,7 @@ deploy:production:
|
||||
- master
|
||||
environment:
|
||||
name: production
|
||||
url: api.sahkoinsinoorikilta.fi
|
||||
url: https://api.sahkoinsinoorikilta.fi
|
||||
when: manual
|
||||
variables:
|
||||
DOCKER_HOST: $CI_DOCKER_HOST
|
||||
|
||||
+23
-11
@@ -32,26 +32,34 @@ git checkout develop
|
||||
|
||||
### Poetry
|
||||
|
||||
For depedencies and virtual environment, we use [poetry](https://python-poetry.org/). The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with
|
||||
For depedencies and virtual environment, we use [poetry](https://python-poetry.org/).
|
||||
|
||||
```bash
|
||||
poetry config virtualenvs.in-project true
|
||||
python3 -m pip install poetry
|
||||
```
|
||||
|
||||
The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with CMD
|
||||
|
||||
```bash
|
||||
python3 -m poetry config virtualenvs.in-project true
|
||||
```
|
||||
|
||||
Start developing by install dependencies first
|
||||
|
||||
#### CMDs
|
||||
|
||||
Activate virtual environment in shell
|
||||
|
||||
```bash
|
||||
python3 -m poetry shell
|
||||
```
|
||||
|
||||
Install dependencies
|
||||
|
||||
```bash
|
||||
poetry install
|
||||
```
|
||||
|
||||
Activate virtual environment in shell
|
||||
|
||||
```bash
|
||||
poetry shell
|
||||
```
|
||||
|
||||
### npm scripts
|
||||
|
||||
We use Node.js for few development tasks, like linting. Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm).
|
||||
@@ -71,15 +79,19 @@ python manage.py createdummydata # creates dummy members to the member regis
|
||||
### Running
|
||||
|
||||
```bash
|
||||
python manage.py runserver 0.0.0.0:8000
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
Using address `0.0.0.0` will bind to all IP addresses. Using `localhost` will only bind to your machine.
|
||||
|
||||
#### Visit the page
|
||||
|
||||
Visit [https://localhost:8000](https://localhost:8000) in your browser!
|
||||
|
||||
Using address `0.0.0.0` will bind to all IP addresses. Using `localhost` will only bind to your machine.
|
||||
|
||||
```bash
|
||||
python manage.py runserver 0.0.0.0:8000
|
||||
```
|
||||
|
||||
### Development workflow
|
||||
|
||||
When you start working on a feature, create a feature branch for your changes. These feature branches should be prefixed with `feature`.
|
||||
+9
-2
@@ -2,8 +2,15 @@
|
||||
|
||||
from django.contrib import admin
|
||||
from infoscreen.models import (
|
||||
Rotation, InfoItem, InfoInstance, ImageInfoItem,
|
||||
ExternalImageInfoItem, ABBInfoItem, ExternalWebsiteInfoItem, VideoInfoItem)
|
||||
Rotation,
|
||||
InfoItem,
|
||||
InfoInstance,
|
||||
ImageInfoItem,
|
||||
ExternalImageInfoItem,
|
||||
ABBInfoItem,
|
||||
ExternalWebsiteInfoItem,
|
||||
VideoInfoItem,
|
||||
)
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Rotation)
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ from django.apps import AppConfig
|
||||
class InfoscreenConfig(AppConfig):
|
||||
"""Infoscreen app configuration."""
|
||||
|
||||
name = 'infoscreen'
|
||||
name = "infoscreen"
|
||||
|
||||
@@ -11,81 +11,173 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HSLDataModel',
|
||||
name="HSLDataModel",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('data', models.TextField(default='', editable=False)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("data", models.TextField(default="", editable=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InfoInstance',
|
||||
name="InfoInstance",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('duration', models.FloatField(default=15.0)),
|
||||
('item_id', models.PositiveIntegerField()),
|
||||
('item_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("duration", models.FloatField(default=15.0)),
|
||||
("item_id", models.PositiveIntegerField()),
|
||||
(
|
||||
"item_type",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="contenttypes.ContentType",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InfoItem',
|
||||
name="InfoItem",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('expire_date', models.DateTimeField(blank=True, null=True)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=255)),
|
||||
("expire_date", models.DateTimeField(blank=True, null=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Rotation',
|
||||
name="Rotation",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ABBInfoItem',
|
||||
name="ABBInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ExternalImageInfoItem',
|
||||
name="ExternalImageInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
('url', models.TextField()),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
("url", models.TextField()),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HslInfoItem',
|
||||
name="HslInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ImageInfoItem',
|
||||
name="ImageInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
('img', models.ImageField(upload_to='infoimages/')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
("img", models.ImageField(upload_to="infoimages/")),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SossoInfoItem',
|
||||
name="SossoInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='infoinstance',
|
||||
name='rotation',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='infoscreen.Rotation'),
|
||||
model_name="infoinstance",
|
||||
name="rotation",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="instances",
|
||||
to="infoscreen.Rotation",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,15 +9,25 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('infoscreen', '0001_initial'),
|
||||
("infoscreen", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CoffeeInfoItem',
|
||||
name="CoffeeInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,33 +9,63 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('infoscreen', '0002_coffeeinfoitem'),
|
||||
("infoscreen", "0002_coffeeinfoitem"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ApyInfoItem',
|
||||
name="ApyInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventInfoItem',
|
||||
name="EventInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ExternalWebsiteInfoItem',
|
||||
name="ExternalWebsiteInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
('url', models.TextField()),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
("url", models.TextField()),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='CoffeeInfoItem',
|
||||
name="CoffeeInfoItem",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,16 +9,26 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('infoscreen', '0003_auto_20170329_1857'),
|
||||
("infoscreen", "0003_auto_20170329_1857"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='VideoInfoItem',
|
||||
name="VideoInfoItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
('video', models.FileField(upload_to='infovideos/')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
("video", models.FileField(upload_to="infovideos/")),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,18 +8,18 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('infoscreen', '0004_videoinfoitem'),
|
||||
("infoscreen", "0004_videoinfoitem"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='externalimageinfoitem',
|
||||
name='url',
|
||||
model_name="externalimageinfoitem",
|
||||
name="url",
|
||||
field=models.URLField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='externalwebsiteinfoitem',
|
||||
name='url',
|
||||
model_name="externalwebsiteinfoitem",
|
||||
name="url",
|
||||
field=models.URLField(),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,14 +8,14 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('infoscreen', '0005_auto_20170913_1841'),
|
||||
("infoscreen", "0005_auto_20170913_1841"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='HSLDataModel',
|
||||
name="HSLDataModel",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='HslInfoItem',
|
||||
name="HslInfoItem",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -7,15 +7,25 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('infoscreen', '0006_delete_hsldatamodel'),
|
||||
("infoscreen", "0006_delete_hsldatamodel"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LunchItem',
|
||||
name="LunchItem",
|
||||
fields=[
|
||||
('infoitem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='infoscreen.InfoItem')),
|
||||
(
|
||||
"infoitem_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="infoscreen.InfoItem",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('infoscreen.infoitem',),
|
||||
bases=("infoscreen.infoitem",),
|
||||
),
|
||||
]
|
||||
|
||||
+46
-53
@@ -23,14 +23,14 @@ class InfoItem(models.Model):
|
||||
|
||||
def get_template_url(self):
|
||||
"""Get infoscreen template url."""
|
||||
raise NotImplementedError(
|
||||
"inheriting classes must implement get_template_url")
|
||||
raise NotImplementedError("inheriting classes must implement get_template_url")
|
||||
|
||||
@staticmethod
|
||||
def get_create_template_url():
|
||||
"""Get create infoscreen template url command."""
|
||||
raise NotImplementedError(
|
||||
"inheriting classes must implement get_create_template_url")
|
||||
"inheriting classes must implement get_create_template_url"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create_from_dict(cls, d):
|
||||
@@ -42,14 +42,13 @@ class InfoItem(models.Model):
|
||||
def update_from_dict(self, d):
|
||||
"""Update model based on given dict."""
|
||||
try:
|
||||
expire_date = d.pop('expire_date', None)
|
||||
self.expire_date = datetime.strptime(
|
||||
expire_date, "%Y-%m-%d %H:%M:%S")
|
||||
expire_date = d.pop("expire_date", None)
|
||||
self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
|
||||
except:
|
||||
pass
|
||||
|
||||
dmap = {
|
||||
'name': 'name',
|
||||
"name": "name",
|
||||
}
|
||||
for k, v in d.items():
|
||||
try:
|
||||
@@ -61,13 +60,13 @@ class InfoItem(models.Model):
|
||||
def get_dict(self):
|
||||
"""Convert django model to dict and return it."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'item_type': ContentType.objects.get_for_model(self).id,
|
||||
'template_url': self.get_template_url(),
|
||||
'display_name': self.display_name,
|
||||
'create_template_url': self.get_create_template_url(),
|
||||
'options': {}
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"item_type": ContentType.objects.get_for_model(self).id,
|
||||
"template_url": self.get_template_url(),
|
||||
"display_name": self.display_name,
|
||||
"create_template_url": self.get_create_template_url(),
|
||||
"options": {},
|
||||
}
|
||||
|
||||
def delete(self):
|
||||
@@ -75,8 +74,8 @@ class InfoItem(models.Model):
|
||||
# since generic foreign keys suck, delete info
|
||||
# items pointing here manually
|
||||
InfoInstance.objects.filter(
|
||||
item_id=self.id,
|
||||
item_type=ContentType.objects.get_for_model(self)).delete()
|
||||
item_id=self.id, item_type=ContentType.objects.get_for_model(self)
|
||||
).delete()
|
||||
super().delete()
|
||||
|
||||
@classmethod
|
||||
@@ -139,7 +138,7 @@ class ExternalWebsiteInfoItem(InfoItem):
|
||||
def get_dict(self):
|
||||
"""Convert django model to dict and return it."""
|
||||
d = super().get_dict()
|
||||
d["options"] = {'url': self.url}
|
||||
d["options"] = {"url": self.url}
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
@@ -152,23 +151,22 @@ class ExternalWebsiteInfoItem(InfoItem):
|
||||
def get_list(self):
|
||||
"""Return list containing infoitem data."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'url': self.url,
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"url": self.url,
|
||||
}
|
||||
|
||||
def update_from_dict(self, d):
|
||||
"""Update model based on given dict."""
|
||||
try:
|
||||
expire_date = d.pop('expire_date', None)
|
||||
self.expire_date = datetime.strptime(
|
||||
expire_date, "%Y-%m-%d %H:%M:%S")
|
||||
expire_date = d.pop("expire_date", None)
|
||||
self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
|
||||
except:
|
||||
pass
|
||||
|
||||
dmap = {
|
||||
'name': 'name',
|
||||
'url': 'url',
|
||||
"name": "name",
|
||||
"url": "url",
|
||||
}
|
||||
for k, v in d.items():
|
||||
try:
|
||||
@@ -241,14 +239,14 @@ class ImageInfoItem(InfoItem):
|
||||
def get_dict(self):
|
||||
"""Convert django model to dict and return it."""
|
||||
d = super().get_dict()
|
||||
d["options"] = {'img': self.img.url}
|
||||
d["options"] = {"img": self.img.url}
|
||||
return d
|
||||
|
||||
|
||||
class VideoInfoItem(InfoItem):
|
||||
"""Class for Video Infoscreen item."""
|
||||
|
||||
display_name = ("Video")
|
||||
display_name = "Video"
|
||||
video = models.FileField(upload_to="infovideos/")
|
||||
|
||||
def get_template_url(self):
|
||||
@@ -263,7 +261,7 @@ class VideoInfoItem(InfoItem):
|
||||
def get_dict(self):
|
||||
"""Convert django model to dict and return it."""
|
||||
d = super().get_dict()
|
||||
d["options"] = {'video': self.video.url}
|
||||
d["options"] = {"video": self.video.url}
|
||||
return d
|
||||
|
||||
|
||||
@@ -285,7 +283,7 @@ class ExternalImageInfoItem(InfoItem):
|
||||
def get_dict(self):
|
||||
"""Convert django model to dict and return it."""
|
||||
d = super().get_dict()
|
||||
d["options"] = {'img': self.url}
|
||||
d["options"] = {"img": self.url}
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
@@ -298,15 +296,14 @@ class ExternalImageInfoItem(InfoItem):
|
||||
def update_from_dict(self, d):
|
||||
"""Update model based on given dict."""
|
||||
try:
|
||||
expire_date = d.pop('expire_date', None)
|
||||
self.expire_date = datetime.strptime(
|
||||
expire_date, "%Y-%m-%d %H:%M:%S")
|
||||
expire_date = d.pop("expire_date", None)
|
||||
self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
|
||||
except:
|
||||
pass
|
||||
|
||||
dmap = {
|
||||
'name': 'name',
|
||||
'url': 'url',
|
||||
"name": "name",
|
||||
"url": "url",
|
||||
}
|
||||
for k, v in d.items():
|
||||
try:
|
||||
@@ -319,12 +316,14 @@ class ExternalImageInfoItem(InfoItem):
|
||||
class InfoInstance(models.Model):
|
||||
"""Class for Info instance in Infoscreen."""
|
||||
|
||||
rotation = models.ForeignKey('Rotation', related_name='instances', on_delete=models.CASCADE)
|
||||
rotation = models.ForeignKey(
|
||||
"Rotation", related_name="instances", on_delete=models.CASCADE
|
||||
)
|
||||
duration = models.FloatField(default=15.0) # seconds
|
||||
# generic relation to some kind of InfoItem
|
||||
item_id = models.PositiveIntegerField()
|
||||
item_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
item = GenericForeignKey('item_type', 'item_id')
|
||||
item = GenericForeignKey("item_type", "item_id")
|
||||
|
||||
@classmethod
|
||||
def create_from_dict(cls, d):
|
||||
@@ -337,26 +336,21 @@ class InfoInstance(models.Model):
|
||||
except:
|
||||
raise RuntimeError("invalid parameters supplied supplied")
|
||||
try:
|
||||
return cls.objects.create(
|
||||
rotation=rotation,
|
||||
item=item,
|
||||
duration=duration
|
||||
)
|
||||
return cls.objects.create(rotation=rotation, item=item, duration=duration)
|
||||
except:
|
||||
raise RuntimeError("error while adding instance to db")
|
||||
|
||||
def get_dict(self):
|
||||
"""Convert django model to dict and return it."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'item': self.item.get_dict(),
|
||||
'duration': self.duration,
|
||||
"id": self.id,
|
||||
"item": self.item.get_dict(),
|
||||
"duration": self.duration,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
"""Return model name."""
|
||||
return "{}: {} ({}s)".format(
|
||||
self.rotation.name, self.item.name, self.duration)
|
||||
return "{}: {} ({}s)".format(self.rotation.name, self.item.name, self.duration)
|
||||
|
||||
|
||||
class Rotation(models.Model):
|
||||
@@ -370,21 +364,20 @@ class Rotation(models.Model):
|
||||
# to avoid excluding items with no expire_date)
|
||||
now = timezone.now()
|
||||
instances = self.instances.all()
|
||||
filtered = filter(lambda i: (i.item.expire_date or now) >= now,
|
||||
list(instances))
|
||||
filtered = filter(lambda i: (i.item.expire_date or now) >= now, list(instances))
|
||||
instance_list = list(map(lambda i: i.get_dict(), filtered))
|
||||
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'instances': instance_list,
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"instances": instance_list,
|
||||
}
|
||||
|
||||
def get_list(self):
|
||||
"""Return list containing infoitem data."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
|
||||
+1
-1
@@ -35,6 +35,6 @@ class InfoscreenTestCase(TestCase):
|
||||
That would mean that something meaningful has been included
|
||||
in the response.
|
||||
"""
|
||||
resp = self.c.get('/infoscreen/items')
|
||||
resp = self.c.get("/infoscreen/items")
|
||||
content = resp.json()
|
||||
self.assertTrue(len(content) > 0)
|
||||
|
||||
+23
-22
@@ -27,30 +27,31 @@ from infoscreen.views import createApyItem
|
||||
from infoscreen.views import get_apy_json
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', default),
|
||||
url(r'^admin$', admin),
|
||||
url(r'^(?P<idx>\d+)$', index),
|
||||
url(r'^items$', info_items),
|
||||
url(r'^rotation/(?P<idx>\d+)$', rotation),
|
||||
url(r'^rotations$', rotations),
|
||||
url(r'^instance$', createInstance),
|
||||
url(r'^instance/(?P<idx>\d+)$', deleteInstance),
|
||||
url(r'^types$', info_types),
|
||||
url(r'^delete_item/(?P<type_id>\d+)/(?P<idx>\d+)$', delete_info_item),
|
||||
url(r'^create_external_image$', createExternalImageInfoItem),
|
||||
url(r'^create_image$', create_image_item),
|
||||
url(r'^create_video$', create_video_item),
|
||||
url(r'^create_abbitem$', createABBItem),
|
||||
url(r'^create_sossoitem$', createSossoItem),
|
||||
url(r'^create_lunchitem$', createLunchItem),
|
||||
url(r'^create_eventitem$', createEventItem),
|
||||
url(r'^create_apyitem$', createApyItem),
|
||||
url(r'^create_websiteitem$', createExternalWebsiteItem),
|
||||
url(r'^create_rotation$', create_rotation),
|
||||
url(r'^delete_rotation/(?P<id>\d+)$', delete_rotation),
|
||||
url(r'^apyjson', get_apy_json),
|
||||
url(r"^$", default),
|
||||
url(r"^admin$", admin),
|
||||
url(r"^(?P<idx>\d+)$", index),
|
||||
url(r"^items$", info_items),
|
||||
url(r"^rotation/(?P<idx>\d+)$", rotation),
|
||||
url(r"^rotations$", rotations),
|
||||
url(r"^instance$", createInstance),
|
||||
url(r"^instance/(?P<idx>\d+)$", deleteInstance),
|
||||
url(r"^types$", info_types),
|
||||
url(r"^delete_item/(?P<type_id>\d+)/(?P<idx>\d+)$", delete_info_item),
|
||||
url(r"^create_external_image$", createExternalImageInfoItem),
|
||||
url(r"^create_image$", create_image_item),
|
||||
url(r"^create_video$", create_video_item),
|
||||
url(r"^create_abbitem$", createABBItem),
|
||||
url(r"^create_sossoitem$", createSossoItem),
|
||||
url(r"^create_lunchitem$", createLunchItem),
|
||||
url(r"^create_eventitem$", createEventItem),
|
||||
url(r"^create_apyitem$", createApyItem),
|
||||
url(r"^create_websiteitem$", createExternalWebsiteItem),
|
||||
url(r"^create_rotation$", create_rotation),
|
||||
url(r"^delete_rotation/(?P<id>\d+)$", delete_rotation),
|
||||
url(r"^apyjson", get_apy_json),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
@@ -16,16 +16,27 @@ import threading
|
||||
import requests
|
||||
|
||||
from infoscreen.models import (
|
||||
Rotation, InfoItem, InfoInstance, ABBInfoItem, ExternalImageInfoItem,
|
||||
ImageInfoItem, SossoInfoItem, LunchItem, EventInfoItem,
|
||||
ExternalWebsiteInfoItem, ImageUploadForm, ApyInfoItem, VideoInfoItem)
|
||||
Rotation,
|
||||
InfoItem,
|
||||
InfoInstance,
|
||||
ABBInfoItem,
|
||||
ExternalImageInfoItem,
|
||||
ImageInfoItem,
|
||||
SossoInfoItem,
|
||||
LunchItem,
|
||||
EventInfoItem,
|
||||
ExternalWebsiteInfoItem,
|
||||
ImageUploadForm,
|
||||
ApyInfoItem,
|
||||
VideoInfoItem,
|
||||
)
|
||||
|
||||
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.change_infoinstance', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.change_infoinstance", raise_exception=True)
|
||||
def admin(request, *args, **kwargs):
|
||||
"""Render infoscreen admin page."""
|
||||
return render(request, 'infoscreen:infoscreen_admin.html', {})
|
||||
return render(request, "infoscreen:infoscreen_admin.html", {})
|
||||
|
||||
|
||||
def create_item_generator(model):
|
||||
@@ -33,20 +44,23 @@ def create_item_generator(model):
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.add_infoinstance', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.add_infoinstance", raise_exception=True)
|
||||
def create_item(request, *args, **kwargs):
|
||||
try:
|
||||
data = json.loads(request.body.decode("utf-8"))
|
||||
except json.JSONDecodeError:
|
||||
return HttpResponseBadRequest(
|
||||
'{"status":"failure","error":"invalid json supplied"}')
|
||||
'{"status":"failure","error":"invalid json supplied"}'
|
||||
)
|
||||
try:
|
||||
model.create_from_dict(data)
|
||||
return HttpResponse('{"status":"success"}')
|
||||
except RuntimeError as e:
|
||||
return HttpResponseBadRequest(
|
||||
json.dumps({"status": "failure", "error": str(e)}))
|
||||
json.dumps({"status": "failure", "error": str(e)})
|
||||
)
|
||||
|
||||
return create_item
|
||||
|
||||
|
||||
@@ -55,8 +69,8 @@ def delete_item_generator(model):
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["DELETE"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.delete_infoinstance', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.delete_infoinstance", raise_exception=True)
|
||||
def delete_item(request, *args, **kwargs):
|
||||
idx = kwargs.pop("idx", 0)
|
||||
try:
|
||||
@@ -72,13 +86,14 @@ def delete_item_generator(model):
|
||||
resp = HttpResponse('{"error" : "could not delete item"}')
|
||||
resp.status_code = 500
|
||||
return resp
|
||||
|
||||
return delete_item
|
||||
|
||||
|
||||
# due to model structure this is little complicated
|
||||
@ensure_csrf_cookie
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.delete_infoinstance', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.delete_infoinstance", raise_exception=True)
|
||||
@require_http_methods(["DELETE"])
|
||||
def delete_info_item(request, *args, **kwargs):
|
||||
"""Delete info item."""
|
||||
@@ -102,42 +117,44 @@ def delete_info_item(request, *args, **kwargs):
|
||||
|
||||
@require_http_methods(["POST"])
|
||||
@ensure_csrf_cookie
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.add_infoinstance', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.add_infoinstance", raise_exception=True)
|
||||
def create_image_item(request, *args, **kwargs):
|
||||
"""Create image Infoscreen item."""
|
||||
form = ImageUploadForm(request.POST, request.FILES)
|
||||
if not form.is_valid():
|
||||
return HttpResponseBadRequest('{"status": "failure",'
|
||||
'"error": "invalid data supplied"}')
|
||||
return HttpResponseBadRequest(
|
||||
'{"status": "failure",' '"error": "invalid data supplied"}'
|
||||
)
|
||||
|
||||
img = form.cleaned_data['image']
|
||||
name = form.cleaned_data['name']
|
||||
img = form.cleaned_data["image"]
|
||||
name = form.cleaned_data["name"]
|
||||
ImageInfoItem.objects.create(img=img, name=name)
|
||||
return HttpResponse('{"status":"success"}')
|
||||
|
||||
|
||||
@require_http_methods(["POST"])
|
||||
@ensure_csrf_cookie
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.add_infoinstance', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.add_infoinstance", raise_exception=True)
|
||||
def create_video_item(request, *args, **kwargs):
|
||||
"""Create video Infoscreen item."""
|
||||
form = UploadFileForm(request.POST, request.FILES)
|
||||
if not form.is_valid():
|
||||
return HttpResponseBadRequest('{"status": "failure",'
|
||||
'"error": "invalid data supplied"}')
|
||||
return HttpResponseBadRequest(
|
||||
'{"status": "failure",' '"error": "invalid data supplied"}'
|
||||
)
|
||||
|
||||
video = form.cleaned_data['video']
|
||||
name = form.cleaned_data['name']
|
||||
video = form.cleaned_data["video"]
|
||||
name = form.cleaned_data["name"]
|
||||
VideoInfoItem.objects.create(video=video, name=name)
|
||||
return HttpResponse('{"status": "success"}')
|
||||
|
||||
|
||||
@require_http_methods(["POST"])
|
||||
@ensure_csrf_cookie
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.add_rotation', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.add_rotation", raise_exception=True)
|
||||
def create_rotation(request, *args, **kwargs):
|
||||
"""Create rotation."""
|
||||
try:
|
||||
@@ -150,16 +167,15 @@ def create_rotation(request, *args, **kwargs):
|
||||
Rotation.objects.create(name=name)
|
||||
resp = HttpResponse(status=200)
|
||||
except DatabaseError:
|
||||
resp = HttpResponse(
|
||||
'{"error" : "could not create rotation!"}', status=400)
|
||||
resp = HttpResponse('{"error" : "could not create rotation!"}', status=400)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@require_http_methods(["DELETE"])
|
||||
@ensure_csrf_cookie
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('infoscreen.delete_rotation', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("infoscreen.delete_rotation", raise_exception=True)
|
||||
def delete_rotation(request, *args, **kwargs):
|
||||
"""Delete rotation."""
|
||||
id = kwargs.pop("id", 0)
|
||||
@@ -169,8 +185,7 @@ def delete_rotation(request, *args, **kwargs):
|
||||
Rotation.objects.filter(id=id).delete()
|
||||
resp = HttpResponse(status=200)
|
||||
except DatabaseError:
|
||||
resp = HttpResponse(
|
||||
'{"error" : "could not delete rotation!"}', status=400)
|
||||
resp = HttpResponse('{"error" : "could not delete rotation!"}', status=400)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import requests
|
||||
@require_http_methods(["GET"])
|
||||
def index(request, idx, *args, **kwargs):
|
||||
"""Render infoscreen index page."""
|
||||
return render(request, 'infoscreen_index.html', {'rotation': idx})
|
||||
return render(request, "infoscreen_index.html", {"rotation": idx})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
@@ -32,7 +32,8 @@ def default(request, *args, **kwargs):
|
||||
def get_apy_json(request):
|
||||
"""Render APY diilikone page."""
|
||||
return HttpResponse(
|
||||
requests.get("https://api-diilikone.apy.fi/deals/top-groups").text)
|
||||
requests.get("https://api-diilikone.apy.fi/deals/top-groups").text
|
||||
)
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
@@ -61,10 +62,12 @@ def info_types(request, *args, **kwargs):
|
||||
types = []
|
||||
classes = InfoItem.get_subclasses()
|
||||
for c in classes:
|
||||
types.append({
|
||||
"name": c.display_name,
|
||||
"create_template_url": c.get_create_template_url(),
|
||||
})
|
||||
types.append(
|
||||
{
|
||||
"name": c.display_name,
|
||||
"create_template_url": c.get_create_template_url(),
|
||||
}
|
||||
)
|
||||
return HttpResponse(json.dumps(types))
|
||||
|
||||
|
||||
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
from django.contrib import admin
|
||||
from modeltranslation.admin import TranslationAdmin
|
||||
|
||||
from kaehmy.models import Application, Comment, CustomRole, PresetRole, TelegramChannel
|
||||
from kaehmy.models import Application, Comment, CustomRole, PresetRole
|
||||
|
||||
admin.site.register(Application)
|
||||
admin.site.register(Comment)
|
||||
admin.site.register(CustomRole)
|
||||
admin.site.register(PresetRole, TranslationAdmin)
|
||||
admin.site.register(TelegramChannel)
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||
|
||||
|
||||
class KaehmyConfig(AppConfig):
|
||||
name = 'kaehmy'
|
||||
name = "kaehmy"
|
||||
|
||||
+51
-26
@@ -6,12 +6,16 @@ from kaehmy.models import PresetRole, CustomRole, Application, Comment, KaehmyBa
|
||||
|
||||
|
||||
class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple):
|
||||
option_template_name = 'checkbox_option.html'
|
||||
option_template_name = "checkbox_option.html"
|
||||
|
||||
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
|
||||
dic = super(CheckboxSelectMultiple, self).create_option(name, value, label, selected, index, subindex, attrs)
|
||||
def create_option(
|
||||
self, name, value, label, selected, index, subindex=None, attrs=None
|
||||
):
|
||||
dic = super(CheckboxSelectMultiple, self).create_option(
|
||||
name, value, label, selected, index, subindex, attrs
|
||||
)
|
||||
description = PresetRole.objects.get(id=value).description
|
||||
dic['description'] = description
|
||||
dic["description"] = description
|
||||
return dic
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -25,30 +29,46 @@ class ApplicationForm(forms.ModelForm):
|
||||
"""Meta for class Application."""
|
||||
|
||||
model = Application
|
||||
fields = ['name', 'email', 'phone_number', 'year',
|
||||
'preset_roles', 'custom_roles', 'custom_role_name',
|
||||
'custom_role_is_board', 'text']
|
||||
fields = [
|
||||
"name",
|
||||
"email",
|
||||
"phone_number",
|
||||
"year",
|
||||
"preset_roles",
|
||||
"custom_roles",
|
||||
"custom_role_name",
|
||||
"custom_role_is_board",
|
||||
"text",
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ApplicationForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fields["email"].label = _('Email (not public)')
|
||||
self.fields["phone_number"].label = _('Phone number (not public)')
|
||||
self.fields["email"].label = _("Email (not public)")
|
||||
self.fields["phone_number"].label = _("Phone number (not public)")
|
||||
|
||||
custom_roles_exist = CustomRole.objects.all().exists()
|
||||
self.fields["custom_roles"].widget = forms.widgets.CheckboxSelectMultiple() if custom_roles_exist else forms.HiddenInput()
|
||||
self.fields["custom_roles"].widget = (
|
||||
forms.widgets.CheckboxSelectMultiple()
|
||||
if custom_roles_exist
|
||||
else forms.HiddenInput()
|
||||
)
|
||||
self.fields["custom_roles"].help_text = ""
|
||||
self.fields["custom_roles"].label = _('Custom roles')
|
||||
self.fields["custom_roles"].label = _("Custom roles")
|
||||
self.fields["custom_roles"].queryset = CustomRole.objects.all()
|
||||
|
||||
for cat_id, category in KaehmyBaseRole.CATEGORIES:
|
||||
key = 'preset_roles_{}'.format(cat_id)
|
||||
qset = PresetRole.objects.filter(category=cat_id).order_by('category', '-is_board')
|
||||
key = "preset_roles_{}".format(cat_id)
|
||||
qset = PresetRole.objects.filter(category=cat_id).order_by(
|
||||
"category", "-is_board"
|
||||
)
|
||||
self.fields[key] = forms.ModelMultipleChoiceField(qset)
|
||||
self.fields[key].widget = CheckboxSelectMultiple(attrs={
|
||||
'title': _('Preset roles'),
|
||||
'name': 'preset_roles',
|
||||
})
|
||||
self.fields[key].widget = CheckboxSelectMultiple(
|
||||
attrs={
|
||||
"title": _("Preset roles"),
|
||||
"name": "preset_roles",
|
||||
}
|
||||
)
|
||||
self.fields[key].help_text = ""
|
||||
self.fields[key].queryset = qset
|
||||
self.fields[key].label = _(category)
|
||||
@@ -57,33 +77,38 @@ class ApplicationForm(forms.ModelForm):
|
||||
def clean(self):
|
||||
cleaned_data = super(ApplicationForm, self).clean()
|
||||
for key in cleaned_data.keys():
|
||||
if 'preset_roles_' in key:
|
||||
cleaned_data['preset_roles'] = cleaned_data['preset_roles'] | cleaned_data[key]
|
||||
if "preset_roles_" in key:
|
||||
cleaned_data["preset_roles"] = (
|
||||
cleaned_data["preset_roles"] | cleaned_data[key]
|
||||
)
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def clean_phone_number(self):
|
||||
"""Clean phone number field."""
|
||||
number = self.cleaned_data.get('phone_number')
|
||||
number = self.cleaned_data.get("phone_number")
|
||||
if number.isdigit():
|
||||
return number
|
||||
else:
|
||||
raise ValidationError(_('Invalid phone number'))
|
||||
raise ValidationError(_("Invalid phone number"))
|
||||
|
||||
def clean_custom_role_name(self):
|
||||
"""Check that no other custom role with same name exists."""
|
||||
custom_name = self.cleaned_data.get('custom_role_name')
|
||||
custom_name = self.cleaned_data.get("custom_role_name")
|
||||
if not CustomRole.objects.filter(name=custom_name).exists():
|
||||
return custom_name
|
||||
else:
|
||||
raise ValidationError(_('Custom role with the same name already exists.'))
|
||||
raise ValidationError(_("Custom role with the same name already exists."))
|
||||
|
||||
def non_role_fields(self):
|
||||
return [self.fields[k] for k in self.fields.keys() if k not in ["preset_roles", "custom_roles"]]
|
||||
return [
|
||||
self.fields[k]
|
||||
for k in self.fields.keys()
|
||||
if k not in ["preset_roles", "custom_roles"]
|
||||
]
|
||||
|
||||
|
||||
class CommentForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ['name', 'email', 'message', 'parent']
|
||||
fields = ["name", "email", "message", "parent"]
|
||||
|
||||
@@ -12,82 +12,188 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0037_auto_20180125_2131'),
|
||||
("webapp", "0037_auto_20180125_2131"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CommentParent',
|
||||
name="CommentParent",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(default='', max_length=255, verbose_name='Name')),
|
||||
('email', models.EmailField(default='', max_length=254, verbose_name='Email')),
|
||||
('timestamp', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Timestamp')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(default="", max_length=255, verbose_name="Name"),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(default="", max_length=254, verbose_name="Email"),
|
||||
),
|
||||
(
|
||||
"timestamp",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="Timestamp"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomRole',
|
||||
name="CustomRole",
|
||||
fields=[
|
||||
('baserole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.BaseRole')),
|
||||
(
|
||||
"baserole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="webapp.BaseRole",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Custom kaehmy roles',
|
||||
'verbose_name': 'Custom kaehmy role',
|
||||
"verbose_name_plural": "Custom kaehmy roles",
|
||||
"verbose_name": "Custom kaehmy role",
|
||||
},
|
||||
bases=('webapp.baserole',),
|
||||
bases=("webapp.baserole",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PresetRole',
|
||||
name="PresetRole",
|
||||
fields=[
|
||||
('presetrole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.PresetRole')),
|
||||
(
|
||||
"presetrole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="webapp.PresetRole",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Preset kaehmy roles',
|
||||
'verbose_name': 'Preset kaehmy role',
|
||||
"verbose_name_plural": "Preset kaehmy roles",
|
||||
"verbose_name": "Preset kaehmy role",
|
||||
},
|
||||
bases=('webapp.presetrole',),
|
||||
bases=("webapp.presetrole",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TelegramChannel',
|
||||
name="TelegramChannel",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('channel_id', models.CharField(max_length=255, unique=True)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=255)),
|
||||
("channel_id", models.CharField(max_length=255, unique=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Telegram channels',
|
||||
'verbose_name': 'Telegram channel',
|
||||
"verbose_name_plural": "Telegram channels",
|
||||
"verbose_name": "Telegram channel",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Application',
|
||||
name="Application",
|
||||
fields=[
|
||||
('commentparent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='kaehmy.CommentParent')),
|
||||
('phone_number', models.CharField(default='', max_length=10, verbose_name='Phone number')),
|
||||
('year', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, 'N')], verbose_name='Year')),
|
||||
('text', models.TextField(default='', max_length=300, verbose_name='Text')),
|
||||
('custom_role_name', models.CharField(blank=True, max_length=255, verbose_name='Custom role name')),
|
||||
('custom_role_is_board', models.BooleanField(verbose_name='Board member')),
|
||||
('custom_roles', models.ManyToManyField(blank=True, related_name='forms', to='kaehmy.CustomRole')),
|
||||
('preset_roles', models.ManyToManyField(blank=True, related_name='forms', to='kaehmy.PresetRole')),
|
||||
(
|
||||
"commentparent_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="kaehmy.CommentParent",
|
||||
),
|
||||
),
|
||||
(
|
||||
"phone_number",
|
||||
models.CharField(
|
||||
default="", max_length=10, verbose_name="Phone number"
|
||||
),
|
||||
),
|
||||
(
|
||||
"year",
|
||||
models.IntegerField(
|
||||
choices=[(1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "N")],
|
||||
verbose_name="Year",
|
||||
),
|
||||
),
|
||||
(
|
||||
"text",
|
||||
models.TextField(default="", max_length=300, verbose_name="Text"),
|
||||
),
|
||||
(
|
||||
"custom_role_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=255, verbose_name="Custom role name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"custom_role_is_board",
|
||||
models.BooleanField(verbose_name="Board member"),
|
||||
),
|
||||
(
|
||||
"custom_roles",
|
||||
models.ManyToManyField(
|
||||
blank=True, related_name="forms", to="kaehmy.CustomRole"
|
||||
),
|
||||
),
|
||||
(
|
||||
"preset_roles",
|
||||
models.ManyToManyField(
|
||||
blank=True, related_name="forms", to="kaehmy.PresetRole"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Kaehmylomakkeet',
|
||||
'verbose_name': 'Kaehmylomake',
|
||||
"verbose_name_plural": "Kaehmylomakkeet",
|
||||
"verbose_name": "Kaehmylomake",
|
||||
},
|
||||
bases=('kaehmy.commentparent',),
|
||||
bases=("kaehmy.commentparent",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Comment',
|
||||
name="Comment",
|
||||
fields=[
|
||||
('commentparent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='kaehmy.CommentParent')),
|
||||
('message', models.TextField(verbose_name='Message')),
|
||||
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='kaehmy.CommentParent')),
|
||||
(
|
||||
"commentparent_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="kaehmy.CommentParent",
|
||||
),
|
||||
),
|
||||
("message", models.TextField(verbose_name="Message")),
|
||||
(
|
||||
"parent",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="messages",
|
||||
to="kaehmy.CommentParent",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Kaehmykommentit',
|
||||
'verbose_name': 'Kaehmykommentti',
|
||||
"verbose_name_plural": "Kaehmykommentit",
|
||||
"verbose_name": "Kaehmykommentti",
|
||||
},
|
||||
bases=('kaehmy.commentparent',),
|
||||
bases=("kaehmy.commentparent",),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -7,26 +7,59 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0047_auto_20180710_2110'),
|
||||
('kaehmy', '0001_initial'),
|
||||
("webapp", "0047_auto_20180710_2110"),
|
||||
("kaehmy", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='KaehmyBaseRole',
|
||||
name="KaehmyBaseRole",
|
||||
fields=[
|
||||
('baserole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.BaseRole')),
|
||||
('category', models.CharField(choices=[('corporate', 'Corporate affairs'), ('freshman', 'Freshmen'), ('international', 'International'), ('external', 'External affairs'), ('media', 'Media'), ('tech', 'Technology'), ('wellbeing', 'Wellbeing'), ('elepaja', 'Elepaja'), ('ceremonies', 'Ceremonies'), ('culture', 'Culture'), ('studies', 'Studies'), ('sosso', 'Sössö magazine'), ('alumni', 'Alumni relations'), ('others', 'Others')], default='others', max_length=255, verbose_name='Category')),
|
||||
(
|
||||
"baserole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="webapp.BaseRole",
|
||||
),
|
||||
),
|
||||
(
|
||||
"category",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("corporate", "Corporate affairs"),
|
||||
("freshman", "Freshmen"),
|
||||
("international", "International"),
|
||||
("external", "External affairs"),
|
||||
("media", "Media"),
|
||||
("tech", "Technology"),
|
||||
("wellbeing", "Wellbeing"),
|
||||
("elepaja", "Elepaja"),
|
||||
("ceremonies", "Ceremonies"),
|
||||
("culture", "Culture"),
|
||||
("studies", "Studies"),
|
||||
("sosso", "Sössö magazine"),
|
||||
("alumni", "Alumni relations"),
|
||||
("others", "Others"),
|
||||
],
|
||||
default="others",
|
||||
max_length=255,
|
||||
verbose_name="Category",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('webapp.baserole',),
|
||||
bases=("webapp.baserole",),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Application',
|
||||
name="Application",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='customrole',
|
||||
name="customrole",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='presetrole',
|
||||
name="presetrole",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -7,57 +7,113 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kaehmy', '0002_auto_20180902_1929'),
|
||||
("kaehmy", "0002_auto_20180902_1929"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Application',
|
||||
name="Application",
|
||||
fields=[
|
||||
('commentparent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='kaehmy.CommentParent')),
|
||||
('phone_number', models.CharField(default='', max_length=10, verbose_name='Phone number')),
|
||||
('year', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, 'N')], verbose_name='Year')),
|
||||
('text', models.TextField(default='', max_length=300, verbose_name='Text')),
|
||||
('custom_role_name', models.CharField(blank=True, max_length=255, verbose_name='Custom role name')),
|
||||
('custom_role_is_board', models.BooleanField(verbose_name='Board member')),
|
||||
(
|
||||
"commentparent_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="kaehmy.CommentParent",
|
||||
),
|
||||
),
|
||||
(
|
||||
"phone_number",
|
||||
models.CharField(
|
||||
default="", max_length=10, verbose_name="Phone number"
|
||||
),
|
||||
),
|
||||
(
|
||||
"year",
|
||||
models.IntegerField(
|
||||
choices=[(1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "N")],
|
||||
verbose_name="Year",
|
||||
),
|
||||
),
|
||||
(
|
||||
"text",
|
||||
models.TextField(default="", max_length=300, verbose_name="Text"),
|
||||
),
|
||||
(
|
||||
"custom_role_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=255, verbose_name="Custom role name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"custom_role_is_board",
|
||||
models.BooleanField(verbose_name="Board member"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Kaehmylomake',
|
||||
'verbose_name_plural': 'Kaehmylomakkeet',
|
||||
"verbose_name": "Kaehmylomake",
|
||||
"verbose_name_plural": "Kaehmylomakkeet",
|
||||
},
|
||||
bases=('kaehmy.commentparent',),
|
||||
bases=("kaehmy.commentparent",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomRole',
|
||||
name="CustomRole",
|
||||
fields=[
|
||||
('kaehmybaserole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='kaehmy.KaehmyBaseRole')),
|
||||
(
|
||||
"kaehmybaserole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="kaehmy.KaehmyBaseRole",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Custom kaehmy role',
|
||||
'verbose_name_plural': 'Custom kaehmy roles',
|
||||
"verbose_name": "Custom kaehmy role",
|
||||
"verbose_name_plural": "Custom kaehmy roles",
|
||||
},
|
||||
bases=('kaehmy.kaehmybaserole',),
|
||||
bases=("kaehmy.kaehmybaserole",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PresetRole',
|
||||
name="PresetRole",
|
||||
fields=[
|
||||
('kaehmybaserole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='kaehmy.KaehmyBaseRole')),
|
||||
('description', models.TextField(verbose_name='Description')),
|
||||
(
|
||||
"kaehmybaserole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="kaehmy.KaehmyBaseRole",
|
||||
),
|
||||
),
|
||||
("description", models.TextField(verbose_name="Description")),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Preset kaehmy role',
|
||||
'verbose_name_plural': 'Preset kaehmy roles',
|
||||
"verbose_name": "Preset kaehmy role",
|
||||
"verbose_name_plural": "Preset kaehmy roles",
|
||||
},
|
||||
bases=('kaehmy.kaehmybaserole',),
|
||||
bases=("kaehmy.kaehmybaserole",),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='application',
|
||||
name='custom_roles',
|
||||
field=models.ManyToManyField(blank=True, related_name='forms', to='kaehmy.CustomRole'),
|
||||
model_name="application",
|
||||
name="custom_roles",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, related_name="forms", to="kaehmy.CustomRole"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='application',
|
||||
name='preset_roles',
|
||||
field=models.ManyToManyField(blank=True, related_name='forms', to='kaehmy.PresetRole'),
|
||||
model_name="application",
|
||||
name="preset_roles",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, related_name="forms", to="kaehmy.PresetRole"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -6,13 +6,32 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kaehmy', '0003_auto_20180902_1943'),
|
||||
("kaehmy", "0003_auto_20180902_1943"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='kaehmybaserole',
|
||||
name='category',
|
||||
field=models.CharField(choices=[('corporate', 'Corporate affairs'), ('freshman', 'Freshmen'), ('international', 'International'), ('external', 'External affairs'), ('media', 'Media'), ('tech', 'Technology'), ('wellbeing', 'Wellbeing'), ('elepaja', 'Elepaja'), ('ceremonies', 'Ceremonies'), ('studies', 'Studies'), ('sosso', 'Sössö magazine'), ('alumni', 'Alumni relations'), ('others', 'Others')], default='others', max_length=255, verbose_name='Category'),
|
||||
model_name="kaehmybaserole",
|
||||
name="category",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("corporate", "Corporate affairs"),
|
||||
("freshman", "Freshmen"),
|
||||
("international", "International"),
|
||||
("external", "External affairs"),
|
||||
("media", "Media"),
|
||||
("tech", "Technology"),
|
||||
("wellbeing", "Wellbeing"),
|
||||
("elepaja", "Elepaja"),
|
||||
("ceremonies", "Ceremonies"),
|
||||
("studies", "Studies"),
|
||||
("sosso", "Sössö magazine"),
|
||||
("alumni", "Alumni relations"),
|
||||
("others", "Others"),
|
||||
],
|
||||
default="others",
|
||||
max_length=255,
|
||||
verbose_name="Category",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -6,13 +6,13 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kaehmy', '0004_auto_20181018_2121'),
|
||||
("kaehmy", "0004_auto_20181018_2121"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='application',
|
||||
name='custom_role_is_board',
|
||||
field=models.BooleanField(blank=True, verbose_name='Board member'),
|
||||
model_name="application",
|
||||
name="custom_role_is_board",
|
||||
field=models.BooleanField(blank=True, verbose_name="Board member"),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# Generated by Django 2.2.26 on 2022-01-12 20:38
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("kaehmy", "0005_auto_20190312_1458"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name="TelegramChannel",
|
||||
),
|
||||
]
|
||||
+65
-66
@@ -10,56 +10,58 @@ from webapp.models import BaseRole
|
||||
# 2. Data migrate from webapp BaseRole to new kaehmy BaseRole
|
||||
# 3. Delete webapp BaseRole table
|
||||
|
||||
VERBOSE_NAME = _('Kaehmy')
|
||||
VERBOSE_NAME = _("Kaehmy")
|
||||
|
||||
|
||||
class KaehmyBaseRole(BaseRole):
|
||||
"""ABC"""
|
||||
|
||||
CATEGORIES = (
|
||||
('corporate', _('Corporate affairs')),
|
||||
('freshman', _('Freshmen')),
|
||||
('international', _('International')),
|
||||
('external', _('External affairs')),
|
||||
('media', _('Media')),
|
||||
('tech', _('Technology')),
|
||||
('wellbeing', _('Wellbeing')),
|
||||
('elepaja', _('Elepaja')),
|
||||
('ceremonies', _('Ceremonies')),
|
||||
('studies', _('Studies')),
|
||||
('sosso', _('Sössö magazine')),
|
||||
('alumni', _('Alumni relations')),
|
||||
('others', _('Others')),
|
||||
("corporate", _("Corporate affairs")),
|
||||
("freshman", _("Freshmen")),
|
||||
("international", _("International")),
|
||||
("external", _("External affairs")),
|
||||
("media", _("Media")),
|
||||
("tech", _("Technology")),
|
||||
("wellbeing", _("Wellbeing")),
|
||||
("elepaja", _("Elepaja")),
|
||||
("ceremonies", _("Ceremonies")),
|
||||
("studies", _("Studies")),
|
||||
("sosso", _("Sössö magazine")),
|
||||
("alumni", _("Alumni relations")),
|
||||
("others", _("Others")),
|
||||
)
|
||||
category = models.CharField(
|
||||
_("Category"), choices=CATEGORIES, default="others", max_length=255
|
||||
)
|
||||
category = models.CharField(_('Category'), choices=CATEGORIES, default='others', max_length=255)
|
||||
|
||||
|
||||
class PresetRole(KaehmyBaseRole):
|
||||
"""Model for kaehmy role."""
|
||||
|
||||
description = models.TextField(_('Description'))
|
||||
description = models.TextField(_("Description"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Preset kaehmy role')
|
||||
verbose_name_plural = _('Preset kaehmy roles')
|
||||
verbose_name = _("Preset kaehmy role")
|
||||
verbose_name_plural = _("Preset kaehmy roles")
|
||||
|
||||
|
||||
class CustomRole(KaehmyBaseRole):
|
||||
"""Model representing a user-specified custom occupation."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Custom kaehmy role')
|
||||
verbose_name_plural = _('Custom kaehmy roles')
|
||||
verbose_name = _("Custom kaehmy role")
|
||||
verbose_name_plural = _("Custom kaehmy roles")
|
||||
|
||||
|
||||
class CommentParent(models.Model):
|
||||
|
||||
name = models.CharField(_('Name'), max_length=255, default='')
|
||||
email = models.EmailField(_('Email'), default='')
|
||||
timestamp = models.DateTimeField(_('Timestamp'), default=timezone.now)
|
||||
name = models.CharField(_("Name"), max_length=255, default="")
|
||||
email = models.EmailField(_("Email"), default="")
|
||||
timestamp = models.DateTimeField(_("Timestamp"), default=timezone.now)
|
||||
|
||||
def __str__(self):
|
||||
return 'Message parent #{}'.format(self.id)
|
||||
return "Message parent #{}".format(self.id)
|
||||
|
||||
|
||||
class Comment(CommentParent):
|
||||
@@ -70,11 +72,13 @@ class Comment(CommentParent):
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Kaehmykommentti')
|
||||
verbose_name_plural = _('Kaehmykommentit')
|
||||
verbose_name = _("Kaehmykommentti")
|
||||
verbose_name_plural = _("Kaehmykommentit")
|
||||
|
||||
message = models.TextField(_('Message'))
|
||||
parent = models.ForeignKey('CommentParent', related_name='messages', on_delete=models.CASCADE)
|
||||
message = models.TextField(_("Message"))
|
||||
parent = models.ForeignKey(
|
||||
"CommentParent", related_name="messages", on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
|
||||
class Application(CommentParent):
|
||||
@@ -83,34 +87,36 @@ class Application(CommentParent):
|
||||
|
||||
Allows user to choose from existing roles or to create custom ones.
|
||||
"""
|
||||
|
||||
YEAR_CHOICES = (
|
||||
(1, '1'),
|
||||
(2, '2'),
|
||||
(3, '3'),
|
||||
(4, '4'),
|
||||
(5, 'N'),
|
||||
(1, "1"),
|
||||
(2, "2"),
|
||||
(3, "3"),
|
||||
(4, "4"),
|
||||
(5, "N"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Kaehmylomake')
|
||||
verbose_name_plural = _('Kaehmylomakkeet')
|
||||
verbose_name = _("Kaehmylomake")
|
||||
verbose_name_plural = _("Kaehmylomakkeet")
|
||||
|
||||
phone_number = models.CharField(
|
||||
_('Phone number'), max_length=10, default="")
|
||||
year = models.IntegerField(_('Year'), choices=YEAR_CHOICES)
|
||||
text = models.TextField(_('Text'), default="", max_length=300)
|
||||
phone_number = models.CharField(_("Phone number"), max_length=10, default="")
|
||||
year = models.IntegerField(_("Year"), choices=YEAR_CHOICES)
|
||||
text = models.TextField(_("Text"), default="", max_length=300)
|
||||
custom_role_name = models.CharField(
|
||||
_('Custom role name'), max_length=255, blank=True)
|
||||
custom_role_is_board = models.BooleanField(
|
||||
_('Board member'), blank=True)
|
||||
_("Custom role name"), max_length=255, blank=True
|
||||
)
|
||||
custom_role_is_board = models.BooleanField(_("Board member"), blank=True)
|
||||
custom_roles = models.ManyToManyField(
|
||||
'kaehmy.CustomRole', related_name='forms', blank=True)
|
||||
"kaehmy.CustomRole", related_name="forms", blank=True
|
||||
)
|
||||
preset_roles = models.ManyToManyField(
|
||||
'kaehmy.PresetRole', related_name='forms', blank=True)
|
||||
"kaehmy.PresetRole", related_name="forms", blank=True
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
"""Return model info."""
|
||||
return _('Kaehmy application: {}').format(self.name)
|
||||
return _("Kaehmy application: {}").format(self.name)
|
||||
|
||||
def comment_count(self):
|
||||
"""Count comments for kaehmy."""
|
||||
@@ -132,34 +138,27 @@ class Application(CommentParent):
|
||||
presets = [r.name.capitalize() for r in self.preset_roles.filter(is_board=True)]
|
||||
customs = [r.name.capitalize() for r in self.custom_roles.filter(is_board=True)]
|
||||
combined = presets + customs
|
||||
return _('Board: {}').format(', '.join(combined)) if len(combined) > 0 else ''
|
||||
return _("Board: {}").format(", ".join(combined)) if len(combined) > 0 else ""
|
||||
|
||||
def official_roles(self):
|
||||
presets = [r.name.capitalize() for r in self.preset_roles.filter(is_board=False)]
|
||||
customs = [r.name.capitalize() for r in self.custom_roles.filter(is_board=False)]
|
||||
presets = [
|
||||
r.name.capitalize() for r in self.preset_roles.filter(is_board=False)
|
||||
]
|
||||
customs = [
|
||||
r.name.capitalize() for r in self.custom_roles.filter(is_board=False)
|
||||
]
|
||||
combined = presets + customs
|
||||
return _('Official: {}').format(', '.join(combined)) if len(combined) > 0 else ''
|
||||
return (
|
||||
_("Official: {}").format(", ".join(combined)) if len(combined) > 0 else ""
|
||||
)
|
||||
|
||||
def all_roles(self):
|
||||
presets = [r.name.capitalize() for r in self.preset_roles.all()]
|
||||
customs = [r.name.capitalize() for r in self.custom_roles.all()]
|
||||
combined = presets + customs
|
||||
return ', '.join(combined) if len(combined) > 0 else ''
|
||||
return ", ".join(combined) if len(combined) > 0 else ""
|
||||
|
||||
def has_any_board_role(self):
|
||||
return self.preset_roles.filter(is_board=True).exists() or self.custom_roles.filter(is_board=True)
|
||||
|
||||
|
||||
# Telegram channel entry for Kaehmys
|
||||
class TelegramChannel(models.Model):
|
||||
"""Model containing the channel id of a Telegram chat"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Telegram channel')
|
||||
verbose_name_plural = _('Telegram channels')
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
channel_id = models.CharField(max_length=255, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return 'Telegram channel: "{}"'.format(self.name)
|
||||
return self.preset_roles.filter(
|
||||
is_board=True
|
||||
).exists() or self.custom_roles.filter(is_board=True)
|
||||
|
||||
+8
-2
@@ -8,6 +8,12 @@ from kaehmy.models import Application
|
||||
class ExportTable(tables.Table):
|
||||
class Meta:
|
||||
model = Application
|
||||
exclude = ['text', 'messageparent_ptr', 'custom_role_name', 'custom_role_is_board', 'timestamp']
|
||||
exclude = [
|
||||
"text",
|
||||
"messageparent_ptr",
|
||||
"custom_role_name",
|
||||
"custom_role_is_board",
|
||||
"timestamp",
|
||||
]
|
||||
|
||||
all_roles = tables.Column(verbose_name=_('Roles'), orderable=False)
|
||||
all_roles = tables.Column(verbose_name=_("Roles"), orderable=False)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
'''
|
||||
A telegram bot api for whatever purposes.
|
||||
TODO: kaehmy app is definitely not correct place for this
|
||||
'''
|
||||
import logging
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from kaehmy.models import TelegramChannel
|
||||
|
||||
|
||||
class TelegramBot:
|
||||
'''
|
||||
A telegram bot api for whatever purposes
|
||||
Currently only able to broadcast stuff to all registered
|
||||
channels using broadcast method.
|
||||
'''
|
||||
|
||||
def __init__(self, api_token=None):
|
||||
|
||||
self.api_token = api_token or settings.TELEGRAM_BOT_TOKEN
|
||||
self.send_message_url = "https://api.telegram.org/bot{}/sendMessage".format(self.api_token)
|
||||
|
||||
def broadcast(self, message):
|
||||
channels_ids = TelegramChannel.objects.values_list("channel_id", flat=True)
|
||||
for id_ in channels_ids:
|
||||
self.send_message(id_, message)
|
||||
|
||||
def send_message(self, channel_id, message):
|
||||
'''
|
||||
Send message to a chat with given channel_id
|
||||
'''
|
||||
data = {
|
||||
'chat_id': channel_id,
|
||||
'text': message,
|
||||
'parse_mode': 'Markdown'
|
||||
}
|
||||
resp = requests.post(self.send_message_url, json=data)
|
||||
logging.debug(resp.content)
|
||||
@@ -6,13 +6,13 @@ from kaehmy.models import PresetRole, CustomRole
|
||||
|
||||
@register(PresetRole)
|
||||
class PresetRoleTranslationOptions(TranslationOptions):
|
||||
""" Class for PresetRole translation options"""
|
||||
"""Class for PresetRole translation options"""
|
||||
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(CustomRole)
|
||||
class CustomRoleTranslationOptions(TranslationOptions):
|
||||
""" Class for CustomROle translation options"""
|
||||
"""Class for CustomROle translation options"""
|
||||
|
||||
fields = ()
|
||||
|
||||
+7
-6
@@ -13,14 +13,15 @@ from kaehmy.views import export_view
|
||||
|
||||
urlpatterns = [
|
||||
# kaehmy
|
||||
url(r'^new', view),
|
||||
url(r'^submit', submit),
|
||||
url(r'^add_comment', comment),
|
||||
url(r'^statistics', statistics_view),
|
||||
url(r'^export', export_view),
|
||||
url(r'^$', list_view),
|
||||
url(r"^new", view),
|
||||
url(r"^submit", submit),
|
||||
url(r"^add_comment", comment),
|
||||
url(r"^statistics", statistics_view),
|
||||
url(r"^export", export_view),
|
||||
url(r"^$", list_view),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
+3
-14
@@ -13,10 +13,11 @@ from dealer.git import git
|
||||
from sikweb.settings import URL
|
||||
|
||||
from members.views.utils import *
|
||||
from kaehmy.models import Application, CustomRole, PresetRole, TelegramChannel
|
||||
from kaehmy.models import Application, CustomRole, PresetRole
|
||||
from kaehmy.forms import ApplicationForm, CommentForm
|
||||
from kaehmy.tables import ExportTable
|
||||
from webapp.utils import send_email
|
||||
from webapp.models import processHooks
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@@ -144,19 +145,7 @@ def submit(request, *args, **kwargs):
|
||||
send_email(to_email, subject, email_body)
|
||||
logging.debug(f"Sent kaehmy email to recipient <{to_email}>")
|
||||
|
||||
CHAT_IDS = [channel.channel_id for channel in TelegramChannel.objects.all()]
|
||||
for chat_id in CHAT_IDS:
|
||||
tg_string = (
|
||||
"https://api.telegram.org/bot{}/sendMessage?chat_id={}&text={}".format(
|
||||
settings.TELEGRAM_BOT_TOKEN,
|
||||
chat_id,
|
||||
"Uusi New kaehmy! {} -> {}".format(name, url),
|
||||
)
|
||||
)
|
||||
response = requests.get(tg_string).json()
|
||||
logging.debug("Telegram API response:\n{}".format(response))
|
||||
logging.debug("Sent kaehmy announcement to {} channels.".format(len(CHAT_IDS)))
|
||||
|
||||
processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy")
|
||||
else:
|
||||
context = {"error": form.errors}
|
||||
return render(request, "kaehmy:error.html", context)
|
||||
|
||||
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-13 21:55+0200\n"
|
||||
"POT-Creation-Date: 2022-01-13 22:18+0200\n"
|
||||
"PO-Revision-Date: 2017-11-02 23:09+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@@ -37,7 +37,7 @@ msgstr "Sössö articles"
|
||||
msgid "Today's lunch"
|
||||
msgstr ""
|
||||
|
||||
#: infoscreen/models.py:212 webapp/models.py:70
|
||||
#: infoscreen/models.py:212 webapp/models.py:93
|
||||
msgid "Events"
|
||||
msgstr "Events"
|
||||
|
||||
@@ -113,7 +113,7 @@ msgid "Delete"
|
||||
msgstr "Delete"
|
||||
|
||||
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57
|
||||
#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173
|
||||
#: kaehmy/templates/list.html:36 webapp/models.py:188 webapp/models.py:223
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
@@ -327,7 +327,7 @@ msgstr ""
|
||||
msgid "Custom role name"
|
||||
msgstr ""
|
||||
|
||||
#: kaehmy/models.py:105 webapp/models.py:174
|
||||
#: kaehmy/models.py:105 webapp/models.py:224
|
||||
msgid "Board member"
|
||||
msgstr "Board member"
|
||||
|
||||
@@ -343,14 +343,6 @@ msgstr ""
|
||||
msgid "Official: {}"
|
||||
msgstr ""
|
||||
|
||||
#: kaehmy/models.py:158
|
||||
msgid "Telegram channel"
|
||||
msgstr ""
|
||||
|
||||
#: kaehmy/models.py:159
|
||||
msgid "Telegram channels"
|
||||
msgstr ""
|
||||
|
||||
#: kaehmy/tables.py:13
|
||||
msgid "Roles"
|
||||
msgstr ""
|
||||
@@ -635,6 +627,10 @@ msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
|
||||
msgstr "Amazing! Your membership application has been sent."
|
||||
|
||||
#: members/templates/application_success.html:9
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä "
|
||||
#| "admin@sahkoinsinoorikilta.fi.fi jos viestiä ei näy."
|
||||
msgid ""
|
||||
"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä "
|
||||
"admin@sahkoinsinoorikilta.fi jos viestiä ei näy."
|
||||
@@ -1104,91 +1100,135 @@ msgstr "Go"
|
||||
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
|
||||
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
|
||||
|
||||
#: webapp/models.py:18
|
||||
#: webapp/models.py:20
|
||||
msgid "Webapp"
|
||||
msgstr "Webapp"
|
||||
|
||||
#: webapp/models.py:26
|
||||
#: webapp/models.py:28
|
||||
msgid "Tag"
|
||||
msgstr "Tag"
|
||||
|
||||
#: webapp/models.py:27
|
||||
#: webapp/models.py:29
|
||||
msgid "Tags"
|
||||
msgstr "Tags"
|
||||
|
||||
#: webapp/models.py:34
|
||||
#: webapp/models.py:36
|
||||
msgid "Tag: {}"
|
||||
msgstr "Tag: {}"
|
||||
|
||||
#: webapp/models.py:52
|
||||
#: webapp/models.py:54
|
||||
msgid "Feed"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:53
|
||||
#: webapp/models.py:55
|
||||
msgid "Feeds"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119
|
||||
#: webapp/models.py:152 webapp/models.py:198
|
||||
#: webapp/models.py:63 webapp/models.py:102 webapp/models.py:162
|
||||
#: webapp/models.py:196 webapp/models.py:248
|
||||
msgid "Deleted: "
|
||||
msgstr "Deleted: "
|
||||
|
||||
#: webapp/models.py:62
|
||||
#: webapp/models.py:64
|
||||
msgid "{}Feed: {}"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:69
|
||||
#: webapp/models.py:92
|
||||
msgid "Event"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:81
|
||||
#: webapp/models.py:103
|
||||
msgid "{}Event: {}"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:90
|
||||
#: webapp/models.py:133
|
||||
msgid "Template question"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:91
|
||||
#: webapp/models.py:134
|
||||
msgid "Template questions"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:98
|
||||
#: webapp/models.py:141
|
||||
msgid "Template questions: {}"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:105
|
||||
#: webapp/models.py:148
|
||||
msgid "Signup form"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:106
|
||||
#: webapp/models.py:149
|
||||
msgid "Signup forms"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:120
|
||||
#: webapp/models.py:163
|
||||
msgid "#{} {}{}"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:138
|
||||
#: webapp/models.py:181
|
||||
msgid "Sign-up"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:139
|
||||
#: webapp/models.py:182
|
||||
msgid "Sign-ups"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:178
|
||||
#: webapp/models.py:228
|
||||
msgid "board member"
|
||||
msgstr "board member"
|
||||
|
||||
#: webapp/models.py:185
|
||||
#: webapp/models.py:235
|
||||
msgid "JobAd"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:186
|
||||
#: webapp/models.py:236
|
||||
msgid "JobAds"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:295
|
||||
#, fuzzy
|
||||
#| msgid "Kaehmy"
|
||||
msgid "Hook Kaehmys"
|
||||
msgstr "Kaehmy"
|
||||
|
||||
#: webapp/models.py:296
|
||||
#, fuzzy
|
||||
#| msgid "Total challenges:"
|
||||
msgid "Hook Ohlhafv challenges"
|
||||
msgstr "Total challenges:"
|
||||
|
||||
#: webapp/models.py:297
|
||||
msgid "Hook published news"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:298
|
||||
msgid "Hook published Job Ads"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:299
|
||||
msgid "Hook published events"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:300
|
||||
msgid "Hook opened signups"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:325
|
||||
msgid "Webhook"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:326
|
||||
msgid "Webhooks"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:339
|
||||
msgid "Telegram channel"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:340
|
||||
msgid "Telegram channels"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/templates/contact.html:9 webapp/templates/navigation.html:20
|
||||
msgid "Contact"
|
||||
msgstr "Contact"
|
||||
@@ -1232,3 +1272,6 @@ msgstr "Sössö"
|
||||
#: webapp/templates/navigation.html:24
|
||||
msgid "Corporate"
|
||||
msgstr "Corporate"
|
||||
|
||||
#~ msgid "Hallitustyrkkypaneeli"
|
||||
#~ msgstr "Panel for board applicants"
|
||||
|
||||
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-13 21:55+0200\n"
|
||||
"POT-Creation-Date: 2022-01-13 22:18+0200\n"
|
||||
"PO-Revision-Date: 2017-11-02 23:04+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@@ -38,7 +38,7 @@ msgstr "Sössön artikkelit"
|
||||
msgid "Today's lunch"
|
||||
msgstr "Päivän lounas"
|
||||
|
||||
#: infoscreen/models.py:212 webapp/models.py:70
|
||||
#: infoscreen/models.py:212 webapp/models.py:93
|
||||
msgid "Events"
|
||||
msgstr "Tapahtumat"
|
||||
|
||||
@@ -114,7 +114,7 @@ msgid "Delete"
|
||||
msgstr "Poista"
|
||||
|
||||
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57
|
||||
#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173
|
||||
#: kaehmy/templates/list.html:36 webapp/models.py:188 webapp/models.py:223
|
||||
msgid "Name"
|
||||
msgstr "Nimi"
|
||||
|
||||
@@ -328,7 +328,7 @@ msgstr "Teksti"
|
||||
msgid "Custom role name"
|
||||
msgstr "Uusi virka"
|
||||
|
||||
#: kaehmy/models.py:105 webapp/models.py:174
|
||||
#: kaehmy/models.py:105 webapp/models.py:224
|
||||
msgid "Board member"
|
||||
msgstr "Hallituksen jäsen"
|
||||
|
||||
@@ -344,14 +344,6 @@ msgstr "Hallitus: {}"
|
||||
msgid "Official: {}"
|
||||
msgstr "Toimari: {}"
|
||||
|
||||
#: kaehmy/models.py:158
|
||||
msgid "Telegram channel"
|
||||
msgstr "Telegram-kanava"
|
||||
|
||||
#: kaehmy/models.py:159
|
||||
msgid "Telegram channels"
|
||||
msgstr "Telegram-kanavat"
|
||||
|
||||
#: kaehmy/tables.py:13
|
||||
msgid "Roles"
|
||||
msgstr "Roolit"
|
||||
@@ -1014,11 +1006,11 @@ msgstr "Øhlhäfv"
|
||||
|
||||
#: ohlhafv/models.py:22
|
||||
msgid "Ohlhafv challenge"
|
||||
msgstr "Ohlhafv haaste"
|
||||
msgstr "Øhlhäfv-haaste"
|
||||
|
||||
#: ohlhafv/models.py:23
|
||||
msgid "Ohlhafv challenges"
|
||||
msgstr "Ohlhafv haasteet"
|
||||
msgstr "Øhlhäfv-haasteet"
|
||||
|
||||
#: ohlhafv/models.py:29
|
||||
msgid "Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)"
|
||||
@@ -1042,7 +1034,7 @@ msgstr "Sarja"
|
||||
|
||||
#: ohlhafv/models.py:40
|
||||
msgid "Ohlhafv challenge: {} vs. {}"
|
||||
msgstr "Ohlhafv-haaste: {} vs. {}"
|
||||
msgstr "Øhlhäfv-haaste: {} vs. {}"
|
||||
|
||||
#: ohlhafv/templates/email.html:4
|
||||
msgid "on haastanut sinut oluenjuontimittelöön"
|
||||
@@ -1093,91 +1085,131 @@ msgstr "Vaihda"
|
||||
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
|
||||
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
|
||||
|
||||
#: webapp/models.py:18
|
||||
#: webapp/models.py:20
|
||||
msgid "Webapp"
|
||||
msgstr "Nettisivut"
|
||||
|
||||
#: webapp/models.py:26
|
||||
#: webapp/models.py:28
|
||||
msgid "Tag"
|
||||
msgstr "Tunniste"
|
||||
|
||||
#: webapp/models.py:27
|
||||
#: webapp/models.py:29
|
||||
msgid "Tags"
|
||||
msgstr "Tunnisteet"
|
||||
|
||||
#: webapp/models.py:34
|
||||
#: webapp/models.py:36
|
||||
msgid "Tag: {}"
|
||||
msgstr "Tunniste: {}"
|
||||
|
||||
#: webapp/models.py:52
|
||||
#: webapp/models.py:54
|
||||
msgid "Feed"
|
||||
msgstr "Uutinen"
|
||||
|
||||
#: webapp/models.py:53
|
||||
#: webapp/models.py:55
|
||||
msgid "Feeds"
|
||||
msgstr "Uutiset"
|
||||
|
||||
#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119
|
||||
#: webapp/models.py:152 webapp/models.py:198
|
||||
#: webapp/models.py:63 webapp/models.py:102 webapp/models.py:162
|
||||
#: webapp/models.py:196 webapp/models.py:248
|
||||
msgid "Deleted: "
|
||||
msgstr "Poistettu: "
|
||||
|
||||
#: webapp/models.py:62
|
||||
#: webapp/models.py:64
|
||||
msgid "{}Feed: {}"
|
||||
msgstr "{}Uutinen: {}"
|
||||
|
||||
#: webapp/models.py:69
|
||||
#: webapp/models.py:92
|
||||
msgid "Event"
|
||||
msgstr "Tapahtuma"
|
||||
|
||||
#: webapp/models.py:81
|
||||
#: webapp/models.py:103
|
||||
msgid "{}Event: {}"
|
||||
msgstr "{}Tapahtuma: {}"
|
||||
|
||||
#: webapp/models.py:90
|
||||
#: webapp/models.py:133
|
||||
msgid "Template question"
|
||||
msgstr "Vakiokysymys"
|
||||
|
||||
#: webapp/models.py:91
|
||||
#: webapp/models.py:134
|
||||
msgid "Template questions"
|
||||
msgstr "Vakiokysymykset"
|
||||
|
||||
#: webapp/models.py:98
|
||||
#: webapp/models.py:141
|
||||
msgid "Template questions: {}"
|
||||
msgstr "Vakiokysymykset: {}"
|
||||
|
||||
#: webapp/models.py:105
|
||||
#: webapp/models.py:148
|
||||
msgid "Signup form"
|
||||
msgstr "Ilmoittautumislomake"
|
||||
|
||||
#: webapp/models.py:106
|
||||
#: webapp/models.py:149
|
||||
msgid "Signup forms"
|
||||
msgstr "Ilmoittautumislomakkeet"
|
||||
|
||||
#: webapp/models.py:120
|
||||
#: webapp/models.py:163
|
||||
msgid "#{} {}{}"
|
||||
msgstr ""
|
||||
|
||||
#: webapp/models.py:138
|
||||
#: webapp/models.py:181
|
||||
msgid "Sign-up"
|
||||
msgstr "Ilmoittautuminen"
|
||||
|
||||
#: webapp/models.py:139
|
||||
#: webapp/models.py:182
|
||||
msgid "Sign-ups"
|
||||
msgstr "Ilmoittautumiset"
|
||||
|
||||
#: webapp/models.py:178
|
||||
#: webapp/models.py:228
|
||||
msgid "board member"
|
||||
msgstr "hallituksen jäsen"
|
||||
|
||||
#: webapp/models.py:185
|
||||
#: webapp/models.py:235
|
||||
msgid "JobAd"
|
||||
msgstr "Työpaikkailmoitus"
|
||||
|
||||
#: webapp/models.py:186
|
||||
#: webapp/models.py:236
|
||||
msgid "JobAds"
|
||||
msgstr "Työpaikkailmoitukset"
|
||||
|
||||
#: webapp/models.py:295
|
||||
msgid "Hook Kaehmys"
|
||||
msgstr "Lähetä Kähmyt"
|
||||
|
||||
#: webapp/models.py:296
|
||||
msgid "Hook Ohlhafv challenges"
|
||||
msgstr "Lähetä Øhlhäfv-haasteet"
|
||||
|
||||
#: webapp/models.py:297
|
||||
msgid "Hook published news"
|
||||
msgstr "Lähetä julkaistut uutiset"
|
||||
|
||||
#: webapp/models.py:298
|
||||
msgid "Hook published Job Ads"
|
||||
msgstr "Lähetä työpaikkailmoitukset"
|
||||
|
||||
#: webapp/models.py:299
|
||||
msgid "Hook published events"
|
||||
msgstr "Lähetä julkaistut tapahtumat"
|
||||
|
||||
#: webapp/models.py:300
|
||||
msgid "Hook opened signups"
|
||||
msgstr "Lähetä auenneet ilmot"
|
||||
|
||||
#: webapp/models.py:325
|
||||
msgid "Webhook"
|
||||
msgstr "Webhook"
|
||||
|
||||
#: webapp/models.py:326
|
||||
msgid "Webhooks"
|
||||
msgstr "Webhookit"
|
||||
|
||||
#: webapp/models.py:339
|
||||
msgid "Telegram channel"
|
||||
msgstr "Telegram-kanava"
|
||||
|
||||
#: webapp/models.py:340
|
||||
msgid "Telegram channels"
|
||||
msgstr "Telegram-kanavat"
|
||||
|
||||
#: webapp/templates/contact.html:9 webapp/templates/navigation.html:20
|
||||
msgid "Contact"
|
||||
msgstr "Yhteystiedot"
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ admin.site.register(Member)
|
||||
admin.site.register(Request)
|
||||
admin.site.register(Payment)
|
||||
|
||||
admin.site.site_header = 'SIK Admin'
|
||||
admin.site.site_header = "SIK Admin"
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ from django.apps import AppConfig
|
||||
class MembersConfig(AppConfig):
|
||||
"""Class for Members app configurations."""
|
||||
|
||||
name = 'members'
|
||||
name = "members"
|
||||
|
||||
+25
-22
@@ -23,7 +23,7 @@ class MemberForm(forms.ModelForm):
|
||||
"""Meta for Member model form."""
|
||||
|
||||
model = Member
|
||||
fields = ['first_name', 'last_name', 'email', 'POR', 'AYY', 'jas']
|
||||
fields = ["first_name", "last_name", "email", "POR", "AYY", "jas"]
|
||||
|
||||
class ImportResult:
|
||||
def __init__(self, members, payments):
|
||||
@@ -32,22 +32,27 @@ class MemberForm(forms.ModelForm):
|
||||
|
||||
def _clean_boolean_field(self, key):
|
||||
value = self.data.get(key, None)
|
||||
if value in ['1', '0']:
|
||||
if value in ["1", "0"]:
|
||||
return bool(int(value))
|
||||
else:
|
||||
return value == 'on'
|
||||
return value == "on"
|
||||
|
||||
def clean_jas(self):
|
||||
return self._clean_boolean_field('jas')
|
||||
return self._clean_boolean_field("jas")
|
||||
|
||||
def clean_AYY(self):
|
||||
return self._clean_boolean_field('AYY')
|
||||
return self._clean_boolean_field("AYY")
|
||||
|
||||
@staticmethod
|
||||
def csv_to_models(data, payment_source='AYY', delimiter=','):
|
||||
clean_data = data.strip().split('\n')
|
||||
clean_data = [row.rstrip(',').rstrip('\r').strip() for row in clean_data]
|
||||
csv_reader = csv.DictReader(clean_data, fieldnames=MemberForm.Meta.fields, delimiter=delimiter, quoting=csv.QUOTE_NONE)
|
||||
def csv_to_models(data, payment_source="AYY", delimiter=","):
|
||||
clean_data = data.strip().split("\n")
|
||||
clean_data = [row.rstrip(",").rstrip("\r").strip() for row in clean_data]
|
||||
csv_reader = csv.DictReader(
|
||||
clean_data,
|
||||
fieldnames=MemberForm.Meta.fields,
|
||||
delimiter=delimiter,
|
||||
quoting=csv.QUOTE_NONE,
|
||||
)
|
||||
|
||||
members = []
|
||||
payments = []
|
||||
@@ -57,10 +62,10 @@ class MemberForm(forms.ModelForm):
|
||||
line[key] = value.strip()
|
||||
except AttributeError as ex:
|
||||
logging.error('Invalid line in CSV: "{}"'.format(line))
|
||||
logging.error('Delimiter: {}'.format(delimiter))
|
||||
logging.error("Delimiter: {}".format(delimiter))
|
||||
raise
|
||||
|
||||
email = line['email']
|
||||
email = line["email"]
|
||||
member_exists = False
|
||||
if Member.objects.filter(email=email).exists():
|
||||
member_exists = True
|
||||
@@ -76,9 +81,9 @@ class MemberForm(forms.ModelForm):
|
||||
else:
|
||||
member = Member.objects.get(email=email)
|
||||
payment_data = {
|
||||
'source': payment_source,
|
||||
'member': member.id,
|
||||
'date': timezone.now(),
|
||||
"source": payment_source,
|
||||
"member": member.id,
|
||||
"date": timezone.now(),
|
||||
}
|
||||
form = PaymentForm(payment_data)
|
||||
if not form.is_valid():
|
||||
@@ -95,17 +100,15 @@ class PaymentForm(forms.ModelForm):
|
||||
|
||||
member = forms.ModelChoiceField(
|
||||
queryset=Member.objects.all(),
|
||||
widget=autocomplete.ModelSelect2(url='member-autocomplete')
|
||||
widget=autocomplete.ModelSelect2(url="member-autocomplete"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Meta for Payment model form."""
|
||||
|
||||
model = Payment
|
||||
fields = ['date', 'source', 'member']
|
||||
labels = {
|
||||
'member': _('Member')
|
||||
}
|
||||
fields = ["date", "source", "member"]
|
||||
labels = {"member": _("Member")}
|
||||
|
||||
|
||||
class ApplicationForm(forms.ModelForm):
|
||||
@@ -115,13 +118,13 @@ class ApplicationForm(forms.ModelForm):
|
||||
"""Meta for application model form."""
|
||||
|
||||
model = Request
|
||||
fields = ['first_name', 'last_name', 'email', 'AYY', 'jas', 'POR']
|
||||
fields = ["first_name", "last_name", "email", "AYY", "jas", "POR"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ApplicationForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fields['AYY'].label = _("I'm a member of AYY")
|
||||
self.fields['jas'].label = _("I want to receive a weekly newsletter")
|
||||
self.fields["AYY"].label = _("I'm a member of AYY")
|
||||
self.fields["jas"].label = _("I want to receive a weekly newsletter")
|
||||
|
||||
|
||||
class UploadFileForm(forms.Form):
|
||||
|
||||
@@ -12,29 +12,52 @@ class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Member',
|
||||
name="Member",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=127)),
|
||||
('last_name', models.CharField(max_length=127)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('POR', models.CharField(max_length=255)),
|
||||
('AYY', models.BooleanField(default=False)),
|
||||
('jas', models.BooleanField(default=False)),
|
||||
('created', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('paid', models.DateTimeField(default=datetime.datetime(1970, 1, 1, 0, 0))),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("first_name", models.CharField(max_length=127)),
|
||||
("last_name", models.CharField(max_length=127)),
|
||||
("email", models.EmailField(max_length=254)),
|
||||
("POR", models.CharField(max_length=255)),
|
||||
("AYY", models.BooleanField(default=False)),
|
||||
("jas", models.BooleanField(default=False)),
|
||||
("created", models.DateTimeField(default=django.utils.timezone.now)),
|
||||
(
|
||||
"paid",
|
||||
models.DateTimeField(default=datetime.datetime(1970, 1, 1, 0, 0)),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MemberRequest',
|
||||
name="MemberRequest",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.Member')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"member",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="members.Member"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,13 +9,13 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0001_initial'),
|
||||
("members", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='paid',
|
||||
model_name="member",
|
||||
name="paid",
|
||||
field=models.DateTimeField(default=datetime.datetime(1970, 1, 1, 2, 0)),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -11,57 +11,81 @@ import django.utils.timezone
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0002_auto_20170329_1857'),
|
||||
("members", "0002_auto_20170329_1857"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Payment',
|
||||
name="Payment",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateTimeField(default=datetime.datetime(1970, 1, 1, 2, 0))),
|
||||
('source', models.CharField(max_length=255)),
|
||||
('first_name', models.CharField(max_length=255)),
|
||||
('last_name', models.CharField(max_length=255)),
|
||||
('email', models.EmailField(max_length=255)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date",
|
||||
models.DateTimeField(default=datetime.datetime(1970, 1, 1, 2, 0)),
|
||||
),
|
||||
("source", models.CharField(max_length=255)),
|
||||
("first_name", models.CharField(max_length=255)),
|
||||
("last_name", models.CharField(max_length=255)),
|
||||
("email", models.EmailField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Request',
|
||||
name="Request",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=127)),
|
||||
('last_name', models.CharField(max_length=127)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('POR', models.CharField(default='ei_tiedossa', max_length=255)),
|
||||
('AYY', models.BooleanField(default=False)),
|
||||
('jas', models.BooleanField(default=False)),
|
||||
('submitted', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("first_name", models.CharField(max_length=127)),
|
||||
("last_name", models.CharField(max_length=127)),
|
||||
("email", models.EmailField(max_length=254)),
|
||||
("POR", models.CharField(default="ei_tiedossa", max_length=255)),
|
||||
("AYY", models.BooleanField(default=False)),
|
||||
("jas", models.BooleanField(default=False)),
|
||||
("submitted", models.DateTimeField(default=django.utils.timezone.now)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='memberrequest',
|
||||
name='member',
|
||||
model_name="memberrequest",
|
||||
name="member",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='POR',
|
||||
field=models.CharField(default='ei_tiedossa', max_length=255),
|
||||
model_name="member",
|
||||
name="POR",
|
||||
field=models.CharField(default="ei_tiedossa", max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='paid',
|
||||
model_name="member",
|
||||
name="paid",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='MemberRequest',
|
||||
name="MemberRequest",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='payment',
|
||||
name='member',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='members.Member'),
|
||||
model_name="payment",
|
||||
name="member",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="members.Member",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,80 +8,80 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0003_auto_20170329_1928'),
|
||||
("members", "0003_auto_20170329_1928"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='payment',
|
||||
name='email',
|
||||
model_name="payment",
|
||||
name="email",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='payment',
|
||||
name='first_name',
|
||||
model_name="payment",
|
||||
name="first_name",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='payment',
|
||||
name='last_name',
|
||||
model_name="payment",
|
||||
name="last_name",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='AYY',
|
||||
field=models.BooleanField(default=False, verbose_name='AYY'),
|
||||
model_name="member",
|
||||
name="AYY",
|
||||
field=models.BooleanField(default=False, verbose_name="AYY"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='POR',
|
||||
field=models.CharField(max_length=255, verbose_name='Place of residence'),
|
||||
model_name="member",
|
||||
name="POR",
|
||||
field=models.CharField(max_length=255, verbose_name="Place of residence"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, verbose_name='Email'),
|
||||
model_name="member",
|
||||
name="email",
|
||||
field=models.EmailField(max_length=254, verbose_name="Email"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='first_name',
|
||||
field=models.CharField(max_length=127, verbose_name='First name'),
|
||||
model_name="member",
|
||||
name="first_name",
|
||||
field=models.CharField(max_length=127, verbose_name="First name"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='jas',
|
||||
field=models.BooleanField(default=False, verbose_name='JAS'),
|
||||
model_name="member",
|
||||
name="jas",
|
||||
field=models.BooleanField(default=False, verbose_name="JAS"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='last_name',
|
||||
field=models.CharField(max_length=127, verbose_name='Last name'),
|
||||
model_name="member",
|
||||
name="last_name",
|
||||
field=models.CharField(max_length=127, verbose_name="Last name"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='AYY',
|
||||
field=models.BooleanField(default=False, verbose_name='AYY'),
|
||||
model_name="request",
|
||||
name="AYY",
|
||||
field=models.BooleanField(default=False, verbose_name="AYY"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='POR',
|
||||
field=models.CharField(max_length=255, verbose_name='Place of residence'),
|
||||
model_name="request",
|
||||
name="POR",
|
||||
field=models.CharField(max_length=255, verbose_name="Place of residence"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, verbose_name='Email'),
|
||||
model_name="request",
|
||||
name="email",
|
||||
field=models.EmailField(max_length=254, verbose_name="Email"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='first_name',
|
||||
field=models.CharField(max_length=127, verbose_name='First name'),
|
||||
model_name="request",
|
||||
name="first_name",
|
||||
field=models.CharField(max_length=127, verbose_name="First name"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='jas',
|
||||
field=models.BooleanField(default=False, verbose_name='JAS'),
|
||||
model_name="request",
|
||||
name="jas",
|
||||
field=models.BooleanField(default=False, verbose_name="JAS"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='last_name',
|
||||
field=models.CharField(max_length=127, verbose_name='Last name'),
|
||||
model_name="request",
|
||||
name="last_name",
|
||||
field=models.CharField(max_length=127, verbose_name="Last name"),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,22 +9,31 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0004_auto_20170512_1454'),
|
||||
("members", "0004_auto_20170512_1454"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='member',
|
||||
name='paid',
|
||||
model_name="member",
|
||||
name="paid",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='date',
|
||||
field=models.DateTimeField(default=datetime.datetime(2017, 5, 13, 10, 29, 50, 116064)),
|
||||
model_name="payment",
|
||||
name="date",
|
||||
field=models.DateTimeField(
|
||||
default=datetime.datetime(2017, 5, 13, 10, 29, 50, 116064)
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='source',
|
||||
field=models.CharField(choices=[('AYY', 'AYY'), ('cash', 'Cash'), ('bank_transfer', 'Bank transfer')], max_length=255),
|
||||
model_name="payment",
|
||||
name="source",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("AYY", "AYY"),
|
||||
("cash", "Cash"),
|
||||
("bank_transfer", "Bank transfer"),
|
||||
],
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,13 +9,15 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0005_auto_20170513_1029'),
|
||||
("members", "0005_auto_20170513_1029"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='date',
|
||||
field=models.DateTimeField(default=datetime.datetime(2017, 5, 17, 13, 9, 21, 49238)),
|
||||
model_name="payment",
|
||||
name="date",
|
||||
field=models.DateTimeField(
|
||||
default=datetime.datetime(2017, 5, 17, 13, 9, 21, 49238)
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,13 +9,15 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0006_auto_20170517_1309'),
|
||||
("members", "0006_auto_20170517_1309"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='date',
|
||||
field=models.DateTimeField(default=datetime.datetime(2017, 5, 18, 15, 38, 7, 668612)),
|
||||
model_name="payment",
|
||||
name="date",
|
||||
field=models.DateTimeField(
|
||||
default=datetime.datetime(2017, 5, 18, 15, 38, 7, 668612)
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,18 +9,18 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0007_auto_20170518_1538'),
|
||||
("members", "0007_auto_20170518_1538"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='created',
|
||||
model_name="member",
|
||||
name="created",
|
||||
field=models.DateTimeField(default=datetime.datetime.now),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='date',
|
||||
model_name="payment",
|
||||
name="date",
|
||||
field=models.DateTimeField(default=datetime.datetime.now),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,13 +9,15 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0008_auto_20170518_1540'),
|
||||
("members", "0008_auto_20170518_1540"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='created',
|
||||
field=models.DateTimeField(default=datetime.datetime.now, verbose_name='Created'),
|
||||
model_name="member",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
default=datetime.datetime.now, verbose_name="Created"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,18 +9,28 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0009_auto_20170526_1903'),
|
||||
("members", "0009_auto_20170526_1903"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='date',
|
||||
field=models.DateTimeField(default=datetime.datetime.now, verbose_name='Date'),
|
||||
model_name="payment",
|
||||
name="date",
|
||||
field=models.DateTimeField(
|
||||
default=datetime.datetime.now, verbose_name="Date"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='source',
|
||||
field=models.CharField(choices=[('AYY', 'AYY'), ('cash', 'Cash'), ('bank_transfer', 'Bank transfer')], max_length=255, verbose_name='Source'),
|
||||
model_name="payment",
|
||||
name="source",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("AYY", "AYY"),
|
||||
("cash", "Cash"),
|
||||
("bank_transfer", "Bank transfer"),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name="Source",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,13 +9,15 @@ import django.utils.timezone
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0010_auto_20170526_1910'),
|
||||
("members", "0010_auto_20170526_1910"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='submitted',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Submitted'),
|
||||
model_name="request",
|
||||
name="submitted",
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="Submitted"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,16 +9,38 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0011_auto_20170526_2013'),
|
||||
("members", "0011_auto_20170526_2013"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MemberConflict',
|
||||
name="MemberConflict",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberconflict_first_member', to='members.Member')),
|
||||
('second_member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberconflict_second_member', to='members.Member')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_member",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="memberconflict_first_member",
|
||||
to="members.Member",
|
||||
),
|
||||
),
|
||||
(
|
||||
"second_member",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="memberconflict_second_member",
|
||||
to="members.Member",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,13 +9,19 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0012_memberconflict'),
|
||||
("members", "0012_memberconflict"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='member',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='payments', to='members.Member'),
|
||||
model_name="payment",
|
||||
name="member",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="payments",
|
||||
to="members.Member",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,18 +8,18 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0013_auto_20170601_1822'),
|
||||
("members", "0013_auto_20170601_1822"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, unique=True, verbose_name='Email'),
|
||||
model_name="member",
|
||||
name="email",
|
||||
field=models.EmailField(max_length=254, unique=True, verbose_name="Email"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='request',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, unique=True, verbose_name='Email'),
|
||||
model_name="request",
|
||||
name="email",
|
||||
field=models.EmailField(max_length=254, unique=True, verbose_name="Email"),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,19 +8,19 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0014_auto_20170920_1457'),
|
||||
("members", "0014_auto_20170920_1457"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='memberconflict',
|
||||
name='first_member',
|
||||
model_name="memberconflict",
|
||||
name="first_member",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='memberconflict',
|
||||
name='second_member',
|
||||
model_name="memberconflict",
|
||||
name="second_member",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='MemberConflict',
|
||||
name="MemberConflict",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,18 +9,22 @@ import django.utils.timezone
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0015_auto_20170925_1917'),
|
||||
("members", "0015_auto_20170925_1917"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='created',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Created'),
|
||||
model_name="member",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="Created"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date'),
|
||||
model_name="payment",
|
||||
name="date",
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="Date"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,12 +8,16 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0016_auto_20170925_1924'),
|
||||
("members", "0016_auto_20170925_1924"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='member',
|
||||
options={'permissions': (('check_by_email', 'Can check if user exists by email'),)},
|
||||
name="member",
|
||||
options={
|
||||
"permissions": (
|
||||
("check_by_email", "Can check if user exists by email"),
|
||||
)
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,20 +8,29 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0017_auto_20170926_1316'),
|
||||
("members", "0017_auto_20170926_1316"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='member',
|
||||
options={'permissions': (('check_by_email', 'Can check if user exists by email'), ('read_member', 'Can see member in list'))},
|
||||
name="member",
|
||||
options={
|
||||
"permissions": (
|
||||
("check_by_email", "Can check if user exists by email"),
|
||||
("read_member", "Can see member in list"),
|
||||
)
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='payment',
|
||||
options={'permissions': (('read_payment', 'Can see payment in list'),)},
|
||||
name="payment",
|
||||
options={"permissions": (("read_payment", "Can see payment in list"),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='request',
|
||||
options={'permissions': (('read_application', 'Can see member application in list'),)},
|
||||
name="request",
|
||||
options={
|
||||
"permissions": (
|
||||
("read_application", "Can see member application in list"),
|
||||
)
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,12 +8,19 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0018_auto_20170927_1918'),
|
||||
("members", "0018_auto_20170927_1918"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='member',
|
||||
options={'permissions': (('check_by_email', 'Can check if user exists by email'), ('read_member', 'Can see member in list')), 'verbose_name': 'Member', 'verbose_name_plural': 'Members'},
|
||||
name="member",
|
||||
options={
|
||||
"permissions": (
|
||||
("check_by_email", "Can check if user exists by email"),
|
||||
("read_member", "Can see member in list"),
|
||||
),
|
||||
"verbose_name": "Member",
|
||||
"verbose_name_plural": "Members",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
+36
-30
@@ -12,8 +12,9 @@ class BaseMember(models.Model):
|
||||
first_name = models.CharField(_("First name"), max_length=127)
|
||||
last_name = models.CharField(_("Last name"), max_length=127)
|
||||
email = models.EmailField(_("Email"), unique=True)
|
||||
POR = models.CharField(_("Place of residence"),
|
||||
max_length=255) # place of residence
|
||||
POR = models.CharField(
|
||||
_("Place of residence"), max_length=255
|
||||
) # place of residence
|
||||
AYY = models.BooleanField(_("AYY"), default=False)
|
||||
jas = models.BooleanField(_("JAS"), default=False)
|
||||
|
||||
@@ -34,7 +35,7 @@ class BaseMember(models.Model):
|
||||
self.email,
|
||||
self.POR,
|
||||
int(self.AYY),
|
||||
int(self.jas)
|
||||
int(self.jas),
|
||||
]
|
||||
|
||||
|
||||
@@ -42,11 +43,9 @@ class Request(BaseMember):
|
||||
"""Member request model represents one member request."""
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('read_application', 'Can see member application in list'),
|
||||
)
|
||||
permissions = (("read_application", "Can see member application in list"),)
|
||||
|
||||
submitted = models.DateTimeField(_('Submitted'), default=timezone.now)
|
||||
submitted = models.DateTimeField(_("Submitted"), default=timezone.now)
|
||||
|
||||
def to_member(self):
|
||||
"""Convert array to member model."""
|
||||
@@ -59,47 +58,54 @@ class Payment(models.Model):
|
||||
"""Payment model representing one payment event."""
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('read_payment', 'Can see payment in list'),
|
||||
)
|
||||
permissions = (("read_payment", "Can see payment in list"),)
|
||||
|
||||
date = models.DateTimeField(_('Date'), default=timezone.now)
|
||||
source = models.CharField(_('Source'), choices=[
|
||||
('AYY', _('AYY')),
|
||||
('cash', _('Cash')),
|
||||
('bank_transfer', _('Bank transfer')),
|
||||
], max_length=255)
|
||||
date = models.DateTimeField(_("Date"), default=timezone.now)
|
||||
source = models.CharField(
|
||||
_("Source"),
|
||||
choices=[
|
||||
("AYY", _("AYY")),
|
||||
("cash", _("Cash")),
|
||||
("bank_transfer", _("Bank transfer")),
|
||||
],
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
member = models.ForeignKey('Member',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='payments')
|
||||
member = models.ForeignKey(
|
||||
"Member",
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name="payments",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
"""Return payment id and date."""
|
||||
return 'Payment no. {}, {}'.format(self.id, str(self.date))
|
||||
return "Payment no. {}, {}".format(self.id, str(self.date))
|
||||
|
||||
@staticmethod
|
||||
def find_payments_by_name(query_name):
|
||||
qs = Payment.objects.all()
|
||||
for term in query_name.split():
|
||||
qs = qs.filter(Q(member__first_name__icontains=term) | Q(member__last_name__icontains=term))
|
||||
qs = qs.filter(
|
||||
Q(member__first_name__icontains=term)
|
||||
| Q(member__last_name__icontains=term)
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
class Member(BaseMember):
|
||||
"""Member model represets one member on the registry."""
|
||||
|
||||
created = models.DateTimeField(_('Created'), default=timezone.now)
|
||||
created = models.DateTimeField(_("Created"), default=timezone.now)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('check_by_email', 'Can check if user exists by email'),
|
||||
('read_member', 'Can see member in list'),
|
||||
("check_by_email", "Can check if user exists by email"),
|
||||
("read_member", "Can see member in list"),
|
||||
)
|
||||
verbose_name = _('Member')
|
||||
verbose_name_plural = _('Members')
|
||||
verbose_name = _("Member")
|
||||
verbose_name_plural = _("Members")
|
||||
|
||||
@staticmethod
|
||||
def from_array(array):
|
||||
@@ -126,5 +132,5 @@ class Member(BaseMember):
|
||||
@staticmethod
|
||||
def get_members_with_latest_payment(members_query):
|
||||
"""Return QuerySet of given members QS with last_paid attribute."""
|
||||
latest = Payment.objects.filter(member=OuterRef('pk')).order_by('-date')
|
||||
return members_query.annotate(last_paid=Subquery(latest.values('date')[:1]))
|
||||
latest = Payment.objects.filter(member=OuterRef("pk")).order_by("-date")
|
||||
return members_query.annotate(last_paid=Subquery(latest.values("date")[:1]))
|
||||
|
||||
@@ -5,4 +5,4 @@ import logging
|
||||
|
||||
class CheckByEmailPermission(BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return request.user.has_perm('members.check_by_email')
|
||||
return request.user.has_perm("members.check_by_email")
|
||||
|
||||
@@ -6,7 +6,7 @@ from .models import Member, Payment, Request
|
||||
class MemberResource(resources.ModelResource):
|
||||
class Meta:
|
||||
model = Member
|
||||
exclude = ['id', 'created']
|
||||
exclude = ["id", "created"]
|
||||
|
||||
|
||||
class PaymentResource(resources.ModelResource):
|
||||
@@ -14,13 +14,13 @@ class PaymentResource(resources.ModelResource):
|
||||
|
||||
class Meta:
|
||||
model = Payment
|
||||
exclude = ['id']
|
||||
exclude = ["id"]
|
||||
|
||||
def dehydrate_member(self, payment):
|
||||
return '{} {}'.format(payment.member.first_name, payment.member.last_name)
|
||||
return "{} {}".format(payment.member.first_name, payment.member.last_name)
|
||||
|
||||
|
||||
class ApplicationResource(resources.ModelResource):
|
||||
class Meta:
|
||||
model = Request
|
||||
exclude = ['id']
|
||||
exclude = ["id"]
|
||||
|
||||
+12
-3
@@ -7,11 +7,20 @@ from members.models import Member
|
||||
class MemberSerializer(serializers.ModelSerializer):
|
||||
"""Model serializer for member."""
|
||||
|
||||
paid = serializers.DateTimeField(source='last_paid')
|
||||
paid = serializers.DateTimeField(source="last_paid")
|
||||
|
||||
class Meta:
|
||||
"""Meta of member serializer."""
|
||||
|
||||
model = Member
|
||||
fields = ('id', 'first_name', 'last_name', 'email',
|
||||
'POR', 'AYY', 'jas', 'created', 'paid')
|
||||
fields = (
|
||||
"id",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"POR",
|
||||
"AYY",
|
||||
"jas",
|
||||
"created",
|
||||
"paid",
|
||||
)
|
||||
|
||||
+32
-13
@@ -12,11 +12,16 @@ from members.models import Member, Payment, Request
|
||||
class MemberTable(tables.Table):
|
||||
"""Table for member."""
|
||||
|
||||
last_paid = tables.DateTimeColumn(verbose_name=_('Last paid'))
|
||||
last_paid = tables.DateTimeColumn(verbose_name=_("Last paid"))
|
||||
|
||||
options = tables.TemplateColumn(
|
||||
('<a class="data-table-button btn btn-primary" '
|
||||
'href="/members/edit/{{ record.id }}">') + _('Edit') + '</a>', verbose_name=""
|
||||
(
|
||||
'<a class="data-table-button btn btn-primary" '
|
||||
'href="/members/edit/{{ record.id }}">'
|
||||
)
|
||||
+ _("Edit")
|
||||
+ "</a>",
|
||||
verbose_name="",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -26,24 +31,34 @@ class MemberTable(tables.Table):
|
||||
|
||||
def render_last_paid(self, record):
|
||||
try:
|
||||
return timezone.localtime(record.payments.filter(member=record).latest('date').date).strftime('%-d.%-m.%Y %H:%M')
|
||||
return timezone.localtime(
|
||||
record.payments.filter(member=record).latest("date").date
|
||||
).strftime("%-d.%-m.%Y %H:%M")
|
||||
except ObjectDoesNotExist:
|
||||
return timezone.localtime(record.created).strftime('%-d.%-m.%Y %H:%M') + _(" (not paid)")
|
||||
return timezone.localtime(record.created).strftime("%-d.%-m.%Y %H:%M") + _(
|
||||
" (not paid)"
|
||||
)
|
||||
|
||||
def order_last_paid(self, queryset, is_descending):
|
||||
queryset = Member.get_members_with_latest_payment(queryset).order_by(('-' if is_descending else '') + 'last_paid')
|
||||
queryset = Member.get_members_with_latest_payment(queryset).order_by(
|
||||
("-" if is_descending else "") + "last_paid"
|
||||
)
|
||||
return (queryset, True)
|
||||
|
||||
|
||||
class PaymentTable(tables.Table):
|
||||
"""Table for payments."""
|
||||
|
||||
member = tables.Column(accessor='member', verbose_name=_('Member'))
|
||||
member = tables.Column(accessor="member", verbose_name=_("Member"))
|
||||
|
||||
options = tables.TemplateColumn(
|
||||
('<a class="data-table-button btn btn-primary" '
|
||||
'href="/members/edit_payment/{{ record.id }}">') + _('Edit') + '</a>',
|
||||
verbose_name=""
|
||||
(
|
||||
'<a class="data-table-button btn btn-primary" '
|
||||
'href="/members/edit_payment/{{ record.id }}">'
|
||||
)
|
||||
+ _("Edit")
|
||||
+ "</a>",
|
||||
verbose_name="",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -56,9 +71,13 @@ class RequestTable(tables.Table):
|
||||
"""Table for member applications."""
|
||||
|
||||
options = tables.TemplateColumn(
|
||||
('<a class="data-table-button btn btn-primary" '
|
||||
'href="/members/edit_application/{{ record.id }}">') + _('Edit') + '</a>',
|
||||
verbose_name=""
|
||||
(
|
||||
'<a class="data-table-button btn btn-primary" '
|
||||
'href="/members/edit_application/{{ record.id }}">'
|
||||
)
|
||||
+ _("Edit")
|
||||
+ "</a>",
|
||||
verbose_name="",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
+76
-47
@@ -17,16 +17,23 @@ class MemberRegisterTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Setup testing environment by creating member and admin."""
|
||||
memb = Member.objects.create(first_name="Tidus", last_name="Tester", email="tidus@tester.fi")
|
||||
payment = Payment.objects.create(member=memb, source='AYY')
|
||||
memb = Member.objects.create(
|
||||
first_name="Tidus", last_name="Tester", email="tidus@tester.fi"
|
||||
)
|
||||
payment = Payment.objects.create(member=memb, source="AYY")
|
||||
appl = Request.objects.create(
|
||||
first_name="Liisa", last_name="Mattila",
|
||||
email="liisa.mattila@pylly.com", POR="Kouvola",
|
||||
AYY=True, jas=False)
|
||||
first_name="Liisa",
|
||||
last_name="Mattila",
|
||||
email="liisa.mattila@pylly.com",
|
||||
POR="Kouvola",
|
||||
AYY=True,
|
||||
jas=False,
|
||||
)
|
||||
|
||||
username, password = 'test_admin', 'password123'
|
||||
username, password = "test_admin", "password123"
|
||||
test_admin = User.objects.create_superuser(
|
||||
username, 'myemail@test.com', password)
|
||||
username, "myemail@test.com", password
|
||||
)
|
||||
self.c = Client()
|
||||
self.c.login(username=username, password=password)
|
||||
|
||||
@@ -39,84 +46,106 @@ class MemberRegisterTestCase(TestCase):
|
||||
"""Test csv import only with single line in csv file."""
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
with open(os.path.join(current_dir, 'test_resources', 'single_line_import.csv')) as csvFile:
|
||||
response = self.c.post('/members/import_csv', {
|
||||
'csvFile': csvFile,
|
||||
'delimiter': ';',
|
||||
'payment_source': 'AYY'
|
||||
}, follow=True)
|
||||
with open(
|
||||
os.path.join(current_dir, "test_resources", "single_line_import.csv")
|
||||
) as csvFile:
|
||||
response = self.c.post(
|
||||
"/members/import_csv",
|
||||
{"csvFile": csvFile, "delimiter": ";", "payment_source": "AYY"},
|
||||
follow=True,
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_import_csv_multi_line(self):
|
||||
"""Test csv import with multilined csv."""
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
with open(os.path.join(current_dir, 'test_resources', 'multi_line_import.csv')) as csvFile:
|
||||
response = self.c.post('/members/import_csv', {
|
||||
'csvFile': csvFile,
|
||||
'delimiter': ';',
|
||||
'payment_source': 'AYY'
|
||||
}, follow=True)
|
||||
with open(
|
||||
os.path.join(current_dir, "test_resources", "multi_line_import.csv")
|
||||
) as csvFile:
|
||||
response = self.c.post(
|
||||
"/members/import_csv",
|
||||
{"csvFile": csvFile, "delimiter": ";", "payment_source": "AYY"},
|
||||
follow=True,
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_autocomplete_search_found(self):
|
||||
"""Test member autocomplete search"""
|
||||
search_terms = 'Tidus'
|
||||
response = self.c.get('/members/member-autocomplete?q={}'.format(search_terms), follow=True)
|
||||
results = response.json()['results']
|
||||
search_terms = "Tidus"
|
||||
response = self.c.get(
|
||||
"/members/member-autocomplete?q={}".format(search_terms), follow=True
|
||||
)
|
||||
results = response.json()["results"]
|
||||
self.assertEqual(len(results), 1)
|
||||
|
||||
def test_autocomplete_search_not_found(self):
|
||||
"""Test member autocomplete search"""
|
||||
search_terms = 'Notfound'
|
||||
response = self.c.get('/members/member-autocomplete?q={}'.format(search_terms), follow=True)
|
||||
results = response.json()['results']
|
||||
search_terms = "Notfound"
|
||||
response = self.c.get(
|
||||
"/members/member-autocomplete?q={}".format(search_terms), follow=True
|
||||
)
|
||||
results = response.json()["results"]
|
||||
self.assertEqual(len(results), 0)
|
||||
|
||||
def test_export_members_excel(self):
|
||||
"""Test if the user can download an excel file of the member register"""
|
||||
resp = self.c.get('/members/export_members')
|
||||
content_type = 'application/vnd.ms-excel'
|
||||
self.assertIn(content_type, resp['Content-Type'])
|
||||
resp = self.c.get("/members/export_members")
|
||||
content_type = "application/vnd.ms-excel"
|
||||
self.assertIn(content_type, resp["Content-Type"])
|
||||
|
||||
content = resp.content
|
||||
arrays = pyexcel.get_array(file_content=content, file_type='xlsx')
|
||||
tidus_array = ['Tidus', 'Tester', 'tidus@tester.fi', '', '0', '0']
|
||||
arrays = pyexcel.get_array(file_content=content, file_type="xlsx")
|
||||
tidus_array = ["Tidus", "Tester", "tidus@tester.fi", "", "0", "0"]
|
||||
self.assertIn(tidus_array, arrays)
|
||||
|
||||
def test_export_payments_excel(self):
|
||||
"""Test if the user can download an excel file of the payment register"""
|
||||
resp = self.c.get('/members/export_payments')
|
||||
content_type = 'application/vnd.ms-excel'
|
||||
self.assertIn(content_type, resp['Content-Type'])
|
||||
resp = self.c.get("/members/export_payments")
|
||||
content_type = "application/vnd.ms-excel"
|
||||
self.assertIn(content_type, resp["Content-Type"])
|
||||
|
||||
content = resp.content
|
||||
arrays = pyexcel.get_array(file_content=content, file_type='xlsx')
|
||||
created = Payment.objects.get(member__email='tidus@tester.fi').date.strftime('%Y-%m-%d %H:%M:%S')
|
||||
tidus_array = ['Tidus Tester', created, 'AYY']
|
||||
arrays = pyexcel.get_array(file_content=content, file_type="xlsx")
|
||||
created = Payment.objects.get(member__email="tidus@tester.fi").date.strftime(
|
||||
"%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
tidus_array = ["Tidus Tester", created, "AYY"]
|
||||
self.assertIn(tidus_array, arrays)
|
||||
|
||||
def test_export_applications_excel(self):
|
||||
"""Test if the user can download an excel file of the member application register"""
|
||||
resp = self.c.get('/members/export_applications')
|
||||
content_type = 'application/vnd.ms-excel'
|
||||
self.assertIn(content_type, resp['Content-Type'])
|
||||
resp = self.c.get("/members/export_applications")
|
||||
content_type = "application/vnd.ms-excel"
|
||||
self.assertIn(content_type, resp["Content-Type"])
|
||||
|
||||
content = resp.content
|
||||
arrays = pyexcel.get_array(file_content=content, file_type='xlsx')
|
||||
submitted = Request.objects.get(email='liisa.mattila@pylly.com').submitted.strftime('%Y-%m-%d %H:%M:%S')
|
||||
liisa_array = ['Liisa', 'Mattila', 'liisa.mattila@pylly.com', 'Kouvola', '1', '0', submitted]
|
||||
arrays = pyexcel.get_array(file_content=content, file_type="xlsx")
|
||||
submitted = Request.objects.get(
|
||||
email="liisa.mattila@pylly.com"
|
||||
).submitted.strftime("%Y-%m-%d %H:%M:%S")
|
||||
liisa_array = [
|
||||
"Liisa",
|
||||
"Mattila",
|
||||
"liisa.mattila@pylly.com",
|
||||
"Kouvola",
|
||||
"1",
|
||||
"0",
|
||||
submitted,
|
||||
]
|
||||
self.assertIn(liisa_array, arrays)
|
||||
|
||||
def test_submit_member_application(self):
|
||||
"""Test if submitting a member application works"""
|
||||
data = {
|
||||
'first_name': 'Seppo', 'last_name': 'Saastamoinen',
|
||||
'email': 'seppo@saastamoin.en', 'jas': 'on',
|
||||
'POR': 'Dipolin viinibaari'
|
||||
"first_name": "Seppo",
|
||||
"last_name": "Saastamoinen",
|
||||
"email": "seppo@saastamoin.en",
|
||||
"jas": "on",
|
||||
"POR": "Dipolin viinibaari",
|
||||
}
|
||||
resp = self.c.post('/members/submit_application', data=data)
|
||||
resp = self.c.post("/members/submit_application", data=data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
self.assertTrue(Request.objects.filter(email='seppo@saastamoin.en').exists())
|
||||
self.assertTrue(Request.objects.filter(email="seppo@saastamoin.en").exists())
|
||||
|
||||
@@ -6,10 +6,10 @@ from rest_framework.throttling import UserRateThrottle
|
||||
class BurstRateThrottle(UserRateThrottle):
|
||||
"""Class for burst rate throttle."""
|
||||
|
||||
scope = 'burst'
|
||||
scope = "burst"
|
||||
|
||||
|
||||
class SustainedRateThrottle(UserRateThrottle):
|
||||
"""Class for sustained rate throttle."""
|
||||
|
||||
scope = 'sustained'
|
||||
scope = "sustained"
|
||||
|
||||
+34
-55
@@ -41,86 +41,65 @@ from members.views import application_submit
|
||||
# from members.views import validateEmail, validate_success, validate_fail
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
# landing page
|
||||
url(r'^$', member_list),
|
||||
url(r'^list$', member_list),
|
||||
|
||||
url(r"^$", member_list),
|
||||
url(r"^list$", member_list),
|
||||
# add member form view
|
||||
url(r'^add$', member_add),
|
||||
|
||||
url(r"^add$", member_add),
|
||||
# add many members view
|
||||
url(r'^add_many$', member_add_many),
|
||||
|
||||
url(r"^add_many$", member_add_many),
|
||||
# edit member information view
|
||||
url(r'^edit/(?P<index>\d+)$', member_edit),
|
||||
|
||||
url(r"^edit/(?P<index>\d+)$", member_edit),
|
||||
# delete confirmation view
|
||||
url(r'^delete_member_confirm/(?P<index>\d+)$', member_delete_confirm),
|
||||
|
||||
url(r"^delete_member_confirm/(?P<index>\d+)$", member_delete_confirm),
|
||||
# list all member applications
|
||||
url(r'^applications$', application_list),
|
||||
|
||||
url(r"^applications$", application_list),
|
||||
# edit member application
|
||||
url(r'^edit_application/(?P<index>\d+)$', application_edit),
|
||||
|
||||
url(r"^edit_application/(?P<index>\d+)$", application_edit),
|
||||
# post request targets
|
||||
url(r'^submit_member$', member_submit),
|
||||
url(r'^update_member$', member_update),
|
||||
url(r'^delete_member$', member_delete),
|
||||
url(r'^submit_payment$', payment_submit),
|
||||
url(r'^update_payment$', payment_update),
|
||||
url(r'^delete_payment$', payment_delete),
|
||||
url(r'^submit_application$', application_submit),
|
||||
url(r'^accept_application$', application_accept),
|
||||
url(r'^delete_application$', application_delete),
|
||||
|
||||
url(r"^submit_member$", member_submit),
|
||||
url(r"^update_member$", member_update),
|
||||
url(r"^delete_member$", member_delete),
|
||||
url(r"^submit_payment$", payment_submit),
|
||||
url(r"^update_payment$", payment_update),
|
||||
url(r"^delete_payment$", payment_delete),
|
||||
url(r"^submit_application$", application_submit),
|
||||
url(r"^accept_application$", application_accept),
|
||||
url(r"^delete_application$", application_delete),
|
||||
# the actual member application form
|
||||
url(r'^application/$', application_form),
|
||||
|
||||
url(r"^application/$", application_form),
|
||||
# delete confirmation view for applications
|
||||
url(r'^delete_application_confirm/(?P<index>\d+)$',
|
||||
application_delete_confirm),
|
||||
|
||||
url(r"^delete_application_confirm/(?P<index>\d+)$", application_delete_confirm),
|
||||
# list all payment events
|
||||
url(r'^payments$', payment_list),
|
||||
|
||||
url(r"^payments$", payment_list),
|
||||
# add payment event
|
||||
url(r'^add_payment$', payment_add),
|
||||
|
||||
url(r"^add_payment$", payment_add),
|
||||
# edit payment event
|
||||
url(r'^edit_payment/(?P<index>\d+)$', payment_edit),
|
||||
|
||||
url(r"^edit_payment/(?P<index>\d+)$", payment_edit),
|
||||
# delete confirmation view
|
||||
url(r'^delete_payment_confirm/(?P<index>\d+)$', payment_delete_confirm),
|
||||
|
||||
url(r"^delete_payment_confirm/(?P<index>\d+)$", payment_delete_confirm),
|
||||
# post endpoint for confirming multiple entries
|
||||
url(r'^add_many_confirm$', add_many_confirm),
|
||||
|
||||
url(r"^add_many_confirm$", add_many_confirm),
|
||||
# settings page
|
||||
url(r'^settings$', settings_page),
|
||||
|
||||
url(r"^settings$", settings_page),
|
||||
# send CSV member data by POST
|
||||
url(r'^import_csv', import_csv),
|
||||
|
||||
url(r"^import_csv", import_csv),
|
||||
# export members as excel file
|
||||
url(r'export_members', export_members_excel),
|
||||
url(r'export_payments', export_payments_excel),
|
||||
url(r'export_applications', export_applications_excel),
|
||||
|
||||
url(r"export_members", export_members_excel),
|
||||
url(r"export_payments", export_payments_excel),
|
||||
url(r"export_applications", export_applications_excel),
|
||||
# rest api url
|
||||
url(r'^api/members/(?P<pk>\d+)$', MemberDetail.as_view()),
|
||||
|
||||
url(r"^api/members/(?P<pk>\d+)$", MemberDetail.as_view()),
|
||||
# member select autocomplete view
|
||||
url(
|
||||
r'^member-autocomplete/$',
|
||||
r"^member-autocomplete/$",
|
||||
MemberAutoComplete.as_view(),
|
||||
name='member-autocomplete',
|
||||
name="member-autocomplete",
|
||||
),
|
||||
|
||||
url(r'^check', CheckByEmail.as_view())
|
||||
url(r"^check", CheckByEmail.as_view()),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
@@ -21,52 +21,53 @@ from members.views import error_view
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.read_application', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.read_application", raise_exception=True)
|
||||
def application_list(request, *args, **kwargs):
|
||||
"""List member applications not yet processed."""
|
||||
applications = Request.objects.all()
|
||||
application_count = len(applications)
|
||||
table = RequestTable(applications,
|
||||
request=request,
|
||||
exclude=['id'],
|
||||
attrs={'class': 'table table-bordered table-hover'})
|
||||
table = RequestTable(
|
||||
applications,
|
||||
request=request,
|
||||
exclude=["id"],
|
||||
attrs={"class": "table table-bordered table-hover"},
|
||||
)
|
||||
|
||||
table.paginate(page=request.GET.get('page', 1), per_page=25)
|
||||
table.paginate(page=request.GET.get("page", 1), per_page=25)
|
||||
table_html = convert_table_to_html(table, request)
|
||||
context = {
|
||||
'table': table_html,
|
||||
'application_count': application_count,
|
||||
'notification': request.GET.get('notification', None)
|
||||
"table": table_html,
|
||||
"application_count": application_count,
|
||||
"notification": request.GET.get("notification", None),
|
||||
}
|
||||
return render(request, 'application_list.html', context)
|
||||
return render(request, "application_list.html", context)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.change_request', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.change_request", raise_exception=True)
|
||||
def application_edit(request, *args, **kwargs):
|
||||
"""Edit member request information."""
|
||||
i = kwargs.pop('index', None)
|
||||
i = kwargs.pop("index", None)
|
||||
if i is None:
|
||||
return error_view(request, _('No application id specified'))
|
||||
return error_view(request, _("No application id specified"))
|
||||
else:
|
||||
application = Request.objects.get(id=i)
|
||||
form = ApplicationForm(instance=application)
|
||||
return render(
|
||||
request,
|
||||
'application_edit.html',
|
||||
{'application_id': i, 'form': form})
|
||||
request, "application_edit.html", {"application_id": i, "form": form}
|
||||
)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.add_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.add_member", raise_exception=True)
|
||||
def application_accept(request, *args, **kwargs):
|
||||
"""Accept application."""
|
||||
id = request.POST.get('id', None)
|
||||
id = request.POST.get("id", None)
|
||||
if id is not None:
|
||||
application = Request.objects.get(id=id)
|
||||
else:
|
||||
@@ -78,9 +79,12 @@ def application_accept(request, *args, **kwargs):
|
||||
application = form.save()
|
||||
|
||||
if Member.objects.filter(email=application.email).exists():
|
||||
return error_view(request, _(
|
||||
'Email {} is already in use by a member. Application cannot be accepted.'
|
||||
).format(application.email))
|
||||
return error_view(
|
||||
request,
|
||||
_(
|
||||
"Email {} is already in use by a member. Application cannot be accepted."
|
||||
).format(application.email),
|
||||
)
|
||||
|
||||
member = application.to_member()
|
||||
member.save()
|
||||
@@ -88,24 +92,25 @@ def application_accept(request, *args, **kwargs):
|
||||
|
||||
logging.info(
|
||||
"Accepted application in member "
|
||||
"register with the following info: {}"
|
||||
.format(form))
|
||||
notification = "{} {}.".format(_("Successfully accepted application"),
|
||||
str(application))
|
||||
"register with the following info: {}".format(form)
|
||||
)
|
||||
notification = "{} {}.".format(
|
||||
_("Successfully accepted application"), str(application)
|
||||
)
|
||||
|
||||
subject = _('Jäsenhakemuksesi Sähköinsinöörikiltaan on hyväksytty!')
|
||||
subject = _("Jäsenhakemuksesi Sähköinsinöörikiltaan on hyväksytty!")
|
||||
|
||||
message = render_to_string(
|
||||
'members:email_application_accept.html', {
|
||||
'first_name': application.first_name
|
||||
}
|
||||
"members:email_application_accept.html",
|
||||
{"first_name": application.first_name},
|
||||
)
|
||||
send_email(member.email, subject, message)
|
||||
|
||||
return HttpResponseRedirect(
|
||||
'/members/list?notification={}'.format(html.escape(notification)))
|
||||
"/members/list?notification={}".format(html.escape(notification))
|
||||
)
|
||||
except Exception as ex:
|
||||
logging.exception('Exception while accepting application')
|
||||
logging.exception("Exception while accepting application")
|
||||
return error_view(request, str(ex))
|
||||
else:
|
||||
logging.info(form)
|
||||
@@ -114,52 +119,55 @@ def application_accept(request, *args, **kwargs):
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.delete_request', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.delete_request", raise_exception=True)
|
||||
def application_delete(request, *args, **kwargs):
|
||||
"""Delete member application."""
|
||||
try:
|
||||
id = request.POST['id']
|
||||
id = request.POST["id"]
|
||||
except KeyError:
|
||||
return error_view(request, _('No application id specified'))
|
||||
return error_view(request, _("No application id specified"))
|
||||
|
||||
try:
|
||||
application = Request.objects.get(id=id)
|
||||
notification = "{} {}.".format(_("Successfully deleted application"),
|
||||
str(application))
|
||||
notification = "{} {}.".format(
|
||||
_("Successfully deleted application"), str(application)
|
||||
)
|
||||
application.delete()
|
||||
logging.info(
|
||||
"Delete application in member register with the following id: {}"
|
||||
.format(id))
|
||||
"Delete application in member register with the following id: {}".format(id)
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
'/members/applications?notification={}'
|
||||
.format(html.escape(notification)))
|
||||
"/members/applications?notification={}".format(html.escape(notification))
|
||||
)
|
||||
except:
|
||||
return error_view(request, _('Could not delete application object'))
|
||||
return error_view(request, _("Could not delete application object"))
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.delete_request', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.delete_request", raise_exception=True)
|
||||
def application_delete_confirm(request, *args, **kwargs):
|
||||
"""Confirm application deletion."""
|
||||
i = kwargs.pop('index', None)
|
||||
i = kwargs.pop("index", None)
|
||||
if i is None:
|
||||
return error_view(request, _('No application id specified'))
|
||||
return error_view(request, _("No application id specified"))
|
||||
else:
|
||||
application = Request.objects.get(id=i)
|
||||
form = ApplicationForm(instance=application)
|
||||
return render(request,
|
||||
'application_delete_confirm.html',
|
||||
{'application_id': i, 'form': form})
|
||||
return render(
|
||||
request,
|
||||
"application_delete_confirm.html",
|
||||
{"application_id": i, "form": form},
|
||||
)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
def application_form(request, *args, **kwargs):
|
||||
"""Render member application form."""
|
||||
form = ApplicationForm()
|
||||
return render(request, 'application_index.html', {'form': form})
|
||||
return render(request, "application_index.html", {"form": form})
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@@ -171,19 +179,22 @@ def application_submit(request, *args, **kwargs):
|
||||
form.save()
|
||||
try:
|
||||
application = form.instance
|
||||
email = form.cleaned_data.get('email', '')
|
||||
email = form.cleaned_data.get("email", "")
|
||||
|
||||
subject = _('Jäsenhakemuksesi Sähköinsinöörikiltaan on lähetetty onnistuneesti!')
|
||||
subject = _(
|
||||
"Jäsenhakemuksesi Sähköinsinöörikiltaan on lähetetty onnistuneesti!"
|
||||
)
|
||||
|
||||
message = render_to_string(
|
||||
'members:email_application_submit.html', {
|
||||
'application': application,
|
||||
'ayy': _('Kyllä') if application.AYY else _('Ei'),
|
||||
'jas': _('Kyllä') if application.jas else _('Ei')
|
||||
}
|
||||
"members:email_application_submit.html",
|
||||
{
|
||||
"application": application,
|
||||
"ayy": _("Kyllä") if application.AYY else _("Ei"),
|
||||
"jas": _("Kyllä") if application.jas else _("Ei"),
|
||||
},
|
||||
)
|
||||
send_email(email, subject, message)
|
||||
finally:
|
||||
return render(request, 'application_success.html', {})
|
||||
return render(request, "application_success.html", {})
|
||||
else:
|
||||
return error_view(request, form.errors)
|
||||
|
||||
+90
-71
@@ -3,7 +3,12 @@ from django.contrib.auth.decorators import permission_required, login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse, HttpResponseForbidden
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
HttpResponseRedirect,
|
||||
JsonResponse,
|
||||
HttpResponseForbidden,
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.forms.models import model_to_dict
|
||||
@@ -28,22 +33,24 @@ from members.views.utils import *
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.read_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.read_member", raise_exception=True)
|
||||
def member_list(request, *args, **kwargs):
|
||||
"""Render members list."""
|
||||
search = request.GET.get('q', None)
|
||||
search = request.GET.get("q", None)
|
||||
if search:
|
||||
members = Member.find_members_by_name(search)
|
||||
else:
|
||||
members = Member.objects.all()
|
||||
|
||||
table = MemberTable(members,
|
||||
request=request,
|
||||
exclude=['id'],
|
||||
attrs={'class': 'table table-bordered table-hover'})
|
||||
table = MemberTable(
|
||||
members,
|
||||
request=request,
|
||||
exclude=["id"],
|
||||
attrs={"class": "table table-bordered table-hover"},
|
||||
)
|
||||
|
||||
table.paginate(page=request.GET.get('page', 1), per_page=25)
|
||||
table.paginate(page=request.GET.get("page", 1), per_page=25)
|
||||
table_html = convert_table_to_html(table, request)
|
||||
|
||||
queryset = Member.get_members_with_latest_payment(members)
|
||||
@@ -51,61 +58,64 @@ def member_list(request, *args, **kwargs):
|
||||
f_day = 1
|
||||
f_month = 9
|
||||
now = timezone.now()
|
||||
if (now.month >= f_month):
|
||||
if now.month >= f_month:
|
||||
filter_date = timezone.make_aware(datetime.datetime(now.year, f_month, f_day))
|
||||
else:
|
||||
filter_date = timezone.make_aware(datetime.datetime(now.year - 1, f_month, f_day))
|
||||
filter_date = timezone.make_aware(
|
||||
datetime.datetime(now.year - 1, f_month, f_day)
|
||||
)
|
||||
context = {
|
||||
'table': table_html,
|
||||
'member_count': len(members),
|
||||
'paid_count': len(queryset.filter(last_paid__gte=filter_date)),
|
||||
'notification': request.GET.get('notification', None),
|
||||
"table": table_html,
|
||||
"member_count": len(members),
|
||||
"paid_count": len(queryset.filter(last_paid__gte=filter_date)),
|
||||
"notification": request.GET.get("notification", None),
|
||||
}
|
||||
return render(request, 'member_list.html', context)
|
||||
return render(request, "member_list.html", context)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.add_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.add_member", raise_exception=True)
|
||||
def member_add(request, *args, **kwargs):
|
||||
"""Render add member page."""
|
||||
form = MemberForm()
|
||||
return render(request, 'member_add.html', {'form': form})
|
||||
return render(request, "member_add.html", {"form": form})
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.delete_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.delete_member", raise_exception=True)
|
||||
def member_delete_confirm(request, *args, **kwargs):
|
||||
"""Render member deletion confirmation page."""
|
||||
i = kwargs.pop('index', None)
|
||||
i = kwargs.pop("index", None)
|
||||
if i is None:
|
||||
return error_view(request, _('No member id specified'))
|
||||
return error_view(request, _("No member id specified"))
|
||||
else:
|
||||
member = Member.objects.get(id=i)
|
||||
form = MemberForm(instance=member)
|
||||
return render(request, 'member_delete_confirm.html',
|
||||
{'member_id': i, 'form': form})
|
||||
return render(
|
||||
request, "member_delete_confirm.html", {"member_id": i, "form": form}
|
||||
)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.add_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.add_member", raise_exception=True)
|
||||
def member_add_many(request, *args, **kwargs):
|
||||
"""Render add multiple members page."""
|
||||
return render(request, 'member_add_many.html', {})
|
||||
return render(request, "member_add_many.html", {})
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.add_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.add_member", raise_exception=True)
|
||||
def add_many_confirm(request, *args, **kwargs):
|
||||
models = request.session['models']
|
||||
payment_source = request.session['payment_source']
|
||||
models = request.session["models"]
|
||||
payment_source = request.session["payment_source"]
|
||||
|
||||
try:
|
||||
members, payments = models.members, models.payments
|
||||
@@ -120,40 +130,47 @@ def add_many_confirm(request, *args, **kwargs):
|
||||
|
||||
msg = "Successfully imported {} members and {} payments."
|
||||
notification = _(msg).format(len(members), len(payments))
|
||||
return HttpResponseRedirect('/members/list?notification={}'.format(html.escape(notification)))
|
||||
return HttpResponseRedirect(
|
||||
"/members/list?notification={}".format(html.escape(notification))
|
||||
)
|
||||
except Exception as ex:
|
||||
logging.exception('Failed to save models after "add many."')
|
||||
return error_view(request, _('Failed to import members'))
|
||||
return error_view(request, _("Failed to import members"))
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.add_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.add_member", raise_exception=True)
|
||||
def member_submit(request, *args, **kwargs):
|
||||
"""Add member based on data gained from member form."""
|
||||
form = MemberForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
logging.info("Saved new member to member register"
|
||||
"with the following info: {}".format(form.cleaned_data))
|
||||
notification = "{} {} {}.".format(_("Successfully added member"),
|
||||
form.cleaned_data['last_name'],
|
||||
form.cleaned_data['first_name'])
|
||||
logging.info(
|
||||
"Saved new member to member register"
|
||||
"with the following info: {}".format(form.cleaned_data)
|
||||
)
|
||||
notification = "{} {} {}.".format(
|
||||
_("Successfully added member"),
|
||||
form.cleaned_data["last_name"],
|
||||
form.cleaned_data["first_name"],
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(
|
||||
'/members/list?notification={}'.format(html.escape(notification)))
|
||||
"/members/list?notification={}".format(html.escape(notification))
|
||||
)
|
||||
else:
|
||||
return error_view(request, form.errors)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.change_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.change_member", raise_exception=True)
|
||||
def member_update(request, *args, **kwargs):
|
||||
"""Update member information."""
|
||||
id = request.POST.get('id', None)
|
||||
id = request.POST.get("id", None)
|
||||
logging.debug(id)
|
||||
if id is not None:
|
||||
member = Member.objects.get(id=id)
|
||||
@@ -165,61 +182,63 @@ def member_update(request, *args, **kwargs):
|
||||
form.save()
|
||||
|
||||
logging.info(
|
||||
"Updated member in member register with the following info: {}"
|
||||
.format(form))
|
||||
notification = "{} {} {}.".format(_("Successfully updated member"),
|
||||
member.last_name, member.first_name)
|
||||
"Updated member in member register with the following info: {}".format(form)
|
||||
)
|
||||
notification = "{} {} {}.".format(
|
||||
_("Successfully updated member"), member.last_name, member.first_name
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
'/members/list?notification={}'.format(html.escape(notification)))
|
||||
"/members/list?notification={}".format(html.escape(notification))
|
||||
)
|
||||
else:
|
||||
return error_view(request, form.errors)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.delete_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.delete_member", raise_exception=True)
|
||||
def member_delete(request, *args, **kwargs):
|
||||
"""Delete member."""
|
||||
try:
|
||||
id = request.POST['id']
|
||||
id = request.POST["id"]
|
||||
except KeyError:
|
||||
return error_view(request, _('No member id specified'))
|
||||
return error_view(request, _("No member id specified"))
|
||||
|
||||
try:
|
||||
member = Member.objects.get(id=id)
|
||||
notification = "{} {} {}.".format(_("Successfully deleted member"),
|
||||
member.last_name, member.first_name)
|
||||
notification = "{} {} {}.".format(
|
||||
_("Successfully deleted member"), member.last_name, member.first_name
|
||||
)
|
||||
member.delete()
|
||||
logging.info(
|
||||
"Delete member in member register with the following id: {}"
|
||||
.format(id))
|
||||
"Delete member in member register with the following id: {}".format(id)
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
'/members/list?notification={}'.format(html.escape(notification)))
|
||||
"/members/list?notification={}".format(html.escape(notification))
|
||||
)
|
||||
except:
|
||||
return error_view(request, _('Could not delete member object'))
|
||||
return error_view(request, _("Could not delete member object"))
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.change_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.change_member", raise_exception=True)
|
||||
def member_edit(request, *args, **kwargs):
|
||||
"""Edit member information."""
|
||||
i = kwargs.pop('index', None)
|
||||
i = kwargs.pop("index", None)
|
||||
if i is None:
|
||||
return error_view(request, _('No member id specified'))
|
||||
return error_view(request, _("No member id specified"))
|
||||
else:
|
||||
member = Member.objects.get(id=i)
|
||||
form = MemberForm(instance=member)
|
||||
return render(
|
||||
request, 'member_edit.html', {'member_id': i, 'form': form})
|
||||
return render(request, "member_edit.html", {"member_id": i, "form": form})
|
||||
|
||||
|
||||
@method_decorator(login_required(login_url='/admin/login'), name='dispatch')
|
||||
@method_decorator(permission_required('members.change_member'), name='dispatch')
|
||||
@method_decorator(login_required(login_url="/admin/login"), name="dispatch")
|
||||
@method_decorator(permission_required("members.change_member"), name="dispatch")
|
||||
class MemberAutoComplete(autocomplete.Select2QuerySetView):
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Member.objects.all()
|
||||
|
||||
@@ -236,7 +255,7 @@ class CheckByEmail(APIView):
|
||||
permission_classes = (CheckByEmailPermission,)
|
||||
|
||||
def get(self, request, format=None):
|
||||
email = request.query_params.get('email')
|
||||
email = request.query_params.get("email")
|
||||
exists = bool(email and Member.objects.filter(email=email).exists())
|
||||
resp = {'exists': exists}
|
||||
resp = {"exists": exists}
|
||||
return JsonResponse(resp)
|
||||
|
||||
+57
-58
@@ -18,143 +18,142 @@ from members.views import error_view
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.read_payment', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.read_payment", raise_exception=True)
|
||||
def payment_list(request, *args, **kwargs):
|
||||
"""Render list of payments."""
|
||||
search = request.GET.get('q', None)
|
||||
search = request.GET.get("q", None)
|
||||
if search:
|
||||
payments = Payment.find_payments_by_name(search)
|
||||
else:
|
||||
payments = Payment.objects.all()
|
||||
|
||||
table = PaymentTable(payments,
|
||||
request=request,
|
||||
exclude=['id'],
|
||||
attrs={'class': 'table table-bordered table-hover'})
|
||||
table = PaymentTable(
|
||||
payments,
|
||||
request=request,
|
||||
exclude=["id"],
|
||||
attrs={"class": "table table-bordered table-hover"},
|
||||
)
|
||||
|
||||
table.paginate(page=request.GET.get('page', 1), per_page=25)
|
||||
table.paginate(page=request.GET.get("page", 1), per_page=25)
|
||||
table_html = convert_table_to_html(table, request)
|
||||
|
||||
context = {
|
||||
'table': table_html,
|
||||
'payment_count': len(payments),
|
||||
'notification': request.GET.get('notification', None)
|
||||
"table": table_html,
|
||||
"payment_count": len(payments),
|
||||
"notification": request.GET.get("notification", None),
|
||||
}
|
||||
return render(request, 'payment_list.html', context)
|
||||
return render(request, "payment_list.html", context)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.add_payment', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.add_payment", raise_exception=True)
|
||||
def payment_add(request, *args, **kwargs):
|
||||
"""Render add payment form."""
|
||||
form = PaymentForm()
|
||||
return render(request, 'payment_add.html', {'form': form})
|
||||
return render(request, "payment_add.html", {"form": form})
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.add_payment', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.add_payment", raise_exception=True)
|
||||
def payment_submit(request, *args, **kwargs):
|
||||
"""Submit payment."""
|
||||
form = PaymentForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
logging.info(
|
||||
"Saved new payment to member register with the following info: {}"
|
||||
.format(form.cleaned_data))
|
||||
"Saved new payment to member register with the following info: {}".format(
|
||||
form.cleaned_data
|
||||
)
|
||||
)
|
||||
notification = "{} {}.".format(
|
||||
_("Successfully added payment for member"),
|
||||
form.cleaned_data['member'])
|
||||
_("Successfully added payment for member"), form.cleaned_data["member"]
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
'/members/payments?notification={}'
|
||||
.format(html.escape(notification)))
|
||||
"/members/payments?notification={}".format(html.escape(notification))
|
||||
)
|
||||
else:
|
||||
return error_view(request, form.errors)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.change_payment', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.change_payment", raise_exception=True)
|
||||
def payment_edit(request, *args, **kwargs):
|
||||
"""Edit payment."""
|
||||
i = kwargs.pop('index', None)
|
||||
i = kwargs.pop("index", None)
|
||||
if i is None:
|
||||
return error_view(request, _('No payment id specified'))
|
||||
return error_view(request, _("No payment id specified"))
|
||||
else:
|
||||
payment = Payment.objects.get(id=i)
|
||||
form = PaymentForm(instance=payment)
|
||||
return render(request,
|
||||
'payment_edit.html',
|
||||
{'payment_id': i, 'form': form})
|
||||
return render(request, "payment_edit.html", {"payment_id": i, "form": form})
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.delete_payment', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.delete_payment", raise_exception=True)
|
||||
def payment_delete_confirm(request, *args, **kwargs):
|
||||
"""Render payment delete confirmation page."""
|
||||
i = kwargs.pop('index', None)
|
||||
i = kwargs.pop("index", None)
|
||||
if i is None:
|
||||
return error_view(request, _('No payment id specified'))
|
||||
return error_view(request, _("No payment id specified"))
|
||||
else:
|
||||
payment = Payment.objects.get(id=i)
|
||||
form = PaymentForm(instance=payment)
|
||||
return render(request,
|
||||
'payment_delete_confirm.html',
|
||||
{'payment_id': i, 'form': form})
|
||||
return render(
|
||||
request, "payment_delete_confirm.html", {"payment_id": i, "form": form}
|
||||
)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.delete_payment', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.delete_payment", raise_exception=True)
|
||||
def payment_delete(request, *args, **kwargs):
|
||||
"""Delete payment."""
|
||||
try:
|
||||
id = request.POST['id']
|
||||
id = request.POST["id"]
|
||||
except KeyError:
|
||||
return error_view(request, _('No payment id specified'))
|
||||
return error_view(request, _("No payment id specified"))
|
||||
|
||||
try:
|
||||
payment = Payment.objects.get(id=id)
|
||||
notification = "{} {}.".format(
|
||||
_("Successfully deleted payment"), str(payment))
|
||||
notification = "{} {}.".format(_("Successfully deleted payment"), str(payment))
|
||||
payment.delete()
|
||||
logging.info(
|
||||
"Delete payment '{}' in member register".format(str(payment)))
|
||||
logging.info("Delete payment '{}' in member register".format(str(payment)))
|
||||
return HttpResponseRedirect(
|
||||
'/members/payments?notification={}'
|
||||
.format(html.escape(notification)))
|
||||
"/members/payments?notification={}".format(html.escape(notification))
|
||||
)
|
||||
except:
|
||||
return error_view(request, _('Could not delete payment object'))
|
||||
return error_view(request, _("Could not delete payment object"))
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.change_payment', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.change_payment", raise_exception=True)
|
||||
def payment_update(request, *args, **kwargs):
|
||||
"""Update payment information."""
|
||||
form = PaymentForm(request.POST)
|
||||
if form.is_valid():
|
||||
id = request.POST['id']
|
||||
id = request.POST["id"]
|
||||
payment = Payment.objects.get(id=id)
|
||||
form = PaymentForm(request.POST, instance=payment)
|
||||
form.save()
|
||||
|
||||
logging.info(
|
||||
"Updated member in member register with the following info: {}"
|
||||
.format(form))
|
||||
notification = "{} {}.".format(
|
||||
_("Successfully updated payment"), str(payment))
|
||||
"Updated member in member register with the following info: {}".format(form)
|
||||
)
|
||||
notification = "{} {}.".format(_("Successfully updated payment"), str(payment))
|
||||
return HttpResponseRedirect(
|
||||
'/members/payments?notification={}'
|
||||
.format(html.escape(notification)))
|
||||
"/members/payments?notification={}".format(html.escape(notification))
|
||||
)
|
||||
else:
|
||||
return error_view(request, _('Could not update payment object'))
|
||||
return error_view(request, _("Could not update payment object"))
|
||||
|
||||
+59
-43
@@ -21,7 +21,13 @@ from rest_framework import generics
|
||||
from rest_framework import permissions
|
||||
|
||||
from members.models import Member, Request, Payment
|
||||
from members.forms import MemberForm, PaymentForm, ApplicationForm, CSVValidationError, UploadFileForm
|
||||
from members.forms import (
|
||||
MemberForm,
|
||||
PaymentForm,
|
||||
ApplicationForm,
|
||||
CSVValidationError,
|
||||
UploadFileForm,
|
||||
)
|
||||
from members.tables import MemberTable, PaymentTable, RequestTable
|
||||
from members.resources import MemberResource, PaymentResource, ApplicationResource
|
||||
|
||||
@@ -32,12 +38,15 @@ class MemberDetail(generics.RetrieveAPIView):
|
||||
|
||||
queryset = Member.objects.all()
|
||||
serializer_class = MemberSerializer
|
||||
permission_classes = (permissions.DjangoModelPermissions, )
|
||||
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
||||
permission_classes = (permissions.DjangoModelPermissions,)
|
||||
throttle_classes = (
|
||||
BurstRateThrottle,
|
||||
SustainedRateThrottle,
|
||||
)
|
||||
|
||||
|
||||
def error_view(request, message, status=400):
|
||||
return render(request, 'error.html', {'error': message}, status=400)
|
||||
return render(request, "error.html", {"error": message}, status=400)
|
||||
|
||||
|
||||
def validate_recaptcha(response):
|
||||
@@ -48,15 +57,15 @@ def validate_recaptcha(response):
|
||||
:return: Boolean, success or not
|
||||
"""
|
||||
values = {
|
||||
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
|
||||
'response': response,
|
||||
"secret": settings.GOOGLE_RECAPTCHA_SECRET_KEY,
|
||||
"response": response,
|
||||
}
|
||||
url = "https://www.google.com/recaptcha/api/siteverify"
|
||||
headers = {'Content-type': 'application/x-www-form-urlencoded'}
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded"}
|
||||
resp = requests.post(url, values, headers=headers)
|
||||
try:
|
||||
result = json.loads(resp.text)
|
||||
logging.info('Recaptcha response: {}'.format(result))
|
||||
logging.info("Recaptcha response: {}".format(result))
|
||||
return result["success"]
|
||||
except:
|
||||
return False
|
||||
@@ -87,81 +96,88 @@ def convert_table_to_html(table, request):
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required('members.change_member', raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required("members.change_member", raise_exception=True)
|
||||
def settings_page(request, *args, **kwargs):
|
||||
"""Render member app settings page."""
|
||||
return render(request, 'settings.html', {})
|
||||
return render(request, "settings.html", {})
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["POST"])
|
||||
@login_required(login_url='/admin/login')
|
||||
@permission_required(['members.change_member', 'members.change_payment'], raise_exception=True)
|
||||
@login_required(login_url="/admin/login")
|
||||
@permission_required(
|
||||
["members.change_member", "members.change_payment"], raise_exception=True
|
||||
)
|
||||
def import_csv(request, *args, **kwargs):
|
||||
"""Get csv data imported to page and create members based on that."""
|
||||
try:
|
||||
csv_in_memory_file = request.FILES.get('csvFile')
|
||||
csv_in_memory_file = request.FILES.get("csvFile")
|
||||
csv_file = csv_in_memory_file.file
|
||||
data = csv_file.read().decode('utf-8')
|
||||
data = csv_file.read().decode("utf-8")
|
||||
|
||||
delimiter = request.POST.get('delimiter', ',')
|
||||
payment_source = request.POST['payment_source']
|
||||
delimiter = request.POST.get("delimiter", ",")
|
||||
payment_source = request.POST["payment_source"]
|
||||
except:
|
||||
return error_view(request, _('Missing CSV file'))
|
||||
return error_view(request, _("Missing CSV file"))
|
||||
|
||||
try:
|
||||
result = MemberForm.csv_to_models(data, payment_source=payment_source, delimiter=delimiter)
|
||||
result = MemberForm.csv_to_models(
|
||||
data, payment_source=payment_source, delimiter=delimiter
|
||||
)
|
||||
except CSVValidationError as ex:
|
||||
logging.exception('Model validation error')
|
||||
logging.exception("Model validation error")
|
||||
return error_view(request, ex.form_errors)
|
||||
except Exception as ex:
|
||||
logging.exception('Other error in CSV import')
|
||||
logging.exception("Other error in CSV import")
|
||||
return error_view(request, str(ex))
|
||||
|
||||
member_table = MemberTable(result.members,
|
||||
request=request,
|
||||
exclude=['id', 'options'],
|
||||
attrs={'class': 'table table-bordered table-hover'})
|
||||
member_table = MemberTable(
|
||||
result.members,
|
||||
request=request,
|
||||
exclude=["id", "options"],
|
||||
attrs={"class": "table table-bordered table-hover"},
|
||||
)
|
||||
|
||||
member_table.paginate(page=request.GET.get('page', 1), per_page=999999)
|
||||
member_table.paginate(page=request.GET.get("page", 1), per_page=999999)
|
||||
member_table_html = convert_table_to_html(member_table, request)
|
||||
|
||||
payment_table = PaymentTable(result.payments,
|
||||
request=request,
|
||||
exclude=['id', 'options'],
|
||||
attrs={'class': 'table table-bordered table-hover'})
|
||||
payment_table = PaymentTable(
|
||||
result.payments,
|
||||
request=request,
|
||||
exclude=["id", "options"],
|
||||
attrs={"class": "table table-bordered table-hover"},
|
||||
)
|
||||
|
||||
payment_table.paginate(page=request.GET.get('page', 1), per_page=999999)
|
||||
payment_table.paginate(page=request.GET.get("page", 1), per_page=999999)
|
||||
payment_table_html = convert_table_to_html(payment_table, request)
|
||||
|
||||
request.session['models'] = result
|
||||
request.session['payment_source'] = payment_source
|
||||
context = {
|
||||
'members': member_table_html,
|
||||
'payments': payment_table_html
|
||||
}
|
||||
return render(request, 'member_add_many_confirm.html', context)
|
||||
request.session["models"] = result
|
||||
request.session["payment_source"] = payment_source
|
||||
context = {"members": member_table_html, "payments": payment_table_html}
|
||||
return render(request, "member_add_many_confirm.html", context)
|
||||
|
||||
|
||||
def make_excel_response(Resource):
|
||||
res = Resource()
|
||||
dataset = res.export()
|
||||
response = HttpResponse(dataset.xlsx, content_type='application/vnd.ms-excel; charset=utf-8')
|
||||
response['Content-Disposition'] = 'attachment; filename="export.xlsx"'
|
||||
response = HttpResponse(
|
||||
dataset.xlsx, content_type="application/vnd.ms-excel; charset=utf-8"
|
||||
)
|
||||
response["Content-Disposition"] = 'attachment; filename="export.xlsx"'
|
||||
return response
|
||||
|
||||
|
||||
@require_http_methods(['GET'])
|
||||
@require_http_methods(["GET"])
|
||||
def export_members_excel(request, *args, **kwargs):
|
||||
return make_excel_response(MemberResource)
|
||||
|
||||
|
||||
@require_http_methods(['GET'])
|
||||
@require_http_methods(["GET"])
|
||||
def export_payments_excel(request, *args, **kwargs):
|
||||
return make_excel_response(PaymentResource)
|
||||
|
||||
|
||||
@require_http_methods(['GET'])
|
||||
@require_http_methods(["GET"])
|
||||
def export_applications_excel(request, *args, **kwargs):
|
||||
return make_excel_response(ApplicationResource)
|
||||
|
||||
+10
-6
@@ -3,14 +3,18 @@ import os
|
||||
|
||||
|
||||
def generate_names(n):
|
||||
'''
|
||||
"""
|
||||
generates list of n random names
|
||||
returns tuple(first, last)
|
||||
'''
|
||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "firstnames.txt")) as f:
|
||||
fs = f.read().split('\n')
|
||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "lastnames.txt")) as f:
|
||||
ls = f.read().split('\n')
|
||||
"""
|
||||
with open(
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "firstnames.txt")
|
||||
) as f:
|
||||
fs = f.read().split("\n")
|
||||
with open(
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "lastnames.txt")
|
||||
) as f:
|
||||
ls = f.read().split("\n")
|
||||
|
||||
names = []
|
||||
for i in range(n):
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||
|
||||
|
||||
class OhlhafvConfig(AppConfig):
|
||||
name = 'ohlhafv'
|
||||
name = "ohlhafv"
|
||||
|
||||
+1
-1
@@ -14,4 +14,4 @@ class OhlhafvForm(forms.ModelForm):
|
||||
"""Meta class for Ohlhafv form."""
|
||||
|
||||
model = OhlhafvChallenge
|
||||
fields = ['challenger', 'victim', 'victim_email', 'series', 'message']
|
||||
fields = ["challenger", "victim", "victim_email", "series", "message"]
|
||||
|
||||
@@ -9,24 +9,55 @@ class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OhlhafvChallenge',
|
||||
name="OhlhafvChallenge",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('challenger', models.CharField(max_length=255, verbose_name='Challenger')),
|
||||
('victim', models.CharField(max_length=255, verbose_name='Victim')),
|
||||
('challenger_email', models.EmailField(max_length=254, verbose_name='Challenger email')),
|
||||
('victim_email', models.EmailField(max_length=254, verbose_name='Victim email')),
|
||||
('series', models.CharField(choices=[('0.33 L', '0.33 L'), ('0.5 L', '0.5 L'), ('1.0 L', '1.0 L'), ('Team', 'Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)')], max_length=10, verbose_name='Series')),
|
||||
('message', models.TextField(blank=True, verbose_name='Message')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"challenger",
|
||||
models.CharField(max_length=255, verbose_name="Challenger"),
|
||||
),
|
||||
("victim", models.CharField(max_length=255, verbose_name="Victim")),
|
||||
(
|
||||
"challenger_email",
|
||||
models.EmailField(max_length=254, verbose_name="Challenger email"),
|
||||
),
|
||||
(
|
||||
"victim_email",
|
||||
models.EmailField(max_length=254, verbose_name="Victim email"),
|
||||
),
|
||||
(
|
||||
"series",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("0.33 L", "0.33 L"),
|
||||
("0.5 L", "0.5 L"),
|
||||
("1.0 L", "1.0 L"),
|
||||
(
|
||||
"Team",
|
||||
"Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)",
|
||||
),
|
||||
],
|
||||
max_length=10,
|
||||
verbose_name="Series",
|
||||
),
|
||||
),
|
||||
("message", models.TextField(blank=True, verbose_name="Message")),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Ohlhafv challenge',
|
||||
'verbose_name_plural': 'Ohlhafv challenges',
|
||||
"verbose_name": "Ohlhafv challenge",
|
||||
"verbose_name_plural": "Ohlhafv challenges",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,12 +8,12 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ohlhafv', '0001_initial'),
|
||||
("ohlhafv", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='ohlhafvchallenge',
|
||||
name='challenger_email',
|
||||
model_name="ohlhafvchallenge",
|
||||
name="challenger_email",
|
||||
),
|
||||
]
|
||||
|
||||
+13
-13
@@ -12,32 +12,32 @@ from phonenumber_field.modelfields import PhoneNumberField
|
||||
|
||||
import logging
|
||||
|
||||
VERBOSE_NAME = _('Ohlhafv')
|
||||
VERBOSE_NAME = _("Ohlhafv")
|
||||
|
||||
|
||||
class OhlhafvChallenge(models.Model):
|
||||
"""Model containing all info about ohlhafv challenge."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Ohlhafv challenge')
|
||||
verbose_name_plural = _('Ohlhafv challenges')
|
||||
verbose_name = _("Ohlhafv challenge")
|
||||
verbose_name_plural = _("Ohlhafv challenges")
|
||||
|
||||
SERIES_CHOICES = (
|
||||
('0.33 L', '0.33 L'),
|
||||
('0.5 L', '0.5 L'),
|
||||
('1.0 L', '1.0 L'),
|
||||
('Team', _('Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)'))
|
||||
("0.33 L", "0.33 L"),
|
||||
("0.5 L", "0.5 L"),
|
||||
("1.0 L", "1.0 L"),
|
||||
("Team", _("Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)")),
|
||||
)
|
||||
|
||||
challenger = models.CharField(_('Challenger'), max_length=255)
|
||||
victim = models.CharField(_('Victim'), max_length=255)
|
||||
victim_email = models.EmailField(_('Victim email'))
|
||||
series = models.CharField(_('Series'), choices=SERIES_CHOICES, max_length=10)
|
||||
message = models.TextField(_('Message'), blank=True, null=False)
|
||||
challenger = models.CharField(_("Challenger"), max_length=255)
|
||||
victim = models.CharField(_("Victim"), max_length=255)
|
||||
victim_email = models.EmailField(_("Victim email"))
|
||||
series = models.CharField(_("Series"), choices=SERIES_CHOICES, max_length=10)
|
||||
message = models.TextField(_("Message"), blank=True, null=False)
|
||||
|
||||
def __str__(self):
|
||||
"""Return model info."""
|
||||
return _('Ohlhafv challenge: {} vs. {}').format(self.challenger, self.victim)
|
||||
return _("Ohlhafv challenge: {} vs. {}").format(self.challenger, self.victim)
|
||||
|
||||
|
||||
auditlog.register(OhlhafvChallenge)
|
||||
|
||||
+4
-3
@@ -8,11 +8,12 @@ from ohlhafv.views import *
|
||||
|
||||
urlpatterns = [
|
||||
# ohlhafv
|
||||
url(r'^submit', ohlhafv_submit),
|
||||
url(r'^list', ohlhafv_list),
|
||||
url(r'^$', ohlhafv_view)
|
||||
url(r"^submit", ohlhafv_submit),
|
||||
url(r"^list", ohlhafv_list),
|
||||
url(r"^$", ohlhafv_view),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
+5
-8
@@ -1,5 +1,3 @@
|
||||
"""Ohlhafv views."""
|
||||
|
||||
import logging
|
||||
from django.shortcuts import render
|
||||
from django.views.decorators.http import require_http_methods
|
||||
@@ -8,12 +6,12 @@ from django.http import HttpResponseRedirect
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from sikweb.settings import URL
|
||||
|
||||
from ohlhafv.models import OhlhafvChallenge
|
||||
from ohlhafv.forms import OhlhafvForm
|
||||
from kaehmy.tgbot import TelegramBot
|
||||
from webapp.utils import send_email
|
||||
from sikweb.settings import URL
|
||||
from webapp.models import processHooks
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
@@ -47,12 +45,11 @@ def ohlhafv_submit(request, *args, **kwargs):
|
||||
logging.debug(f"Sent ohlhafv email to recipient <{to_email}>")
|
||||
|
||||
try:
|
||||
tg_message = render_to_string(
|
||||
webhook_message = render_to_string(
|
||||
"ohlhafv:tgmsg.tpl", {"challenge": challenge, "url": url}
|
||||
)
|
||||
bot = TelegramBot()
|
||||
bot.broadcast(tg_message)
|
||||
except Exception: # tg spam is not critical. Ignore on failure
|
||||
processHooks(message=webhook_message, eventType="ohlhafv")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
else:
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@
|
||||
"lint": "run-p lint:js lint:md lint:py",
|
||||
"lint:js": "eslint .",
|
||||
"lint:md": "remark .",
|
||||
"lint:py": "pycodestyle --config=pycodestyle.cfg --count .",
|
||||
"lint:py": "black --diff .",
|
||||
"lint:py:fix": "black .",
|
||||
"lint:py-type": "pyright",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
|
||||
Generated
+116
-19
@@ -23,6 +23,32 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
[package.dependencies]
|
||||
pytz = ">=2015.7"
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "21.12b0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=7.1.2"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.9.0,<1"
|
||||
platformdirs = ">=2"
|
||||
tomli = ">=0.2.6,<2.0.0"
|
||||
typing-extensions = [
|
||||
{version = ">=3.10.0.0", markers = "python_version < \"3.10\""},
|
||||
{version = "!=3.10.0.1", markers = "python_version >= \"3.10\""},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
python2 = ["typed-ast (>=1.4.3)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2021.10.8"
|
||||
@@ -243,6 +269,17 @@ phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phon
|
||||
phonenumbers = ["phonenumbers (>=7.0.2)"]
|
||||
phonenumberslite = ["phonenumberslite (>=7.0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "django-polymorphic"
|
||||
version = "3.1.0"
|
||||
description = "Seamless polymorphic inheritance for Django models"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=2.1"
|
||||
|
||||
[[package]]
|
||||
name = "django-suit"
|
||||
version = "0.2.28"
|
||||
@@ -397,6 +434,14 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
|
||||
[package.extras]
|
||||
testing = ["coverage", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "nose"
|
||||
version = "1.3.7"
|
||||
@@ -439,6 +484,14 @@ python-versions = ">=3.6"
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.9.0"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||
|
||||
[[package]]
|
||||
name = "phonenumbers"
|
||||
version = "8.12.41"
|
||||
@@ -455,6 +508,18 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.4.1"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
|
||||
test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg2-binary"
|
||||
version = "2.8.6"
|
||||
@@ -463,14 +528,6 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.8.0"
|
||||
description = "Python style guide checker"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pyexcel"
|
||||
version = "0.5.15"
|
||||
@@ -562,7 +619,7 @@ six = ">=1.5"
|
||||
|
||||
[[package]]
|
||||
name = "python-http-client"
|
||||
version = "3.3.4"
|
||||
version = "3.3.5"
|
||||
description = "HTTP REST client, simplified for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -618,7 +675,7 @@ requests = "*"
|
||||
|
||||
[[package]]
|
||||
name = "sendgrid"
|
||||
version = "6.9.3"
|
||||
version = "6.9.4"
|
||||
description = "Twilio SendGrid library for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -716,6 +773,22 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "1.2.3"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.0.1"
|
||||
description = "Backported and Experimental Type Hints for Python 3.6+"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.8"
|
||||
@@ -763,7 +836,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "755a353d6f70eab1933125429b38d1e7249f57a9a61769507a3ef624f2d3cddb"
|
||||
content-hash = "0e5a9eb8c05af55b1d5b572f4ccaa323e14f8eeb0a554a4975c9bb1afeba66b4"
|
||||
|
||||
[metadata.files]
|
||||
attrs = [
|
||||
@@ -774,6 +847,10 @@ babel = [
|
||||
{file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"},
|
||||
{file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"},
|
||||
]
|
||||
black = [
|
||||
{file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"},
|
||||
{file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
|
||||
{file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
|
||||
@@ -900,6 +977,10 @@ django-phonenumber-field = [
|
||||
{file = "django-phonenumber-field-4.0.0.tar.gz", hash = "sha256:d4580cc3352f4433962825f9927e6669852c1b40ec484fcb5a74064dabc1201a"},
|
||||
{file = "django_phonenumber_field-4.0.0-py3-none-any.whl", hash = "sha256:2ca3bb0ada0ebc164bd903a981a34f1202a4294006e520b0da961bd7ce9f20a4"},
|
||||
]
|
||||
django-polymorphic = [
|
||||
{file = "django-polymorphic-3.1.0.tar.gz", hash = "sha256:d6955b5308bf6e41dcb22ba7c96f00b51dfa497a8a5ab1e9c06c7951bf417bf8"},
|
||||
{file = "django_polymorphic-3.1.0-py3-none-any.whl", hash = "sha256:08bc4f4f4a773a19b2deced5a56deddd1ef56ebd15207bf4052e2901c25ef57e"},
|
||||
]
|
||||
django-suit = [
|
||||
{file = "django-suit-0.2.28.tar.gz", hash = "sha256:bacd8a079fcc08deb6efd0d7f60241e3c319526939ae1abe8ccfbc1b03e97104"},
|
||||
{file = "django_suit-0.2.28-py2.py3-none-any.whl", hash = "sha256:256412597ac8e9461780542eebb12b37f65ff702bf23de23d07d245510c64ff2"},
|
||||
@@ -950,6 +1031,10 @@ markdown = [
|
||||
{file = "Markdown-3.3.6-py3-none-any.whl", hash = "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3"},
|
||||
{file = "Markdown-3.3.6.tar.gz", hash = "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
nose = [
|
||||
{file = "nose-1.3.7-py2-none-any.whl", hash = "sha256:dadcddc0aefbf99eea214e0f1232b94f2fa9bd98fa8353711dacb112bfcbbb2a"},
|
||||
{file = "nose-1.3.7-py3-none-any.whl", hash = "sha256:9ff7c6cc443f8c51994b34a667bbcf45afd6d945be7477b52e97516fd17c53ac"},
|
||||
@@ -965,6 +1050,10 @@ packaging = [
|
||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||
]
|
||||
pathspec = [
|
||||
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
|
||||
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
|
||||
]
|
||||
phonenumbers = [
|
||||
{file = "phonenumbers-8.12.41-py2.py3-none-any.whl", hash = "sha256:2b8c7a7ffac4fe2be3d8bf20dad316ea1292f27422c9e18b1f3cd16734d4a5ed"},
|
||||
{file = "phonenumbers-8.12.41.tar.gz", hash = "sha256:f477da623a51cba084567d6a67b1882a8aaaf3e7beadad655f8613a8f887ac62"},
|
||||
@@ -1012,6 +1101,10 @@ pillow = [
|
||||
{file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"},
|
||||
{file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"},
|
||||
]
|
||||
platformdirs = [
|
||||
{file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"},
|
||||
{file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"},
|
||||
]
|
||||
psycopg2-binary = [
|
||||
{file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"},
|
||||
{file = "psycopg2_binary-2.8.6-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4"},
|
||||
@@ -1049,10 +1142,6 @@ psycopg2-binary = [
|
||||
{file = "psycopg2_binary-2.8.6-cp39-cp39-win32.whl", hash = "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056"},
|
||||
{file = "psycopg2_binary-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6"},
|
||||
]
|
||||
pycodestyle = [
|
||||
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
|
||||
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
|
||||
]
|
||||
pyexcel = [
|
||||
{file = "pyexcel-0.5.15-py2.py3-none-any.whl", hash = "sha256:7fac067e65567c380933b4d382587a5ce6581d0ad85992f6f0bc7c3f16012184"},
|
||||
{file = "pyexcel-0.5.15.tar.gz", hash = "sha256:f0a7797f3a0de9e6f81151c9581fa90c4e1afce207dc47d2f0ba728dd2e24467"},
|
||||
@@ -1102,8 +1191,8 @@ python-dateutil = [
|
||||
{file = "python_dateutil-2.6.0-py2.py3-none-any.whl", hash = "sha256:537bf2a8f8ce6f6862ad705cd68f9e405c0b5db014aa40fa29eab4335d4b1716"},
|
||||
]
|
||||
python-http-client = [
|
||||
{file = "python_http_client-3.3.4-py3-none-any.whl", hash = "sha256:742169c42033ee81e22958dcf9e2c6f2b196028d72f87563eecdb28324d82528"},
|
||||
{file = "python_http_client-3.3.4.tar.gz", hash = "sha256:723d0d6f03d4865cc8d08172aef15837a1eb9d8d8a719a08107e4680d0876e41"},
|
||||
{file = "python_http_client-3.3.5-py3-none-any.whl", hash = "sha256:558ece0088af1c3430d55ea65e3f06a6a3d7cdd9e14bd905916081ce876c5aaf"},
|
||||
{file = "python_http_client-3.3.5.tar.gz", hash = "sha256:a41da9bd1d38c6a5fc673d1667501e9e691783f7caa14db70985da43c6d99fba"},
|
||||
]
|
||||
pytz = [
|
||||
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
|
||||
@@ -1153,8 +1242,8 @@ safety = [
|
||||
{file = "safety-1.10.3.tar.gz", hash = "sha256:30e394d02a20ac49b7f65292d19d38fa927a8f9582cdfd3ad1adbbc66c641ad5"},
|
||||
]
|
||||
sendgrid = [
|
||||
{file = "sendgrid-6.9.3-py3-none-any.whl", hash = "sha256:dfe281d60dedcd6386f715662c5c0e95cab539e15fddd17b4909d08b8065fc88"},
|
||||
{file = "sendgrid-6.9.3.tar.gz", hash = "sha256:561bd742728631d194398d856ec8c28ba2a024e7c9e5750561f529202aa1d24a"},
|
||||
{file = "sendgrid-6.9.4-py3-none-any.whl", hash = "sha256:f0faa12189e85962651ec1062f6a53e7b0272c452e721a8f4d6847cddad9cfa8"},
|
||||
{file = "sendgrid-6.9.4.tar.gz", hash = "sha256:40653ec7a1ca889398ed6aadba480ae6ab73b9f4329ad841ef4d2bd81848cf62"},
|
||||
]
|
||||
sentry-sdk = [
|
||||
{file = "sentry-sdk-1.5.2.tar.gz", hash = "sha256:7bbaa32bba806ec629962f207b597e86831c7ee2c1f287c21ba7de7fea9a9c46"},
|
||||
@@ -1183,6 +1272,14 @@ toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
tomli = [
|
||||
{file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"},
|
||||
{file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
|
||||
{file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
|
||||
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
if test -f "$SECRET_KEY_FILE"; then
|
||||
export SECRET_KEY=$(cat $SECRET_KEY_FILE)
|
||||
fi
|
||||
if test -f "$TG_BOT_TOKEN_FILE"; then
|
||||
export TG_BOT_TOKEN=$(cat $TG_BOT_TOKEN_FILE)
|
||||
fi
|
||||
if test -f "$EMAIL_API_KEY_FILE"; then
|
||||
export EMAIL_API_KEY=$(cat $EMAIL_API_KEY_FILE)
|
||||
fi
|
||||
|
||||
+2
-1
@@ -37,12 +37,13 @@ gunicorn = "^20.1.0"
|
||||
Pillow = "^8.4.0"
|
||||
sendgrid = "^6.7.0"
|
||||
sentry-sdk = "^1.4.3"
|
||||
django-polymorphic = "^3.1.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
coverage = "^5.5"
|
||||
nose-exclude = "^0.5.0"
|
||||
safety = "^1.10.3"
|
||||
pycodestyle = "^2.7.0"
|
||||
black = "^21.12b0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
|
||||
+123
-140
@@ -11,7 +11,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
|
||||
|
||||
|
||||
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
|
||||
SESSION_SERIALIZER = "django.contrib.sessions.serializers.PickleSerializer"
|
||||
|
||||
# Logger level
|
||||
|
||||
@@ -20,49 +20,47 @@ LOGPATH = os.path.join(BASE_DIR, "logs", "debug.log")
|
||||
|
||||
|
||||
def disable_pyexcel_logs(record):
|
||||
if record.module in ['loader', 'utils', 'source_plugin', 'plugin']:
|
||||
if record.module in ["loader", "utils", "source_plugin", "plugin"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'disable_pyexcel_logs': {
|
||||
'()': 'django.utils.log.CallbackFilter',
|
||||
'callback': disable_pyexcel_logs
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"filters": {
|
||||
"disable_pyexcel_logs": {
|
||||
"()": "django.utils.log.CallbackFilter",
|
||||
"callback": disable_pyexcel_logs,
|
||||
},
|
||||
},
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s: %(message)s'
|
||||
"formatters": {
|
||||
"verbose": {"format": "%(levelname)s %(asctime)s %(module)s: %(message)s"},
|
||||
},
|
||||
"handlers": {
|
||||
"file": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.FileHandler",
|
||||
"filename": LOGPATH,
|
||||
"formatter": "verbose",
|
||||
},
|
||||
"console": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
"filters": ["disable_pyexcel_logs"],
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'file': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': LOGPATH,
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'verbose',
|
||||
'filters': ['disable_pyexcel_logs'],
|
||||
},
|
||||
"root": {
|
||||
"handlers": ["file", "console"],
|
||||
"level": "DEBUG",
|
||||
"propagate": True,
|
||||
},
|
||||
'root': {
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': True,
|
||||
},
|
||||
'loggers': {
|
||||
'django': {
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'WARNING',
|
||||
'propagate': True,
|
||||
"loggers": {
|
||||
"django": {
|
||||
"handlers": ["file", "console"],
|
||||
"level": "WARNING",
|
||||
"propagate": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -71,121 +69,117 @@ LOGGING = {
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'modeltranslation', # has to be before admin for translation admin to work
|
||||
'suit',
|
||||
'dal',
|
||||
'dal_select2',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'rest_framework.authtoken',
|
||||
'corsheaders',
|
||||
'webapp',
|
||||
'members',
|
||||
'infoscreen',
|
||||
'kaehmy',
|
||||
'ohlhafv',
|
||||
'rest_framework',
|
||||
'rest_framework_jwt',
|
||||
'django_nose',
|
||||
'bootstrap3',
|
||||
'django_tables2',
|
||||
'auditlog',
|
||||
'phonenumber_field',
|
||||
'import_export',
|
||||
'django_filters',
|
||||
"modeltranslation", # has to be before admin for translation admin to work
|
||||
"suit",
|
||||
"dal",
|
||||
"dal_select2",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"rest_framework.authtoken",
|
||||
"corsheaders",
|
||||
"webapp",
|
||||
"members",
|
||||
"infoscreen",
|
||||
"kaehmy",
|
||||
"ohlhafv",
|
||||
"rest_framework",
|
||||
"rest_framework_jwt",
|
||||
"django_nose",
|
||||
"bootstrap3",
|
||||
"django_tables2",
|
||||
"auditlog",
|
||||
"phonenumber_field",
|
||||
"import_export",
|
||||
"django_filters",
|
||||
]
|
||||
|
||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||
|
||||
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
|
||||
TEST_RUNNER = "django_nose.NoseTestSuiteRunner"
|
||||
|
||||
NOSE_ARGS = [
|
||||
'--with-coverage',
|
||||
'--cover-package=webapp,members,infoscreen',
|
||||
'--exclude-dir={}'.format(os.path.join(BASE_DIR, 'members', 'migrations')),
|
||||
'--exclude-dir={}'.format(os.path.join(BASE_DIR,
|
||||
'infoscreen', 'migrations')),
|
||||
'--exclude-dir={}'.format(os.path.join(BASE_DIR, 'webapp', 'migrations')),
|
||||
"--with-coverage",
|
||||
"--cover-package=webapp,members,infoscreen",
|
||||
"--exclude-dir={}".format(os.path.join(BASE_DIR, "members", "migrations")),
|
||||
"--exclude-dir={}".format(os.path.join(BASE_DIR, "infoscreen", "migrations")),
|
||||
"--exclude-dir={}".format(os.path.join(BASE_DIR, "webapp", "migrations")),
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'sikweb.middleware.ForceDefaultLanguageMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'auditlog.middleware.AuditlogMiddleware'
|
||||
"sikweb.middleware.ForceDefaultLanguageMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"corsheaders.middleware.CorsMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"auditlog.middleware.AuditlogMiddleware",
|
||||
]
|
||||
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
|
||||
ROOT_URLCONF = 'sikweb.urls'
|
||||
ROOT_URLCONF = "sikweb.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': ['templates'],
|
||||
'OPTIONS': {
|
||||
'loaders': [
|
||||
'app_namespace.Loader',
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": ["templates"],
|
||||
"OPTIONS": {
|
||||
"loaders": [
|
||||
"app_namespace.Loader",
|
||||
"django.template.loaders.filesystem.Loader",
|
||||
"django.template.loaders.app_directories.Loader",
|
||||
],
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.template.context_processors.i18n',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django.template.context_processors.static',
|
||||
'dealer.contrib.django.context_processor',
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.template.context_processors.i18n",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"django.template.context_processors.static",
|
||||
"dealer.contrib.django.context_processor",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'sikweb.wsgi.application'
|
||||
WSGI_APPLICATION = "sikweb.wsgi.application"
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.'
|
||||
'UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation."
|
||||
"UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.'
|
||||
'MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation." "MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.'
|
||||
'CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation." "CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.'
|
||||
'NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation." "NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'rest_framework.permissions.IsAuthenticated',
|
||||
'rest_framework.permissions.DjangoModelPermissions',
|
||||
'rest_framework.permissions.IsAdminUser',
|
||||
"DEFAULT_PERMISSION_CLASSES": (
|
||||
"rest_framework.permissions.IsAuthenticated",
|
||||
"rest_framework.permissions.DjangoModelPermissions",
|
||||
"rest_framework.permissions.IsAdminUser",
|
||||
),
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||
"rest_framework_jwt.authentication.JSONWebTokenAuthentication",
|
||||
),
|
||||
# 'DEFAULT_THROTTLE_CLASSES': (
|
||||
# 'members.throttles.BurstRateThrottle',
|
||||
@@ -195,11 +189,9 @@ REST_FRAMEWORK = {
|
||||
# 'burst': '60/min',
|
||||
# 'sustained': '1000/day'
|
||||
# },
|
||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
||||
'PAGE_SIZE': 1000,
|
||||
'DEFAULT_FILTER_BACKENDS': (
|
||||
'django_filters.rest_framework.DjangoFilterBackend',
|
||||
),
|
||||
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
|
||||
"PAGE_SIZE": 1000,
|
||||
"DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",),
|
||||
}
|
||||
|
||||
# Email settings (tested working with gmail)
|
||||
@@ -213,17 +205,15 @@ REST_FRAMEWORK = {
|
||||
# https://docs.djangoproject.com/en/1.9/topics/i18n/
|
||||
|
||||
LANGUAGES = (
|
||||
('fi', _('Finnish')),
|
||||
('en', _('English')),
|
||||
("fi", _("Finnish")),
|
||||
("en", _("English")),
|
||||
)
|
||||
|
||||
LANGUAGE_CODE = 'fi'
|
||||
LANGUAGE_CODE = "fi"
|
||||
|
||||
LOCALE_PATHS = (
|
||||
os.path.join(BASE_DIR, 'locale'),
|
||||
)
|
||||
LOCALE_PATHS = (os.path.join(BASE_DIR, "locale"),)
|
||||
|
||||
TIME_ZONE = 'Europe/Helsinki'
|
||||
TIME_ZONE = "Europe/Helsinki"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
@@ -235,30 +225,26 @@ USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.9/howto/static-files/
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
)
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, 'static'),
|
||||
)
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
MEDIA_URL = '/media/'
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "collected_static")
|
||||
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
LOGIN_URL = '/login/'
|
||||
LOGIN_REDIRECT_URL = '/admin'
|
||||
LOGIN_URL = "/login/"
|
||||
LOGIN_REDIRECT_URL = "/admin"
|
||||
|
||||
SUIT_CONFIG = {
|
||||
# header
|
||||
'ADMIN_NAME': 'SIK Admin',
|
||||
"ADMIN_NAME": "SIK Admin",
|
||||
# 'HEADER_DATE_FORMAT': 'l, j. F Y',
|
||||
# 'HEADER_TIME_FORMAT': 'H:i',
|
||||
|
||||
# forms
|
||||
# 'SHOW_REQUIRED_ASTERISK': True, # Default True
|
||||
# 'CONFIRM_UNSAVED_CHANGES': True, # Default True
|
||||
|
||||
# menu
|
||||
# 'SEARCH_URL': '/admin/auth/user/',
|
||||
# 'MENU_ICONS': {
|
||||
@@ -273,11 +259,8 @@ SUIT_CONFIG = {
|
||||
# {'label': 'Settings', 'icon':'icon-cog', 'models': ('auth.user', 'auth.group')},
|
||||
# {'label': 'Support', 'icon':'icon-question-sign', 'url': '/support/'},
|
||||
# ),
|
||||
|
||||
# misc
|
||||
# 'LIST_PER_PAGE': 15
|
||||
}
|
||||
|
||||
JWT_AUTH = {
|
||||
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7)
|
||||
}
|
||||
JWT_AUTH = {"JWT_EXPIRATION_DELTA": datetime.timedelta(days=7)}
|
||||
|
||||
@@ -8,9 +8,10 @@ def ForceDefaultLanguageMiddleware(get_response):
|
||||
Should be installed *before* any middleware that checks request.META['HTTP_ACCEPT_LANGUAGE'],
|
||||
namely django.middleware.locale.LocaleMiddleware
|
||||
"""
|
||||
|
||||
def process_request(request):
|
||||
if 'HTTP_ACCEPT_LANGUAGE' in request.META:
|
||||
del request.META['HTTP_ACCEPT_LANGUAGE']
|
||||
if "HTTP_ACCEPT_LANGUAGE" in request.META:
|
||||
del request.META["HTTP_ACCEPT_LANGUAGE"]
|
||||
return get_response(request)
|
||||
|
||||
return process_request
|
||||
|
||||
+28
-28
@@ -1,4 +1,3 @@
|
||||
|
||||
"""
|
||||
Django settings for sikweb project.
|
||||
|
||||
@@ -15,8 +14,8 @@ import sentry_sdk
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
from sikweb.base import *
|
||||
|
||||
SENTRY_DSN = os.getenv('SENTRY_DSN', '')
|
||||
DEPLOY_ENV = os.getenv('DEPLOY_ENV', 'production')
|
||||
SENTRY_DSN = os.getenv("SENTRY_DSN", "")
|
||||
DEPLOY_ENV = os.getenv("DEPLOY_ENV", "production")
|
||||
|
||||
# Setup sentry
|
||||
sentry_sdk.init(
|
||||
@@ -25,52 +24,53 @@ sentry_sdk.init(
|
||||
integrations=[DjangoIntegration()],
|
||||
# If you wish to associate users to errors (assuming you are using
|
||||
# django.contrib.auth) you may enable sending PII data.
|
||||
send_default_pii=True
|
||||
send_default_pii=True,
|
||||
)
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = os.getenv('DEBUG', False) == 'True'
|
||||
DEBUG = os.getenv("DEBUG", False) == "True"
|
||||
|
||||
URL = os.getenv('HOST', 'api.sahkoinsinoorikilta.fi')
|
||||
FRONTEND_URL = os.getenv('FRONTEND_URL', 'sahkoinsinoorikilta.fi')
|
||||
ALLOWED_HOSTS = ['localhost', '127.0.0.1', FRONTEND_URL, URL]
|
||||
URL = os.getenv("HOST", "api.sahkoinsinoorikilta.fi")
|
||||
FRONTEND_URL = os.getenv("FRONTEND_URL", "sahkoinsinoorikilta.fi")
|
||||
ALLOWED_HOSTS = ["localhost", "127.0.0.1", FRONTEND_URL, URL]
|
||||
if DEBUG:
|
||||
ALLOWED_HOSTS = ['*']
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
USE_X_FORWARDED_HOST = True
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = os.getenv('SECRET_KEY', '7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(')
|
||||
SECRET_KEY = os.getenv(
|
||||
"SECRET_KEY", "7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp("
|
||||
)
|
||||
|
||||
# ReCaptcha
|
||||
# http://www.yaconiello.com/blog/integrating-google-recaptcha-to-django/
|
||||
GOOGLE_RECAPTCHA_SITE_KEY = os.getenv('GOOGLE_RECAPTCHA_SITE_KEY', 'YOUR-PUBLIC-KEY')
|
||||
GOOGLE_RECAPTCHA_SECRET_KEY = os.getenv('GOOGLE_RECAPTCHA_SECRET_KEY', 'YOUR-PRIVATE-KEY')
|
||||
GOOGLE_RECAPTCHA_SITE_KEY = os.getenv("GOOGLE_RECAPTCHA_SITE_KEY", "YOUR-PUBLIC-KEY")
|
||||
GOOGLE_RECAPTCHA_SECRET_KEY = os.getenv(
|
||||
"GOOGLE_RECAPTCHA_SECRET_KEY", "YOUR-PRIVATE-KEY"
|
||||
)
|
||||
|
||||
# Email settings (more settings in base.py)
|
||||
EMAIL_API_KEY = os.getenv('EMAIL_API_KEY', '')
|
||||
EMAIL_API_KEY = os.getenv("EMAIL_API_KEY", "")
|
||||
# EMAIL_API_SECRET = os.getenv('EMAIL_API_SECRET', '')
|
||||
DEFAULT_EMAIL_FROM = 'SIK'
|
||||
DEFAULT_EMAIL_FROM_ADDR = 'noreply@sahkoinsinoorikilta.fi'
|
||||
DEFAULT_EMAIL_FROM = "SIK"
|
||||
DEFAULT_EMAIL_FROM_ADDR = "noreply@sahkoinsinoorikilta.fi"
|
||||
ENABLE_AUTOMATIC_EMAILS = True
|
||||
|
||||
# Token for Telegram bot
|
||||
TELEGRAM_BOT_TOKEN = os.getenv('TG_BOT_TOKEN', '<tg token>')
|
||||
|
||||
# Database settings
|
||||
# Only uncomment if default settings in base.py are not ok
|
||||
|
||||
DB_OPTIONS = {'sslmode': 'require'} if os.getenv('DB_SSL', False) == 'True' else {}
|
||||
DB_OPTIONS = {"sslmode": "require"} if os.getenv("DB_SSL", False) == "True" else {}
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': os.getenv('DB_NAME', 'postgres'),
|
||||
'USER': os.getenv('DB_USER', 'postgres'),
|
||||
'PASSWORD': os.getenv('DB_PASSWD', 'postgres'),
|
||||
'HOST': os.getenv('DB_HOST', 'localhost'),
|
||||
'PORT': os.getenv('DB_PORT', 5432),
|
||||
'OPTIONS': DB_OPTIONS,
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||
"NAME": os.getenv("DB_NAME", "postgres"),
|
||||
"USER": os.getenv("DB_USER", "postgres"),
|
||||
"PASSWORD": os.getenv("DB_PASSWD", "postgres"),
|
||||
"HOST": os.getenv("DB_HOST", "localhost"),
|
||||
"PORT": os.getenv("DB_PORT", 5432),
|
||||
"OPTIONS": DB_OPTIONS,
|
||||
}
|
||||
}
|
||||
|
||||
+11
-17
@@ -23,28 +23,22 @@ from django.conf import settings
|
||||
from django.contrib.staticfiles import views as static_views
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
favicon_view = RedirectView.as_view(
|
||||
url='static/img/favicon.png', permanent=True)
|
||||
favicon_view = RedirectView.as_view(url="static/img/favicon.png", permanent=True)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'', include('webapp.urls')),
|
||||
url(r'^members/', include('members.urls')),
|
||||
url(r'^infoscreen/', include('infoscreen.urls')),
|
||||
url(r'^kaehmy/', include('kaehmy.urls')),
|
||||
url(r'^ohlhafv/', include('ohlhafv.urls')),
|
||||
|
||||
url(r"", include("webapp.urls")),
|
||||
url(r"^members/", include("members.urls")),
|
||||
url(r"^infoscreen/", include("infoscreen.urls")),
|
||||
url(r"^kaehmy/", include("kaehmy.urls")),
|
||||
url(r"^ohlhafv/", include("ohlhafv.urls")),
|
||||
# favourite icon
|
||||
url(r'^favicon\.ico$', favicon_view),
|
||||
|
||||
url(r"^favicon\.ico$", favicon_view),
|
||||
# admin
|
||||
url(r'^admin/', admin.site.urls),
|
||||
|
||||
url(r"^admin/", admin.site.urls),
|
||||
# i18n default view for changing the active language
|
||||
url(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
|
||||
url(r"^i18n/", include("django.conf.urls.i18n")),
|
||||
# staticfiles default view for static files in development
|
||||
url(r'^static/(?P<path>.*)$', static_views.serve),
|
||||
url(r'^media/(?P<path>.*)$',
|
||||
static_serve, {'document_root': settings.MEDIA_ROOT}),
|
||||
url(r"^static/(?P<path>.*)$", static_views.serve),
|
||||
url(r"^media/(?P<path>.*)$", static_serve, {"document_root": settings.MEDIA_ROOT}),
|
||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
||||
@@ -32,20 +32,16 @@ services:
|
||||
- DB_PORT=5432
|
||||
- DB_SSL=True
|
||||
- SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY
|
||||
- TG_BOT_TOKEN_FILE=/run/secrets/BACKEND_TG_BOT_TOKEN
|
||||
- DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD
|
||||
- EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY
|
||||
|
||||
secrets:
|
||||
- BACKEND_SECRET_KEY
|
||||
- BACKEND_TG_BOT_TOKEN
|
||||
- BACKEND_DB_PASSWD
|
||||
- BACKEND_EMAIL_API_KEY
|
||||
secrets:
|
||||
BACKEND_SECRET_KEY:
|
||||
external: true
|
||||
BACKEND_TG_BOT_TOKEN:
|
||||
external: true
|
||||
BACKEND_DB_PASSWD:
|
||||
external: true
|
||||
BACKEND_EMAIL_API_KEY:
|
||||
|
||||
+15
-1
@@ -1,9 +1,21 @@
|
||||
"""File containing webapp app admin registers."""
|
||||
|
||||
from django.contrib import admin
|
||||
from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd
|
||||
from webapp.models import (
|
||||
Feed,
|
||||
Tag,
|
||||
Event,
|
||||
Signup,
|
||||
SignupForm,
|
||||
TemplateQuestion,
|
||||
JobAd,
|
||||
BaseWebhook,
|
||||
GenericWebhook,
|
||||
TelegramHook,
|
||||
)
|
||||
from modeltranslation.admin import TranslationAdmin
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
# this is needed so that the models get registered for translation
|
||||
import webapp.translation
|
||||
|
||||
@@ -16,3 +28,5 @@ admin.site.register(SignupForm, TranslationAdmin)
|
||||
admin.site.register(Signup, TranslationAdmin)
|
||||
admin.site.register(TemplateQuestion, TranslationAdmin)
|
||||
admin.site.register(JobAd, TranslationAdmin)
|
||||
admin.site.register(GenericWebhook, TranslationAdmin)
|
||||
admin.site.register(TelegramHook, TranslationAdmin)
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ from django.apps import AppConfig
|
||||
class WebappConfig(AppConfig):
|
||||
"""Webapp configurations."""
|
||||
|
||||
name = 'webapp'
|
||||
name = "webapp"
|
||||
|
||||
def ready(self):
|
||||
"""Import translations."""
|
||||
|
||||
@@ -17,4 +17,8 @@ class Command(BaseCommand):
|
||||
u.is_staff = True
|
||||
u.save()
|
||||
|
||||
self.stdout.write("Created default user {} with password {}.".format(self.user_name, self.password))
|
||||
self.stdout.write(
|
||||
"Created default user {} with password {}.".format(
|
||||
self.user_name, self.password
|
||||
)
|
||||
)
|
||||
|
||||
@@ -10,17 +10,18 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
print('THIS SCRIPT WILL GENERATE DUMMY VALUES TO DATABASE '
|
||||
'AND SHOULD __NEVER__ BE RUN ON PRODUCTION. '
|
||||
'IF YOU ARE ON PRODUCTION ABORT (ctrl-c) IMMEDIATELY!!!! '
|
||||
'CONTINUING IN 10 SECONDS')
|
||||
print(
|
||||
"THIS SCRIPT WILL GENERATE DUMMY VALUES TO DATABASE "
|
||||
"AND SHOULD __NEVER__ BE RUN ON PRODUCTION. "
|
||||
"IF YOU ARE ON PRODUCTION ABORT (ctrl-c) IMMEDIATELY!!!! "
|
||||
"CONTINUING IN 10 SECONDS"
|
||||
)
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
DOMAINS = ["example.coms", 'ggmail.om', "notmail.dom"] # intentionally wrong
|
||||
DOMAINS = ["example.coms", "ggmail.om", "notmail.dom"] # intentionally wrong
|
||||
PLACES = ["Helsinki", "Espoo", "Korso", "Kerava", "Kouvostoliitto"]
|
||||
MEMBER_COUNT = 30
|
||||
MEMBER_REQUEST_COUNT = 3
|
||||
@@ -28,44 +29,37 @@ class Command(BaseCommand):
|
||||
names = generate_names(MEMBER_COUNT)
|
||||
for i in range(MEMBER_COUNT):
|
||||
first, last = names[i]
|
||||
mail = "{}.{}@{}".format(first.lower(), last.lower(), random.choice(DOMAINS))
|
||||
mail = "{}.{}@{}".format(
|
||||
first.lower(), last.lower(), random.choice(DOMAINS)
|
||||
)
|
||||
|
||||
por = random.choice(PLACES)
|
||||
ayy = random.randint(0, 1)
|
||||
jas = random.randint(0, 1)
|
||||
Member.objects.create(first_name=first,
|
||||
last_name=last,
|
||||
email=mail,
|
||||
POR=por,
|
||||
AYY=ayy,
|
||||
jas=jas)
|
||||
Member.objects.create(
|
||||
first_name=first, last_name=last, email=mail, POR=por, AYY=ayy, jas=jas
|
||||
)
|
||||
|
||||
i_item = ExternalImageInfoItem.objects.create(
|
||||
name="Heavy",
|
||||
url="https://i.imgur.com/XXSSqDG.gif"
|
||||
name="Heavy", url="https://i.imgur.com/XXSSqDG.gif"
|
||||
)
|
||||
|
||||
rot = Rotation.objects.create(name="Demo")
|
||||
inst = InfoInstance.objects.create(
|
||||
rotation=rot,
|
||||
item=i_item,
|
||||
duration=20.0
|
||||
)
|
||||
inst = InfoInstance.objects.create(rotation=rot, item=i_item, duration=20.0)
|
||||
|
||||
names = generate_names(MEMBER_COUNT)
|
||||
for i in range(MEMBER_COUNT):
|
||||
first, last = names[i]
|
||||
mail = "{}.{}@{}".format(first.lower(), last.lower(), random.choice(DOMAINS))
|
||||
mail = "{}.{}@{}".format(
|
||||
first.lower(), last.lower(), random.choice(DOMAINS)
|
||||
)
|
||||
|
||||
por = random.choice(PLACES)
|
||||
ayy = random.randint(0, 1)
|
||||
jas = random.randint(0, 1)
|
||||
Member.objects.create(first_name=first,
|
||||
last_name=last,
|
||||
email=mail,
|
||||
POR=por,
|
||||
AYY=ayy,
|
||||
jas=jas)
|
||||
Member.objects.create(
|
||||
first_name=first, last_name=last, email=mail, POR=por, AYY=ayy, jas=jas
|
||||
)
|
||||
|
||||
TAGS = ["Party", "International", "Freshmen", "Culture"]
|
||||
TAG_COUNT = 2
|
||||
@@ -74,9 +68,9 @@ class Command(BaseCommand):
|
||||
slug = TAGS[i] + str(random.randint(0, 10))
|
||||
name = slug + str(random.randint(0, 10))
|
||||
print(slug, name)
|
||||
Tag.objects.create(slug=slug,
|
||||
name=name,
|
||||
icon="http://testiurl.com/kuva.jpg")
|
||||
Tag.objects.create(
|
||||
slug=slug, name=name, icon="http://testiurl.com/kuva.jpg"
|
||||
)
|
||||
|
||||
FEED_COUNT = 3
|
||||
|
||||
@@ -84,10 +78,9 @@ class Command(BaseCommand):
|
||||
title = "ds" + str(random.randint(0, 15))
|
||||
description = "dsg" + str(random.randint(0, 20))
|
||||
content = "fdfd"
|
||||
Feed.objects.create(title=title,
|
||||
visible=True,
|
||||
description=description,
|
||||
content=content)
|
||||
Feed.objects.create(
|
||||
title=title, visible=True, description=description, content=content
|
||||
)
|
||||
tag1 = Tag.objects.get(id=1)
|
||||
Feed.objects.get(title=title).tags.add(tag1)
|
||||
tag2 = Tag.objects.get(id=1)
|
||||
|
||||
@@ -4,42 +4,56 @@ from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
"""
|
||||
Creates initial skeleton for the webapp.
|
||||
This command MUST do nothing if already run.
|
||||
'''
|
||||
"""
|
||||
|
||||
def create_infoscreen_moderator(self):
|
||||
self.stdout.write("Creating infoscreen moderator group")
|
||||
infoscreen_group, created = Group.objects.get_or_create(name="infoscreen moderators")
|
||||
infoscreen_group, created = Group.objects.get_or_create(
|
||||
name="infoscreen moderators"
|
||||
)
|
||||
if not created:
|
||||
self.stdout.write('The group "infoscreen moderators" already existed '
|
||||
'and was not therefore created')
|
||||
self.stdout.write(
|
||||
'The group "infoscreen moderators" already existed '
|
||||
"and was not therefore created"
|
||||
)
|
||||
|
||||
cts = ContentType.objects.filter(app_label='infoscreen')
|
||||
cts = ContentType.objects.filter(app_label="infoscreen")
|
||||
permissions = Permission.objects.filter(content_type__in=cts)
|
||||
infoscreen_group.permissions.add(*permissions)
|
||||
|
||||
def create_member_register_viewer(self):
|
||||
self.stdout.write("Creating member register viewer group")
|
||||
viewers_group, created = Group.objects.get_or_create(name="member register viewers")
|
||||
viewers_group, created = Group.objects.get_or_create(
|
||||
name="member register viewers"
|
||||
)
|
||||
if not created:
|
||||
self.stdout.write('The group "member register viewers" already existed '
|
||||
'and was not therefore created')
|
||||
self.stdout.write(
|
||||
'The group "member register viewers" already existed '
|
||||
"and was not therefore created"
|
||||
)
|
||||
|
||||
cts = ContentType.objects.filter(app_label='members')
|
||||
members_permissions = Permission.objects.filter(content_type__in=cts, codename__contains='read')
|
||||
cts = ContentType.objects.filter(app_label="members")
|
||||
members_permissions = Permission.objects.filter(
|
||||
content_type__in=cts, codename__contains="read"
|
||||
)
|
||||
|
||||
viewers_group.permissions.add(*members_permissions)
|
||||
|
||||
def create_member_register_administrator(self):
|
||||
self.stdout.write("Creating member register administrator group")
|
||||
admins_group, created = Group.objects.get_or_create(name="member register administrators")
|
||||
admins_group, created = Group.objects.get_or_create(
|
||||
name="member register administrators"
|
||||
)
|
||||
if not created:
|
||||
self.stdout.write('The group "member register administrators" already existed '
|
||||
'and was not therefore created')
|
||||
self.stdout.write(
|
||||
'The group "member register administrators" already existed '
|
||||
"and was not therefore created"
|
||||
)
|
||||
|
||||
cts = ContentType.objects.filter(app_label='members')
|
||||
cts = ContentType.objects.filter(app_label="members")
|
||||
permissions = Permission.objects.filter(content_type__in=cts)
|
||||
admins_group.permissions.add(*permissions)
|
||||
|
||||
@@ -47,13 +61,20 @@ class Command(BaseCommand):
|
||||
self.stdout.write("Creating official group")
|
||||
officials_group, created = Group.objects.get_or_create(name="officials")
|
||||
if not created:
|
||||
self.stdout.write('The group "officials" already existed '
|
||||
'and was not therefore created')
|
||||
self.stdout.write(
|
||||
'The group "officials" already existed ' "and was not therefore created"
|
||||
)
|
||||
|
||||
cts = ContentType.objects.filter(app_label='webapp')
|
||||
feed_permissions = Permission.objects.filter(content_type__in=cts, codename__contains='feed')
|
||||
event_permissions = Permission.objects.filter(content_type__in=cts, codename__contains='event')
|
||||
registration_permissions = Permission.objects.filter(content_type__in=cts, codename__contains='registration')
|
||||
cts = ContentType.objects.filter(app_label="webapp")
|
||||
feed_permissions = Permission.objects.filter(
|
||||
content_type__in=cts, codename__contains="feed"
|
||||
)
|
||||
event_permissions = Permission.objects.filter(
|
||||
content_type__in=cts, codename__contains="event"
|
||||
)
|
||||
registration_permissions = Permission.objects.filter(
|
||||
content_type__in=cts, codename__contains="registration"
|
||||
)
|
||||
|
||||
officials_group.permissions.add(*feed_permissions)
|
||||
officials_group.permissions.add(*event_permissions)
|
||||
@@ -61,12 +82,16 @@ class Command(BaseCommand):
|
||||
|
||||
def create_webapp_administrator(self):
|
||||
self.stdout.write("Creating webapp administrator group")
|
||||
admins_group, created = Group.objects.get_or_create(name="webapp administrators")
|
||||
admins_group, created = Group.objects.get_or_create(
|
||||
name="webapp administrators"
|
||||
)
|
||||
if not created:
|
||||
self.stdout.write('The group "webapp administrators" already existed '
|
||||
'and was not therefore created')
|
||||
self.stdout.write(
|
||||
'The group "webapp administrators" already existed '
|
||||
"and was not therefore created"
|
||||
)
|
||||
|
||||
cts = ContentType.objects.filter(app_label='webapp')
|
||||
cts = ContentType.objects.filter(app_label="webapp")
|
||||
permissions = Permission.objects.filter(content_type__in=cts)
|
||||
admins_group.permissions.add(*permissions)
|
||||
|
||||
|
||||
@@ -11,46 +11,94 @@ class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Info',
|
||||
name="Info",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('publish_time', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"publish_time",
|
||||
models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InfoTr',
|
||||
name="InfoTr",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('lang', models.CharField(default='fi', max_length=2)),
|
||||
('topic', models.CharField(max_length=255)),
|
||||
('content', models.TextField()),
|
||||
('translation_for', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='webapp.Info')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("lang", models.CharField(default="fi", max_length=2)),
|
||||
("topic", models.CharField(max_length=255)),
|
||||
("content", models.TextField()),
|
||||
(
|
||||
"translation_for",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="translations",
|
||||
to="webapp.Info",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Tag',
|
||||
name="Tag",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dummyname', models.CharField(max_length=127)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("dummyname", models.CharField(max_length=127)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TagTr',
|
||||
name="TagTr",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('lang', models.CharField(default='fi', max_length=2)),
|
||||
('name', models.CharField(max_length=127)),
|
||||
('translation_for', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='webapp.Tag')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("lang", models.CharField(default="fi", max_length=2)),
|
||||
("name", models.CharField(max_length=127)),
|
||||
(
|
||||
"translation_for",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="translations",
|
||||
to="webapp.Tag",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='info',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(related_name='news', to='webapp.Tag'),
|
||||
model_name="info",
|
||||
name="tags",
|
||||
field=models.ManyToManyField(related_name="news", to="webapp.Tag"),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -10,62 +10,76 @@ import webapp.utils
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0001_initial'),
|
||||
("webapp", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Feed',
|
||||
name="Feed",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('publish_time', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('autohide', models.BooleanField(default=False)),
|
||||
('autohide_time', models.DateTimeField(default=webapp.utils.month_from_now)),
|
||||
('topic', models.CharField(max_length=255)),
|
||||
('description', models.CharField(max_length=255)),
|
||||
('content', models.TextField()),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"publish_time",
|
||||
models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
("autohide", models.BooleanField(default=False)),
|
||||
(
|
||||
"autohide_time",
|
||||
models.DateTimeField(default=webapp.utils.month_from_now),
|
||||
),
|
||||
("topic", models.CharField(max_length=255)),
|
||||
("description", models.CharField(max_length=255)),
|
||||
("content", models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='info',
|
||||
name='tags',
|
||||
model_name="info",
|
||||
name="tags",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='infotr',
|
||||
name='translation_for',
|
||||
model_name="infotr",
|
||||
name="translation_for",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='tagtr',
|
||||
name='translation_for',
|
||||
model_name="tagtr",
|
||||
name="translation_for",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='tag',
|
||||
name='dummyname',
|
||||
model_name="tag",
|
||||
name="dummyname",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='name',
|
||||
field=models.CharField(default='', max_length=127),
|
||||
model_name="tag",
|
||||
name="name",
|
||||
field=models.CharField(default="", max_length=127),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='slug',
|
||||
field=models.SlugField(default=''),
|
||||
model_name="tag",
|
||||
name="slug",
|
||||
field=models.SlugField(default=""),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Info',
|
||||
name="Info",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='InfoTr',
|
||||
name="InfoTr",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='TagTr',
|
||||
name="TagTr",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(related_name='news', to='webapp.Tag'),
|
||||
model_name="feed",
|
||||
name="tags",
|
||||
field=models.ManyToManyField(related_name="news", to="webapp.Tag"),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -11,59 +11,114 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
('webapp', '0001_initial'),
|
||||
("auth", "0008_alter_user_username_max_length"),
|
||||
("webapp", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BaseRole',
|
||||
name="BaseRole",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField(verbose_name='Name')),
|
||||
('is_board', models.BooleanField(verbose_name='Board member')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.TextField(verbose_name="Name")),
|
||||
("is_board", models.BooleanField(verbose_name="Board member")),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Official',
|
||||
name="Official",
|
||||
fields=[
|
||||
('user_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
|
||||
('phone_number', models.TextField(verbose_name='Phone number')),
|
||||
(
|
||||
"user_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
("phone_number", models.TextField(verbose_name="Phone number")),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
'abstract': False,
|
||||
"verbose_name": "user",
|
||||
"verbose_name_plural": "users",
|
||||
"abstract": False,
|
||||
},
|
||||
bases=('auth.user',),
|
||||
bases=("auth.user",),
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
("objects", django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomRole',
|
||||
name="CustomRole",
|
||||
fields=[
|
||||
('baserole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.BaseRole')),
|
||||
(
|
||||
"baserole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="webapp.BaseRole",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('webapp.baserole',),
|
||||
bases=("webapp.baserole",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PresetRole',
|
||||
name="PresetRole",
|
||||
fields=[
|
||||
('baserole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.BaseRole')),
|
||||
('description', models.TextField(verbose_name='Description')),
|
||||
('summary', models.TextField(verbose_name='Summary')),
|
||||
(
|
||||
"baserole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="webapp.BaseRole",
|
||||
),
|
||||
),
|
||||
("description", models.TextField(verbose_name="Description")),
|
||||
("summary", models.TextField(verbose_name="Summary")),
|
||||
],
|
||||
bases=('webapp.baserole',),
|
||||
bases=("webapp.baserole",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Role',
|
||||
name="Role",
|
||||
fields=[
|
||||
('presetrole_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.PresetRole')),
|
||||
('start_date', models.DateField(verbose_name='Start date')),
|
||||
('end_date', models.DateField(verbose_name='End date')),
|
||||
('official', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='roles', to='webapp.Official')),
|
||||
(
|
||||
"presetrole_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="webapp.PresetRole",
|
||||
),
|
||||
),
|
||||
("start_date", models.DateField(verbose_name="Start date")),
|
||||
("end_date", models.DateField(verbose_name="End date")),
|
||||
(
|
||||
"official",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="roles",
|
||||
to="webapp.Official",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=('webapp.presetrole',),
|
||||
bases=("webapp.presetrole",),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,38 +8,38 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0002_auto_20170601_1919'),
|
||||
("webapp", "0002_auto_20170601_1919"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='content_en',
|
||||
model_name="feed",
|
||||
name="content_en",
|
||||
field=models.TextField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='content_fi',
|
||||
model_name="feed",
|
||||
name="content_fi",
|
||||
field=models.TextField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='description_en',
|
||||
model_name="feed",
|
||||
name="description_en",
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='description_fi',
|
||||
model_name="feed",
|
||||
name="description_fi",
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='topic_en',
|
||||
model_name="feed",
|
||||
name="topic_en",
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='topic_fi',
|
||||
model_name="feed",
|
||||
name="topic_fi",
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,12 +8,12 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0002_baserole_customrole_official_presetrole_role'),
|
||||
("webapp", "0002_baserole_customrole_official_presetrole_role"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='role',
|
||||
options={'verbose_name': 'Official'},
|
||||
name="role",
|
||||
options={"verbose_name": "Official"},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,13 +8,15 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0003_auto_20170607_1643'),
|
||||
("webapp", "0003_auto_20170607_1643"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='feed',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(blank=True, related_name='news', to='webapp.Tag'),
|
||||
model_name="feed",
|
||||
name="tags",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, related_name="news", to="webapp.Tag"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,16 +8,16 @@ from django.db import migrations
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0003_auto_20170607_1825'),
|
||||
("webapp", "0003_auto_20170607_1825"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='official',
|
||||
options={'verbose_name': 'Official'},
|
||||
name="official",
|
||||
options={"verbose_name": "Official"},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='role',
|
||||
options={'verbose_name': 'Role'},
|
||||
name="role",
|
||||
options={"verbose_name": "Role"},
|
||||
),
|
||||
]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user