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)
+28
View File
@@ -0,0 +1,28 @@
{% extends "project.html" %}
{% load static %}
{% load i18n %}
{% block body %}
{% block header %}
<div class="kaehmy_header">
{% include ":header.html" %}
</div>
{% endblock header %}
{% block navigation %}
{% include ":navigation.html" %}
{% endblock %}
<div class="kaehmy-content">
{% block content %}
{% endblock %}
</div>
<div class="footer">
{% block footer %}
{% include ":footer.html" %}
{% endblock footer %}
</div>
{% endblock body %}
+5
View File
@@ -0,0 +1,5 @@
{% if wrap_label %}
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}
{% include "django/forms/widgets/input.html" %}
{% if wrap_label %} {{ widget.label }}</label>{% endif %}
<span class="fa fa-info-circle" data-toggle="tooltip" data-placement="right" title="{{ widget.description }}"></span>
+18
View File
@@ -0,0 +1,18 @@
{% extends "kaehmy:base.html" %}
{% load static %}
{% load i18n %}
{% block content %}
<div>
<div>
<h3>{% trans "Error" %}</h3>
</div>
<div class="alert alert-danger">
{{ error|safe }}
</div>
<div>
<button onclick="window.history.back();" class="btn btn-primary">{% trans "Back" %}</button>
</div>
</div>
{% endblock content %}
+26
View File
@@ -0,0 +1,26 @@
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block content %}
<div>
<div>
<h2 style="padding-top: 1rem">{% trans "All applications" %}</h2>
</div>
<div>
<h4>{% trans "Board applications" %}</h4>
{{ board_table|safe }}
</div>
<div>
<h4>{% trans "Non-board applications" %}</h4>
{{ non_board_table|safe }}
</div>
<div>
<a href="/kaehmy" class="btn btn-primary">{% trans "Front page" %}</a>
</div>
</div>
{% endblock content %}
+25
View File
@@ -0,0 +1,25 @@
{% load i18n %}
{% load static %}
{% load staticfiles %}
<link rel="stylesheet" href="/static/css/kaehmy_footer.css">
<footer style="text-align: center">
<div>
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
<span>
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</span>
</form>
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
</div>
</footer>
+8
View File
@@ -0,0 +1,8 @@
{% load i18n %}
<link rel="stylesheet" href="/static/css/kaehmy_header.css">
<div class="kaehmy_header-content">
<div class="kaehmy-banner logo">
<a href="/kaehmy"><img class="kaehmy-banner-image" src="/static/img/kaehmy_banner.png" alt="Aalto-yliopiston Sähköinsinöörikilta ry"></a>
</div>
</div>
+83
View File
@@ -0,0 +1,83 @@
{% extends "kaehmy:base.html" %}
{% load bootstrap3 %}
{% load i18n %}
{% block navigation %}
{% include "kaehmy:navigation.html" %}
{% endblock %}
{% block content %}
<div>
<h2 style="padding-top: 1rem">{% trans "Kaehmylomake" %}</h2>
<div id="input_form">
<p>
{% blocktrans %}Kaehmykoneella voit ilmaista kiinnostuksesi toimia killassa ensi vuonna.
Listassa on vastuualueittain sekä hallitus- että toimihenkilövirkoja.
Koska lista ei ole koskaan täydellinen, voit myös ehdottaa ihan uutta toimenkuvaa.
Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa konsultoida <a href="/static/other/kahmyopas.pdf">kaehmyopasta</a>
tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan henkilöön.{% endblocktrans %}
</p>
<p>
{% blocktrans %}Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus
eikä siis missään nimessä sitova ilmoittautumien mihinkään tehtävään!{% endblocktrans %}
</p>
<h5>{% trans "Päivämääriä & deadlineja" %}</h5>
<ul>
<li><strong>1.11.</strong> {% blocktrans %}Hallitustyrkkypaneeli (haku hallitukseen olisi hyvä tehdä ennen tätä!){% endblocktrans %}</li>
<li><strong>9.11.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta){% endblocktrans %}</li>
<li><strong>15.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen valinta){% endblocktrans %}</li>
<li><strong>20.11.</strong> {% blocktrans %}Kiltailta{% endblocktrans %}</li>
<li><strong>25.11.</strong> {% blocktrans %}Haku toimariksi olisi hyvä tehdä ennen tätä!{% endblocktrans %}</li>
<li><strong>30.11.</strong> {% blocktrans %}Vaalikokous, osa 3 (toimarien valinta){% endblocktrans %}</li>
</ul>
<form name="kaehmyForm" action="/kaehmy/submit/" method="post" class="form">{% csrf_token %}
{% bootstrap_field form.name %}
{% bootstrap_field form.email %}
{% bootstrap_field form.phone_number %}
{% bootstrap_field form.year %}
<h4>{% trans "Preset roles" %}</h4>
{% for preset_field in form %}
{% if "preset_roles_" in preset_field.name %}
<div class="card">
<div class="card-header">
{{ preset_field.label }}
</div>
<div class="card-block">
{% bootstrap_field preset_field show_label=False %}
</div>
</div>
{% endif %}
{% endfor %}
{% for custom_field in form %}
{% if custom_field.name == 'custom_roles' %}
<div class="card">
<div class="card-header">
{{ custom_field.label }}
</div>
<div class="card-block">
{% bootstrap_field custom_field show_label=False %}
</div>
</div>
{% endif %}
{% endfor %}
<div class="card">
<div class="card-block">
{% bootstrap_field form.custom_role_name %}
{% bootstrap_field form.custom_role_is_board %}
</div>
</div>
{% bootstrap_field form.text %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% trans "Submit" %}
</button>
{% endbuttons %}
</form>
</div>
</div>
{% endblock content %}
+108
View File
@@ -0,0 +1,108 @@
{% extends "kaehmy:base.html" %}
{% load static %}
{% load i18n %}
{% block navigation %}
{% include "kaehmy:navigation.html" %}
{% endblock %}
{% block content %}
<script>
function commentOn(id, op) {
setTimeout(function() {
document.getElementById("commentNameField").focus();
}, 50);
document.getElementById("collapse_add_comment").scrollIntoView();
document.getElementById("commentOP").innerHTML = op;
document.getElementById("commentId").value = id;
}
</script>
<div>
<div>
<h2 style="padding-top: 1rem">{% trans "All kaehmys" %}</h2>
</div>
<div class="collapse" id="collapse_add_comment">
<div class="card">
<div class="card-block">
<form method="POST" action="/kaehmy/add_comment" class="form">{% csrf_token %}
<div class="form-group">
{% trans "Commenting on post by " %} <span id="commentOP"></span>
<input type="hidden" name="parent" id="commentId">
</div>
<div class="form-group">
<label>{% trans "Name" %}</label>
<input id="commentNameField" name="name" type="text" class="form-control" placeholder="Teemu Teekkari">
</div>
<div class="form-group">
<label>{% trans "Email" %}</label>
<input name="email" type="email" class="form-control" placeholder="teemu@teekka.ri">
</div>
<div class="form-group">
<label>{% trans "Comment" %}</label>
<textarea name="message" class="form-control" rows=3 placeholder="Hei!"></textarea>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value={% trans "Send" %}>
</div>
</form>
</div>
</div>
</div>
<div>
<form class="role-filter-form form-inline" method="GET">
<label><strong>{% trans "Filter kaehmys" %}:</strong></label>
<select onchange="this.form.submit()" name="role" class="form-control">
<option value="-1">{% trans "All kaehmys" %}</option>
{% for option in filter_options %}
<option value={{ option.0 }} {% if request.GET.role|slugify == option.0|slugify %} selected="selected"{% endif %}>
{{ option.1 }} ({{ option.2 }})
</option>
{% endfor %}
</select>
</form>
</div>
<div>
<h6 style="padding-bottom: 1rem">{% trans "Total kaehmys:" %} {{ application_count }}</h6>
</div>
{% for application in applications %}
<div class="card">
<h4 class="card-header">{{ application.name }}</h4>
<div class="card-block">
{% if application.board_roles|length > 0 %}
<h5 style="padding-bottom: 1rem" class="card-subtitle mb-2 text-muted">{{ application.board_roles }}</h5>
{% endif %}
{% if application.official_roles|length > 0 %}
<h5 style="padding-bottom: 1rem" class="card-subtitle mb-2 text-muted">{{ application.official_roles }}</h5>
{% endif %}
<p class="card-text">{{ application.text|linebreaks|urlize }}</p>
{% if application.comment_count > 0 %}
<a type="button" style="cursor: pointer" data-toggle="collapse" data-target="#collapse_{{ application.id }}" aria-expanded="false" aria-controls="collapse_{{ application.id }}">
{% trans "Show comments" %} ({{ application.comment_count }})
</a>
{% endif %}
<div>
<a type="button" onclick="commentOn({{ application.id }}, '{{ application.name }}')" style="cursor: pointer" data-toggle="collapse" data-target="#collapse_add_comment" aria-expanded="false" aria-controls="collapse_add_comment">
{% trans "Add comment" %}
</a>
</div>
<div class="collapse" id="collapse_{{ application.id }}">
{% for message in application.messages.all %}
{% include "kaehmy:message.html" with messages=message.messages.all %}
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
+21
View File
@@ -0,0 +1,21 @@
{% load i18n %}
<div class="card" style="margin-top: 0.5rem; margin-bottom: 0">
<div class="card-block">
<h4>{{ message.name }}</h4>
<p>{{ message.message|linebreaks|urlize }}</p>
<h6 class="card-subtitle mb-2 text-muted">{{ message.timestamp }}</h6>
<div>
<a type="button" onclick="commentOn({{ message.id }}, '{{ message.name }}')" style="cursor: pointer" data-toggle="collapse" data-target="#collapse_add_comment" aria-expanded="false" aria-controls="collapse_add_comment">
{% trans "Reply" %}
</a>
</div>
<div>
{% for message in messages %}
{% include "message.html" with messages=message.messages.all %}
{% endfor %}
</div>
</div>
</div>
+12
View File
@@ -0,0 +1,12 @@
{% load i18n %}
<link rel="stylesheet" href="/static/css/kaehmy_nav.css">
<div class="kaehmy_navigation">
<nav class="navbar-border navbar navbar-toggleable-md navbar-light bg-faded">
<div class="navbar-nav">
<a class="nav-item nav-link" href="/kaehmy">{% trans "List kaehmys" %}</a>
<a class="nav-item nav-link" href="/kaehmy/new">{% trans "New kaehmy" %} <span class="sr-only">(current)</span></a>
<a class="nav-item nav-link" href="/kaehmy/statistics">{% trans "Statistics" %}</a>
</div>
</nav>
</div>
+30
View File
@@ -0,0 +1,30 @@
{% extends "kaehmy:base.html" %}
{% load bootstrap3 %}
{% load i18n %}
{% block navigation %}
{% include "kaehmy:navigation.html" %}
{% endblock %}
{% block content %}
<div>
<div>
<h2 style="padding-top: 1rem">{% trans "Statistics" %}</h2>
</div>
<div style="margin-top: 1rem" class="card">
<div class="card-header">
<h5>{% trans "Total kaehmys:" %} {{ application_count }}</h5>
</div>
<div class="card-block">
{% for role in role_list %}
<div>
<p>{{ role.0 }} <strong data-toggle="tooltip" data-placement="right" title="{{ role.2 }}">({{ role.1 }})</strong></p>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock 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)