Move and rename a lot of stuff into kaehmy app
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from modeltranslation.admin import TranslationAdmin
|
||||||
|
|
||||||
|
from kaehmy.models import Application, Comment, CustomRole, PresetRole, TelegramChannel
|
||||||
|
|
||||||
|
admin.site.register(Application)
|
||||||
|
admin.site.register(Comment)
|
||||||
|
admin.site.register(CustomRole)
|
||||||
|
admin.site.register(PresetRole, TranslationAdmin)
|
||||||
|
admin.site.register(TelegramChannel)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class KaehmyConfig(AppConfig):
|
||||||
|
name = 'kaehmy'
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from kaehmy.models import PresetRole, CustomRole, Application, Comment
|
||||||
|
from webapp.models import BaseRole
|
||||||
|
|
||||||
|
class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple):
|
||||||
|
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)
|
||||||
|
description = PresetRole.objects.get(id=value).description
|
||||||
|
dic['description'] = description
|
||||||
|
return dic
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CheckboxSelectMultiple, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationForm(forms.ModelForm):
|
||||||
|
"""Class representing Kaehmy form."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta for class Application."""
|
||||||
|
|
||||||
|
model = Application
|
||||||
|
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)')
|
||||||
|
|
||||||
|
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"].help_text = ""
|
||||||
|
self.fields["custom_roles"].label = _('Custom roles')
|
||||||
|
self.fields["custom_roles"].queryset = CustomRole.objects.all()
|
||||||
|
|
||||||
|
for cat_id, category in BaseRole.CATEGORIES:
|
||||||
|
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].help_text = ""
|
||||||
|
self.fields[key].queryset = qset
|
||||||
|
self.fields[key].label = _(category)
|
||||||
|
self.fields[key].required = False
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
def clean_phone_number(self):
|
||||||
|
"""Clean phone number field."""
|
||||||
|
number = self.cleaned_data.get('phone_number')
|
||||||
|
if number.isdigit():
|
||||||
|
return number
|
||||||
|
else:
|
||||||
|
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')
|
||||||
|
if not CustomRole.objects.filter(name=custom_name).exists():
|
||||||
|
return custom_name
|
||||||
|
else:
|
||||||
|
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"]]
|
||||||
|
|
||||||
|
|
||||||
|
class CommentForm(forms.ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Comment
|
||||||
|
fields = ['name', 'email', 'message', 'parent']
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11 on 2018-01-25 22:31
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('webapp', '0038_auto_20180126_0031'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
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')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Custom kaehmy roles',
|
||||||
|
'verbose_name': 'Custom kaehmy role',
|
||||||
|
},
|
||||||
|
bases=('webapp.baserole',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
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')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Preset kaehmy roles',
|
||||||
|
'verbose_name': 'Preset kaehmy role',
|
||||||
|
},
|
||||||
|
bases=('webapp.presetrole',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
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)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Telegram channels',
|
||||||
|
'verbose_name': 'Telegram channel',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
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')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Kaehmylomakkeet',
|
||||||
|
'verbose_name': 'Kaehmylomake',
|
||||||
|
},
|
||||||
|
bases=('kaehmy.commentparent',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
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')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Kaehmykommentit',
|
||||||
|
'verbose_name': 'Kaehmykommentti',
|
||||||
|
},
|
||||||
|
bases=('kaehmy.commentparent',),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
"""Webapp app models."""
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import timedelta
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from webapp.utils import month_from_now
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from auditlog.registry import auditlog
|
||||||
|
from phonenumber_field.modelfields import PhoneNumberField
|
||||||
|
from django.contrib.postgres.fields import JSONField
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import webapp.models
|
||||||
|
|
||||||
|
VERBOSE_NAME = _('Kaehmy')
|
||||||
|
|
||||||
|
|
||||||
|
class PresetRole(webapp.models.PresetRole):
|
||||||
|
"""Model for kaehmy role."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Preset kaehmy role')
|
||||||
|
verbose_name_plural = _('Preset kaehmy roles')
|
||||||
|
|
||||||
|
|
||||||
|
class CustomRole(webapp.models.BaseRole):
|
||||||
|
"""Model representing a user-specified custom occupation."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
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)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'Message parent #{}'.format(self.id)
|
||||||
|
|
||||||
|
|
||||||
|
class Comment(CommentParent):
|
||||||
|
"""
|
||||||
|
Model representing a kaehmymessage.
|
||||||
|
|
||||||
|
Every message relates to certain kaehmyform or parent message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Kaehmykommentti')
|
||||||
|
verbose_name_plural = _('Kaehmykommentit')
|
||||||
|
|
||||||
|
message = models.TextField(_('Message'))
|
||||||
|
parent = models.ForeignKey('CommentParent', related_name='messages')
|
||||||
|
|
||||||
|
|
||||||
|
class Application(CommentParent):
|
||||||
|
"""
|
||||||
|
Model representing a form for kaehmy.
|
||||||
|
|
||||||
|
Allows user to choose from existing roles or to create custom ones.
|
||||||
|
"""
|
||||||
|
YEAR_CHOICES = (
|
||||||
|
(1, '1'),
|
||||||
|
(2, '2'),
|
||||||
|
(3, '3'),
|
||||||
|
(4, '4'),
|
||||||
|
(5, 'N'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
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)
|
||||||
|
custom_role_name = models.CharField(
|
||||||
|
_('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)
|
||||||
|
preset_roles = models.ManyToManyField(
|
||||||
|
'kaehmy.PresetRole', related_name='forms', blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return model info."""
|
||||||
|
return _('Kaehmy application: {}').format(self.name)
|
||||||
|
|
||||||
|
def comment_count(self):
|
||||||
|
"""Count comments for kaehmy."""
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
def recurse(message):
|
||||||
|
count = 0
|
||||||
|
for msg in message.messages.all():
|
||||||
|
count += recurse(msg)
|
||||||
|
|
||||||
|
return count + 1
|
||||||
|
|
||||||
|
for message in self.messages.all():
|
||||||
|
total += recurse(message)
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
def board_roles(self):
|
||||||
|
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 ''
|
||||||
|
|
||||||
|
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)]
|
||||||
|
combined = presets + customs
|
||||||
|
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 ''
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import django_tables2 as tables
|
||||||
|
from django.db.models import Count, Q
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from webapp.models import Application
|
||||||
|
|
||||||
|
|
||||||
|
class ExportTable(tables.Table):
|
||||||
|
class Meta:
|
||||||
|
model = Application
|
||||||
|
exclude = ['text', 'messageparent_ptr', 'custom_role_name', 'custom_role_is_board', 'timestamp']
|
||||||
|
|
||||||
|
all_roles = tables.Column(verbose_name=_('Roles'), orderable=False)
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
{% extends "form_base.html" %}
|
{% extends "project.html" %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="kaehmy_header">
|
<div class="kaehmy_header">
|
||||||
{% include "kaehmy/header.html" %}
|
{% include ":header.html" %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock header %}
|
{% endblock header %}
|
||||||
|
|
||||||
{% block navigation %}
|
{% block navigation %}
|
||||||
{% include "kaehmy/navigation.html" %}
|
{% include ":navigation.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<div class="kaehmy-content">
|
<div class="kaehmy-content">
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
{% include "kaehmy/footer.html" %}
|
{% include ":footer.html" %}
|
||||||
{% endblock footer %}
|
{% endblock footer %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "kaehmy/base.html" %}
|
{% extends "kaehmy:base.html" %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{% extends "kaehmy/base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
{% extends "kaehmy/base.html" %}
|
{% extends "kaehmy:base.html" %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block navigation %}
|
{% block navigation %}
|
||||||
{% include "kaehmy/navigation.html" %}
|
{% include "kaehmy:navigation.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
{% extends "kaehmy/base.html" %}
|
{% extends "kaehmy:base.html" %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block navigation %}
|
{% block navigation %}
|
||||||
{% include "kaehmy/navigation.html" %}
|
{% include "kaehmy:navigation.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
|
|
||||||
<div class="collapse" id="collapse_{{ application.id }}">
|
<div class="collapse" id="collapse_{{ application.id }}">
|
||||||
{% for message in application.messages.all %}
|
{% for message in application.messages.all %}
|
||||||
{% include "kaehmy/message.html" with messages=message.messages.all %}
|
{% include "kaehmy:message.html" with messages=message.messages.all %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
{% include "kaehmy/message.html" with messages=message.messages.all %}
|
{% include "message.html" with messages=message.messages.all %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
{% extends "kaehmy/base.html" %}
|
{% extends "kaehmy:base.html" %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block navigation %}
|
{% block navigation %}
|
||||||
{% include "kaehmy/navigation.html" %}
|
{% include "kaehmy:navigation.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"""Translation classes."""
|
||||||
|
|
||||||
|
from modeltranslation.translator import register, TranslationOptions
|
||||||
|
from kaehmy.models import PresetRole
|
||||||
|
|
||||||
|
|
||||||
|
@register(PresetRole)
|
||||||
|
class PresetRoleTranslationOptions(TranslationOptions):
|
||||||
|
""" Class for PresetRole translation options"""
|
||||||
|
|
||||||
|
fields = ()
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
"""Kaehmy urls."""
|
||||||
|
|
||||||
|
from django.conf.urls import url
|
||||||
|
# from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from kaehmy.views import view
|
||||||
|
from kaehmy.views import list_view
|
||||||
|
from kaehmy.views import submit
|
||||||
|
from kaehmy.views import comment
|
||||||
|
from kaehmy.views import statistics_view
|
||||||
|
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),
|
||||||
|
]
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
from django.db.models import Count
|
||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from django.contrib.auth import login, logout, authenticate
|
||||||
|
from django.views.decorators.http import require_http_methods
|
||||||
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
from django.contrib.auth.decorators import permission_required, login_required
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
from dealer.git import git
|
||||||
|
|
||||||
|
from members.views.utils import *
|
||||||
|
from kaehmy.models import Application, CustomRole, PresetRole
|
||||||
|
from kaehmy.forms import ApplicationForm
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
def list_view(request, *args, **kwargs):
|
||||||
|
"""Kaehmy application list"""
|
||||||
|
|
||||||
|
role_filter = request.GET.get('role', None)
|
||||||
|
if role_filter is not None and str(role_filter) != '-1':
|
||||||
|
applications = Application.objects.filter(custom_roles__id=role_filter) | Application.objects.filter(preset_roles__id=role_filter)
|
||||||
|
else:
|
||||||
|
applications = Application.objects.all()
|
||||||
|
|
||||||
|
applications = applications.order_by('-timestamp')
|
||||||
|
filter_options_preset = PresetRole.objects.annotate(form_count=Count('forms')).filter(form_count__gt=0)
|
||||||
|
filter_options_preset_list = [(r.id, r.name, r.form_count) for r in filter_options_preset]
|
||||||
|
filter_options_custom = CustomRole.objects.annotate(form_count=Count('forms')).filter(form_count__gt=0)
|
||||||
|
filter_options_custom_list = [(r.id, r.name, r.form_count) for r in filter_options_custom]
|
||||||
|
|
||||||
|
filter_options = filter_options_preset_list + filter_options_custom_list
|
||||||
|
filter_options.sort(key=lambda f: f[1])
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'applications': applications,
|
||||||
|
'application_count': len(applications),
|
||||||
|
'filter_options': filter_options
|
||||||
|
}
|
||||||
|
return render(request, 'list.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
def comment(request, *args, **kwargs):
|
||||||
|
"""POST endpoint for commenting"""
|
||||||
|
|
||||||
|
form = KaehmyCommentForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
comment = form.save()
|
||||||
|
email = comment.parent.email
|
||||||
|
name = comment.name
|
||||||
|
|
||||||
|
subject = 'Kaehmyysi tai kommenttiisi on vastattu!'
|
||||||
|
body = ('{} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.\r\n\r\n'
|
||||||
|
'Käy lukemassa viesti osoitteessa http://sika.sahkoinsinoorikilta.fi/kaehmy').format(name.capitalize())
|
||||||
|
|
||||||
|
send_email(email, subject, body)
|
||||||
|
logging.debug(
|
||||||
|
'Sent kaehmy comment email to recipient <{}>'.format(email))
|
||||||
|
|
||||||
|
return redirect('/kaehmy')
|
||||||
|
else:
|
||||||
|
print(form)
|
||||||
|
context = {
|
||||||
|
'error': form.errors
|
||||||
|
}
|
||||||
|
return render(request, 'error.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
def statistics_view(request, *args, **kwargs):
|
||||||
|
"""Kaehmys roles listed by applications"""
|
||||||
|
|
||||||
|
applications = Application.objects.all()
|
||||||
|
role_list = []
|
||||||
|
|
||||||
|
preset_roles = PresetRole.objects.all()
|
||||||
|
custom_roles = CustomRole.objects.all()
|
||||||
|
|
||||||
|
for preset in preset_roles:
|
||||||
|
people = [form.name for form in preset.forms.all()]
|
||||||
|
role_list.append((preset.name, len(people), ', '.join(people)))
|
||||||
|
for custom in custom_roles:
|
||||||
|
people = [form.name for form in custom.forms.all()]
|
||||||
|
role_list.append((custom.name, len(people), ', '.join(people)))
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'applications': applications,
|
||||||
|
'application_count': len(applications),
|
||||||
|
'role_list': role_list
|
||||||
|
}
|
||||||
|
return render(request, 'statistics.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
def view(request, *args, **kwargs):
|
||||||
|
"""Render Kaehmy form page."""
|
||||||
|
form = ApplicationForm()
|
||||||
|
return render(request, 'kaehmy.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
def submit(request, *args, **kwargs):
|
||||||
|
"""Submit Kaehmy form."""
|
||||||
|
form = ApplicationForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
application = form.save()
|
||||||
|
custom_name = form.cleaned_data.get('custom_role_name')
|
||||||
|
custom_is_board = form.cleaned_data.get('custom_role_is_board')
|
||||||
|
|
||||||
|
if len(custom_name) > 0:
|
||||||
|
custom_role = CustomRole(
|
||||||
|
name=custom_name, is_board=custom_is_board)
|
||||||
|
custom_role.save()
|
||||||
|
application.custom_roles.add(custom_role)
|
||||||
|
|
||||||
|
url = 'https://sika.sahkoinsinoorikilta.fi/kaehmy'
|
||||||
|
|
||||||
|
email = form.cleaned_data.get('email', '')
|
||||||
|
name = form.cleaned_data.get('name', 'Anonymous')
|
||||||
|
subject = 'Arwokas kirjattu kirje mahdolliselle tulewalle kiltahenkilölle'
|
||||||
|
body = ('Moikka {}!\r\n\r\nHienoa, että kilta kiinnostaa! Kaehmysi on vastaanotettu.\r\n'
|
||||||
|
'Mahdollisista kommenteista tulee ilmoitus sähköpostitse.\r\n\r\n'
|
||||||
|
'Käy katsomassa kaehmytilanne osoitteessa {}').format(name, url)
|
||||||
|
|
||||||
|
send_email(email, subject, body)
|
||||||
|
logging.debug('Sent kaehmy email to recipient <{}>'.format(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)))
|
||||||
|
|
||||||
|
else:
|
||||||
|
context = {
|
||||||
|
'error': form.errors
|
||||||
|
}
|
||||||
|
return render(request, 'error.html', context)
|
||||||
|
return HttpResponseRedirect('/kaehmy')
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(['GET'])
|
||||||
|
def export_view(request, *args, **kwargs):
|
||||||
|
def make_table(queryset):
|
||||||
|
table = KaehmyExportTable(queryset,
|
||||||
|
request=request,
|
||||||
|
exclude=['id'],
|
||||||
|
attrs={'class': 'table table-bordered table-hover'})
|
||||||
|
|
||||||
|
table.paginate(page=request.GET.get('page', 1), per_page=9999)
|
||||||
|
table_html = convert_table_to_html(table, request)
|
||||||
|
return table_html
|
||||||
|
|
||||||
|
kaehmys = Application.objects.all()
|
||||||
|
non_board = filter(lambda q: not q.has_any_board_role(), kaehmys)
|
||||||
|
board = filter(lambda q: q.has_any_board_role(), kaehmys)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'non_board_table': make_table(non_board),
|
||||||
|
'board_table': make_table(board),
|
||||||
|
}
|
||||||
|
return render(request, 'export.html', context)
|
||||||
@@ -34,3 +34,4 @@ pyexcel==0.5.7
|
|||||||
pyexcel-xlsx==0.5.5
|
pyexcel-xlsx==0.5.5
|
||||||
django-import-export==0.7.0
|
django-import-export==0.7.0
|
||||||
openpyxl==2.4.11
|
openpyxl==2.4.11
|
||||||
|
django-app-namespace-template-loader==0.4.1
|
||||||
+6
-1
@@ -79,6 +79,7 @@ INSTALLED_APPS = [
|
|||||||
'members',
|
'members',
|
||||||
'infoscreen',
|
'infoscreen',
|
||||||
'coffee_scale',
|
'coffee_scale',
|
||||||
|
'kaehmy',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'django_nose',
|
'django_nose',
|
||||||
'bootstrap3',
|
'bootstrap3',
|
||||||
@@ -124,8 +125,12 @@ TEMPLATES = [
|
|||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': ['templates'],
|
'DIRS': ['templates'],
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
|
'loaders': [
|
||||||
|
'app_namespace.Loader',
|
||||||
|
'django.template.loaders.filesystem.Loader',
|
||||||
|
'django.template.loaders.app_directories.Loader',
|
||||||
|
],
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
'django.template.context_processors.debug',
|
'django.template.context_processors.debug',
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ urlpatterns = [
|
|||||||
url(r'^members/', include('members.urls')),
|
url(r'^members/', include('members.urls')),
|
||||||
url(r'^infoscreen/', include('infoscreen.urls')),
|
url(r'^infoscreen/', include('infoscreen.urls')),
|
||||||
url(r'^coffee/', include('coffee_scale.urls')),
|
url(r'^coffee/', include('coffee_scale.urls')),
|
||||||
|
url(r'^kaehmy/', include('kaehmy.urls')),
|
||||||
|
|
||||||
# admin
|
# admin
|
||||||
url(r'^admin/', admin.site.urls),
|
url(r'^admin/', admin.site.urls),
|
||||||
|
|||||||
@@ -26,11 +26,12 @@
|
|||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
$('[data-toggle="tooltip"]').tooltip()
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
|
console.log('pippeli')
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% endblock %}
|
{% endblock body %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+1
-8
@@ -3,9 +3,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from webapp.models import Official, Role
|
from webapp.models import Official, Role
|
||||||
from webapp.models import Feed, Tag, BaseFeed, Event, Registration
|
from webapp.models import Feed, Tag, BaseFeed, Event, Registration
|
||||||
from webapp.models import KaehmyForm, KaehmyMessage, OhlhafvChallenge
|
from webapp.models import OhlhafvChallenge
|
||||||
from webapp.models import CustomKaehmyRole, PresetKaehmyRole
|
|
||||||
from webapp.models import TelegramChannel
|
|
||||||
from modeltranslation.admin import TranslationAdmin
|
from modeltranslation.admin import TranslationAdmin
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
# this is needed so that the models get registered for translation
|
# this is needed so that the models get registered for translation
|
||||||
@@ -19,9 +17,4 @@ admin.site.register(Event, TranslationAdmin)
|
|||||||
admin.site.register(Registration, TranslationAdmin)
|
admin.site.register(Registration, TranslationAdmin)
|
||||||
admin.site.register(Official)
|
admin.site.register(Official)
|
||||||
admin.site.register(Role)
|
admin.site.register(Role)
|
||||||
admin.site.register(KaehmyForm)
|
|
||||||
admin.site.register(KaehmyMessage)
|
|
||||||
admin.site.register(CustomKaehmyRole)
|
|
||||||
admin.site.register(PresetKaehmyRole, TranslationAdmin)
|
|
||||||
admin.site.register(TelegramChannel)
|
|
||||||
admin.site.register(OhlhafvChallenge)
|
admin.site.register(OhlhafvChallenge)
|
||||||
|
|||||||
+2
-88
@@ -4,94 +4,8 @@ from django import forms
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from webapp.models import CustomKaehmyRole, PresetKaehmyRole, BaseRole
|
from webapp.models import BaseRole
|
||||||
from webapp.models import OhlhafvChallenge, KaehmyForm, KaehmyMessage
|
from webapp.models import OhlhafvChallenge
|
||||||
|
|
||||||
|
|
||||||
class KaehmyCheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple):
|
|
||||||
option_template_name = 'checkbox_option.html'
|
|
||||||
|
|
||||||
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
|
|
||||||
dic = super(KaehmyCheckboxSelectMultiple, self).create_option(name, value, label, selected, index, subindex, attrs)
|
|
||||||
description = PresetKaehmyRole.objects.get(id=value).description
|
|
||||||
dic['description'] = description
|
|
||||||
return dic
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(KaehmyCheckboxSelectMultiple, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class KaehmyForm_Form(forms.ModelForm):
|
|
||||||
"""Class representing Kaehmy form."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta for class KaehmyForm."""
|
|
||||||
|
|
||||||
model = KaehmyForm
|
|
||||||
fields = ['name', 'email', 'phone_number', 'year',
|
|
||||||
'preset_roles', 'custom_roles', 'custom_role_name',
|
|
||||||
'custom_role_is_board', 'text']
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(KaehmyForm_Form, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.fields["email"].label = _('Email (not public)')
|
|
||||||
self.fields["phone_number"].label = _('Phone number (not public)')
|
|
||||||
|
|
||||||
custom_roles_exist = CustomKaehmyRole.objects.all().exists()
|
|
||||||
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"].queryset = CustomKaehmyRole.objects.all()
|
|
||||||
|
|
||||||
for cat_id, category in BaseRole.CATEGORIES:
|
|
||||||
key = 'preset_roles_{}'.format(cat_id)
|
|
||||||
qset = PresetKaehmyRole.objects.filter(category=cat_id).order_by('category', '-is_board')
|
|
||||||
self.fields[key] = forms.ModelMultipleChoiceField(qset)
|
|
||||||
self.fields[key].widget = KaehmyCheckboxSelectMultiple(attrs={
|
|
||||||
'title': _('Preset roles'),
|
|
||||||
'name': 'preset_roles',
|
|
||||||
})
|
|
||||||
self.fields[key].help_text = ""
|
|
||||||
self.fields[key].queryset = qset
|
|
||||||
self.fields[key].label = _(category)
|
|
||||||
self.fields[key].required = False
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
cleaned_data = super(KaehmyForm_Form, self).clean()
|
|
||||||
print(cleaned_data)
|
|
||||||
for key in cleaned_data.keys():
|
|
||||||
if 'preset_roles_' in key:
|
|
||||||
cleaned_data['preset_roles'] = cleaned_data['preset_roles'] | cleaned_data[key]
|
|
||||||
|
|
||||||
print(cleaned_data)
|
|
||||||
return cleaned_data
|
|
||||||
|
|
||||||
def clean_phone_number(self):
|
|
||||||
"""Clean phone number field."""
|
|
||||||
number = self.cleaned_data.get('phone_number')
|
|
||||||
if number.isdigit():
|
|
||||||
return number
|
|
||||||
else:
|
|
||||||
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')
|
|
||||||
if not CustomKaehmyRole.objects.filter(name=custom_name).exists():
|
|
||||||
return custom_name
|
|
||||||
else:
|
|
||||||
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"]]
|
|
||||||
|
|
||||||
|
|
||||||
class KaehmyCommentForm(forms.ModelForm):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = KaehmyMessage
|
|
||||||
fields = ['name', 'email', 'message', 'parent']
|
|
||||||
|
|
||||||
|
|
||||||
class OhlhafvForm(forms.ModelForm):
|
class OhlhafvForm(forms.ModelForm):
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11 on 2018-01-25 22:31
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('webapp', '0037_auto_20180125_2131'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='TelegramChannel',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='CustomKaehmyRole',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='KaehmyForm',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='KaehmyMessage',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='MessageParent',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='PresetKaehmyRole',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -122,120 +122,6 @@ class PresetRole(BaseRole):
|
|||||||
description = models.TextField(_('Description'))
|
description = models.TextField(_('Description'))
|
||||||
|
|
||||||
|
|
||||||
class PresetKaehmyRole(PresetRole):
|
|
||||||
"""Model for kaehmy role."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Preset kaehmy role')
|
|
||||||
verbose_name_plural = _('Preset kaehmy roles')
|
|
||||||
|
|
||||||
|
|
||||||
class CustomKaehmyRole(BaseRole):
|
|
||||||
"""Model representing a user-specified custom occupation."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Custom kaehmy role')
|
|
||||||
verbose_name_plural = _('Custom kaehmy roles')
|
|
||||||
|
|
||||||
|
|
||||||
class MessageParent(models.Model):
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
class KaehmyMessage(MessageParent):
|
|
||||||
"""
|
|
||||||
Model representing a kaehmymessage.
|
|
||||||
|
|
||||||
Every message relates to certain kaehmyform or parent message.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Kaehmykommentti')
|
|
||||||
verbose_name_plural = _('Kaehmykommentit')
|
|
||||||
|
|
||||||
message = models.TextField(_('Message'))
|
|
||||||
parent = models.ForeignKey('MessageParent', related_name='messages')
|
|
||||||
|
|
||||||
|
|
||||||
class KaehmyForm(MessageParent):
|
|
||||||
"""
|
|
||||||
Model representing a form for kaehmy.
|
|
||||||
|
|
||||||
Allows user to choose from existing roles or to create custom ones.
|
|
||||||
"""
|
|
||||||
YEAR_CHOICES = (
|
|
||||||
(1, '1'),
|
|
||||||
(2, '2'),
|
|
||||||
(3, '3'),
|
|
||||||
(4, '4'),
|
|
||||||
(5, 'N'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
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)
|
|
||||||
custom_role_name = models.CharField(
|
|
||||||
_('Custom role name'), max_length=255, blank=True)
|
|
||||||
custom_role_is_board = models.BooleanField(
|
|
||||||
_('Board member'), blank=True)
|
|
||||||
custom_roles = models.ManyToManyField(
|
|
||||||
'CustomKaehmyRole', related_name='forms', blank=True)
|
|
||||||
preset_roles = models.ManyToManyField(
|
|
||||||
'PresetKaehmyRole', related_name='forms', blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""Return model info."""
|
|
||||||
return _('Kaehmy application: {}').format(self.name)
|
|
||||||
|
|
||||||
def comment_count(self):
|
|
||||||
"""Count comments for kaehmy."""
|
|
||||||
total = 0
|
|
||||||
|
|
||||||
def recurse(message):
|
|
||||||
count = 0
|
|
||||||
for msg in message.messages.all():
|
|
||||||
count += recurse(msg)
|
|
||||||
|
|
||||||
return count + 1
|
|
||||||
|
|
||||||
for message in self.messages.all():
|
|
||||||
total += recurse(message)
|
|
||||||
|
|
||||||
return total
|
|
||||||
|
|
||||||
def board_roles(self):
|
|
||||||
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 ''
|
|
||||||
|
|
||||||
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)]
|
|
||||||
combined = presets + customs
|
|
||||||
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 ''
|
|
||||||
|
|
||||||
def has_any_board_role(self):
|
|
||||||
return self.preset_roles.filter(is_board=True).exists() or self.custom_roles.filter(is_board=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Role(PresetRole):
|
class Role(PresetRole):
|
||||||
"""
|
"""
|
||||||
Model for Role.
|
Model for Role.
|
||||||
@@ -294,21 +180,6 @@ class OhlhafvChallenge(models.Model):
|
|||||||
return _('Ohlhafv challenge: {} vs. {}').format(self.challenger, self.victim)
|
return _('Ohlhafv challenge: {} vs. {}').format(self.challenger, self.victim)
|
||||||
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
|
|
||||||
auditlog.register(Tag)
|
auditlog.register(Tag)
|
||||||
auditlog.register(Feed)
|
auditlog.register(Feed)
|
||||||
auditlog.register(Event)
|
auditlog.register(Event)
|
||||||
|
|||||||
+1
-9
@@ -2,17 +2,9 @@ import django_tables2 as tables
|
|||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from webapp.models import OhlhafvChallenge, KaehmyForm
|
from webapp.models import OhlhafvChallenge
|
||||||
|
|
||||||
|
|
||||||
class OhlhafvTable(tables.Table):
|
class OhlhafvTable(tables.Table):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = OhlhafvChallenge
|
model = OhlhafvChallenge
|
||||||
|
|
||||||
|
|
||||||
class KaehmyExportTable(tables.Table):
|
|
||||||
class Meta:
|
|
||||||
model = KaehmyForm
|
|
||||||
exclude = ['text', 'messageparent_ptr', 'custom_role_name', 'custom_role_is_board', 'timestamp']
|
|
||||||
|
|
||||||
all_roles = tables.Column(verbose_name=_('Roles'), orderable=False)
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{% extends "ohlhafv/base.html" %}
|
{% extends ":base.html" %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block navigation %}
|
{% block navigation %}
|
||||||
{% include "ohlhafv/navigation.html" %}
|
{% include ":navigation.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from modeltranslation.translator import register, TranslationOptions
|
from modeltranslation.translator import register, TranslationOptions
|
||||||
from webapp.models import BaseFeed, Feed, Tag, Event, Registration
|
from webapp.models import BaseFeed, Feed, Tag, Event, Registration
|
||||||
from webapp.models import PresetRole, BaseRole, PresetKaehmyRole
|
from webapp.models import PresetRole, BaseRole
|
||||||
|
|
||||||
|
|
||||||
@register(BaseFeed)
|
@register(BaseFeed)
|
||||||
@@ -52,10 +52,3 @@ class PresetRoleTranslationOptions(TranslationOptions):
|
|||||||
"""Class for PresetRole translation options."""
|
"""Class for PresetRole translation options."""
|
||||||
|
|
||||||
fields = ('description',)
|
fields = ('description',)
|
||||||
|
|
||||||
|
|
||||||
@register(PresetKaehmyRole)
|
|
||||||
class PresetKaehmyRoleTranslationOptions(TranslationOptions):
|
|
||||||
""" Class for PresetKaehmyRole translation options"""
|
|
||||||
|
|
||||||
fields = ()
|
|
||||||
|
|||||||
@@ -17,12 +17,6 @@ from webapp.views import event_calendar_view
|
|||||||
from webapp.views import international_view
|
from webapp.views import international_view
|
||||||
from webapp.views import sosso_view
|
from webapp.views import sosso_view
|
||||||
from webapp.views import contact_view
|
from webapp.views import contact_view
|
||||||
from webapp.views import kaehmy_view
|
|
||||||
from webapp.views import kaehmy_list_view
|
|
||||||
from webapp.views import kaehmy_submit
|
|
||||||
from webapp.views import kaehmy_comment
|
|
||||||
from webapp.views import kaehmy_statistics_view
|
|
||||||
from webapp.views import kaehmy_export_view
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# main
|
# main
|
||||||
@@ -46,14 +40,6 @@ urlpatterns = [
|
|||||||
# corporate
|
# corporate
|
||||||
url(r'^jobs', jobs_view),
|
url(r'^jobs', jobs_view),
|
||||||
|
|
||||||
# kaehmy
|
|
||||||
url(r'^kaehmy/new', kaehmy_view),
|
|
||||||
url(r'^kaehmy/submit', kaehmy_submit),
|
|
||||||
url(r'^kaehmy/add_comment', kaehmy_comment),
|
|
||||||
url(r'^kaehmy/statistics', kaehmy_statistics_view),
|
|
||||||
url(r'^kaehmy/export', kaehmy_export_view),
|
|
||||||
url(r'^kaehmy', kaehmy_list_view),
|
|
||||||
|
|
||||||
# ohlhafv
|
# ohlhafv
|
||||||
url(r'^ohlhafv/submit', ohlhafv_submit),
|
url(r'^ohlhafv/submit', ohlhafv_submit),
|
||||||
url(r'^ohlhafv/list', ohlhafv_list),
|
url(r'^ohlhafv/list', ohlhafv_list),
|
||||||
|
|||||||
+3
-162
@@ -13,10 +13,9 @@ import logging
|
|||||||
import requests
|
import requests
|
||||||
from dealer.git import git
|
from dealer.git import git
|
||||||
|
|
||||||
from webapp.models import PresetKaehmyRole, CustomKaehmyRole, Official
|
from webapp.models import Official, OhlhafvChallenge
|
||||||
from webapp.models import OhlhafvChallenge, KaehmyForm, TelegramChannel
|
from webapp.forms import OhlhafvForm
|
||||||
from webapp.forms import OhlhafvForm, KaehmyForm_Form, KaehmyCommentForm
|
from webapp.tables import OhlhafvTable
|
||||||
from webapp.tables import OhlhafvTable, KaehmyExportTable
|
|
||||||
from members.views.utils import *
|
from members.views.utils import *
|
||||||
|
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
@@ -172,161 +171,3 @@ def ohlhafv_list(request, *args, **kwargs):
|
|||||||
'challenge_count': len(challenges),
|
'challenge_count': len(challenges),
|
||||||
}
|
}
|
||||||
return render(request, 'ohlhafv/list.html', context)
|
return render(request, 'ohlhafv/list.html', context)
|
||||||
|
|
||||||
|
|
||||||
@ensure_csrf_cookie
|
|
||||||
@require_http_methods(["GET"])
|
|
||||||
def kaehmy_list_view(request, *args, **kwargs):
|
|
||||||
"""Kaehmy application list"""
|
|
||||||
|
|
||||||
role_filter = request.GET.get('role', None)
|
|
||||||
if role_filter is not None and str(role_filter) != '-1':
|
|
||||||
applications = KaehmyForm.objects.filter(custom_roles__id=role_filter) | KaehmyForm.objects.filter(preset_roles__id=role_filter)
|
|
||||||
else:
|
|
||||||
applications = KaehmyForm.objects.all()
|
|
||||||
|
|
||||||
applications = applications.order_by('-timestamp')
|
|
||||||
filter_options_preset = PresetKaehmyRole.objects.annotate(form_count=Count('forms')).filter(form_count__gt=0)
|
|
||||||
filter_options_preset_list = [(r.id, r.name, r.form_count) for r in filter_options_preset]
|
|
||||||
filter_options_custom = CustomKaehmyRole.objects.annotate(form_count=Count('forms')).filter(form_count__gt=0)
|
|
||||||
filter_options_custom_list = [(r.id, r.name, r.form_count) for r in filter_options_custom]
|
|
||||||
|
|
||||||
filter_options = filter_options_preset_list + filter_options_custom_list
|
|
||||||
filter_options.sort(key=lambda f: f[1])
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'applications': applications,
|
|
||||||
'application_count': len(applications),
|
|
||||||
'filter_options': filter_options
|
|
||||||
}
|
|
||||||
return render(request, 'kaehmy/list.html', context)
|
|
||||||
|
|
||||||
|
|
||||||
@ensure_csrf_cookie
|
|
||||||
@require_http_methods(["POST"])
|
|
||||||
def kaehmy_comment(request, *args, **kwargs):
|
|
||||||
"""POST endpoint for commenting"""
|
|
||||||
|
|
||||||
form = KaehmyCommentForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
comment = form.save()
|
|
||||||
email = comment.parent.email
|
|
||||||
name = comment.name
|
|
||||||
|
|
||||||
subject = 'Kaehmyysi tai kommenttiisi on vastattu!'
|
|
||||||
body = ('{} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.\r\n\r\n'
|
|
||||||
'Käy lukemassa viesti osoitteessa http://sika.sahkoinsinoorikilta.fi/kaehmy').format(name.capitalize())
|
|
||||||
|
|
||||||
send_email(email, subject, body)
|
|
||||||
logging.debug(
|
|
||||||
'Sent kaehmy comment email to recipient <{}>'.format(email))
|
|
||||||
|
|
||||||
return redirect('/kaehmy')
|
|
||||||
else:
|
|
||||||
print(form)
|
|
||||||
context = {
|
|
||||||
'error': form.errors
|
|
||||||
}
|
|
||||||
return render(request, 'kaehmy/error.html', context)
|
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
|
||||||
def kaehmy_statistics_view(request, *args, **kwargs):
|
|
||||||
"""Kaehmys roles listed by applications"""
|
|
||||||
|
|
||||||
applications = KaehmyForm.objects.all()
|
|
||||||
role_list = []
|
|
||||||
|
|
||||||
preset_roles = PresetKaehmyRole.objects.all()
|
|
||||||
custom_roles = CustomKaehmyRole.objects.all()
|
|
||||||
|
|
||||||
for preset in preset_roles:
|
|
||||||
people = [form.name for form in preset.forms.all()]
|
|
||||||
role_list.append((preset.name, len(people), ', '.join(people)))
|
|
||||||
for custom in custom_roles:
|
|
||||||
people = [form.name for form in custom.forms.all()]
|
|
||||||
role_list.append((custom.name, len(people), ', '.join(people)))
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'applications': applications,
|
|
||||||
'application_count': len(applications),
|
|
||||||
'role_list': role_list
|
|
||||||
}
|
|
||||||
return render(request, 'kaehmy/statistics.html', context)
|
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
|
||||||
def kaehmy_view(request, *args, **kwargs):
|
|
||||||
"""Render Kaehmy form page."""
|
|
||||||
form = KaehmyForm_Form()
|
|
||||||
return render(request, 'kaehmy/kaehmy.html', {'form': form})
|
|
||||||
|
|
||||||
|
|
||||||
@ensure_csrf_cookie
|
|
||||||
@require_http_methods(["POST"])
|
|
||||||
def kaehmy_submit(request, *args, **kwargs):
|
|
||||||
"""Submit Kaehmy form."""
|
|
||||||
form = KaehmyForm_Form(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
application = form.save()
|
|
||||||
custom_name = form.cleaned_data.get('custom_role_name')
|
|
||||||
custom_is_board = form.cleaned_data.get('custom_role_is_board')
|
|
||||||
|
|
||||||
if len(custom_name) > 0:
|
|
||||||
custom_role = CustomKaehmyRole(
|
|
||||||
name=custom_name, is_board=custom_is_board)
|
|
||||||
custom_role.save()
|
|
||||||
application.custom_roles.add(custom_role)
|
|
||||||
|
|
||||||
url = 'https://sika.sahkoinsinoorikilta.fi/kaehmy'
|
|
||||||
|
|
||||||
email = form.cleaned_data.get('email', '')
|
|
||||||
name = form.cleaned_data.get('name', 'Anonymous')
|
|
||||||
subject = 'Arwokas kirjattu kirje mahdolliselle tulewalle kiltahenkilölle'
|
|
||||||
body = ('Moikka {}!\r\n\r\nHienoa, että kilta kiinnostaa! Kaehmysi on vastaanotettu.\r\n'
|
|
||||||
'Mahdollisista kommenteista tulee ilmoitus sähköpostitse.\r\n\r\n'
|
|
||||||
'Käy katsomassa kaehmytilanne osoitteessa {}').format(name, url)
|
|
||||||
|
|
||||||
send_email(email, subject, body)
|
|
||||||
logging.debug('Sent kaehmy email to recipient <{}>'.format(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 kaehmy/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)))
|
|
||||||
|
|
||||||
else:
|
|
||||||
context = {
|
|
||||||
'error': form.errors
|
|
||||||
}
|
|
||||||
return render(request, 'kaehmy/error.html', context)
|
|
||||||
return HttpResponseRedirect('/kaehmy')
|
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(['GET'])
|
|
||||||
def kaehmy_export_view(request, *args, **kwargs):
|
|
||||||
def make_table(queryset):
|
|
||||||
table = KaehmyExportTable(queryset,
|
|
||||||
request=request,
|
|
||||||
exclude=['id'],
|
|
||||||
attrs={'class': 'table table-bordered table-hover'})
|
|
||||||
|
|
||||||
table.paginate(page=request.GET.get('page', 1), per_page=9999)
|
|
||||||
table_html = convert_table_to_html(table, request)
|
|
||||||
return table_html
|
|
||||||
|
|
||||||
kaehmys = KaehmyForm.objects.all()
|
|
||||||
non_board = filter(lambda q: not q.has_any_board_role(), kaehmys)
|
|
||||||
board = filter(lambda q: q.has_any_board_role(), kaehmys)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'non_board_table': make_table(non_board),
|
|
||||||
'board_table': make_table(board),
|
|
||||||
}
|
|
||||||
return render(request, 'kaehmy/export.html', context)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user