Move and rename a lot of stuff into kaehmy app

This commit is contained in:
Jan Tuomi
2018-01-26 01:15:35 +02:00
parent 06c2a2b9a6
commit f0ea3505e4
36 changed files with 640 additions and 439 deletions
View File
+10
View File
@@ -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)
+5
View File
@@ -0,0 +1,5 @@
from django.apps import AppConfig
class KaehmyConfig(AppConfig):
name = 'kaehmy'
+89
View File
@@ -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']
+93
View File
@@ -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',),
),
]
View File
+147
View File
@@ -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)
+13
View File
@@ -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 %}
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+11
View File
@@ -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 = ()
+21
View File
@@ -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
View File
@@ -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)
+2 -1
View File
@@ -33,4 +33,5 @@ django-password-reset==1.0
pyexcel==0.5.7 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
View File
@@ -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',
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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',
),
]
-129
View File
@@ -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
View File
@@ -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)
+2 -2
View File
@@ -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 %}
+1 -8
View File
@@ -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 = ()
-14
View File
@@ -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),
+4 -163
View File
@@ -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
@@ -171,162 +170,4 @@ def ohlhafv_list(request, *args, **kwargs):
'challenges': challenges, 'challenges': challenges,
'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)