Merge pull request #48 from makes-hacks-hip/35-orga-view-devices
35 orga view devices
This commit is contained in:
commit
ab0fb93409
33 changed files with 854 additions and 59 deletions
|
|
@ -21,7 +21,9 @@ Als bekannter Gast der ein defekten Geräte anmelden möchte,
|
|||
- Die nächste Seite (S5) bestätigt mir die Anmeldung meines Gerätes.
|
||||
- In meinem eMail Posteingang finde ich ebenfalls eine Bestätigung der Geräteanmeldung.
|
||||
|
||||
## Organisator: Neues Gerät - akzeptieren für ausgewähltes Repair-Café
|
||||
## Organisator: Neues Gerät
|
||||
|
||||
### Variante: Akzeptieren für ausgewähltes Repair-Café
|
||||
|
||||
Als Organisator habe ich eine eMail bekommen dass ein neues Gerät angemeldet wurde. Ich möchte dieses Gerät nun ansehen un die Eingangsüberprüfung durchführen.
|
||||
- Ich öffne die RepApp im Browser
|
||||
|
|
@ -31,7 +33,7 @@ Als Organisator habe ich eine eMail bekommen dass ein neues Gerät angemeldet wu
|
|||
- Ich klicke auf den Knopf "Details" für das neue Gerät, und mein Browser öffnet eine Seite in der ich die Details zu dem Gerät sehe. Auf der Seite sind Knöpfe: `Annehmen für gewähltes Repair-Café`, `Annehmen für anderes Repair-Café`, `Ablehnen` und `Rückfrage`. Alle notwendigen Informationen sind vorhanden und ich entscheide mich das Gerät für das ausgewählte Repair-Café zu akzeptieren. Der Klick auf `Annehmen für gewähltes Repair-Café` bringt mich zurück zum Hauptmenü.
|
||||
- In der Liste der neuen Geräte ist das gerade bearbeitete Gerät nicht mehr sichtbar.
|
||||
|
||||
## Organisator: Neues Gerät - akzeptieren für späteres Repair-Café
|
||||
### Variante: Akzeptieren für späteres Repair-Café
|
||||
|
||||
Als Organisator habe ich eine eMail bekommen dass ein neues Gerät angemeldet wurde. Ich möchte dieses Gerät nun ansehen un die Eingangsüberprüfung durchführen.
|
||||
- Ich öffne die RepApp im Browser
|
||||
|
|
@ -41,7 +43,7 @@ Als Organisator habe ich eine eMail bekommen dass ein neues Gerät angemeldet wu
|
|||
- Ich klicke auf den Knopf "Details" für das neue Gerät, und mein Browser öffnet eine Seite in der ich die Details zu dem Gerät sehe. Auf der Seite sind Knöpfe: `Annehmen für gewähltes Repair-Café`, `Annehmen für anderes Repair-Café`, `Ablehnen` und `Rückfrage`. Alle notwendigen Informationen sind vorhanden, aber da ich weiß dass im nächsten Repair-Café kein geeigneter Reparateur verfügbar ist entscheide ich mich das Gerät für ein späteres Repair-Café zu akzeptieren. Der Klick auf `Annehmen für anderes Repair-Café` bringt mich zurück zum Hauptmenü.
|
||||
- In der Liste der neuen Geräte ist das gerade bearbeitete Gerät nicht mehr sichtbar.
|
||||
|
||||
## Organisator: Neues Gerät - ablehnen
|
||||
### Variante: Ablehnen
|
||||
|
||||
Als Organisator habe ich eine eMail bekommen dass ein neues Gerät angemeldet wurde. Ich möchte dieses Gerät nun ansehen un die Eingangsüberprüfung durchführen.
|
||||
- Ich öffne die RepApp im Browser
|
||||
|
|
@ -52,7 +54,7 @@ Als Organisator habe ich eine eMail bekommen dass ein neues Gerät angemeldet wu
|
|||
- Auf der neuen Seite sehe ich ein Formular mit einem Textbereich der die Überschrift "Antwort an den Gast" trägt. Im Formular ist bereits ein Standard-Text zur Ablehnung eines Geräts. Ich passe den Text an um dem Gast mitzuteilen dass wir leider keinen geeigneten Reparateur haben und klicke auf den Knopf `Absenden`. Der Klick bringt mich zurück zum Hauptmenü.
|
||||
- In der Liste der neuen Geräte ist das gerade bearbeitete Gerät nicht mehr sichtbar.
|
||||
|
||||
## Organisator: Neues Gerät - Rückfrage
|
||||
### Variante: Rückfrage
|
||||
|
||||
Als Organisator habe ich eine eMail bekommen dass ein neues Gerät angemeldet wurde. Ich möchte dieses Gerät nun ansehen un die Eingangsüberprüfung durchführen.
|
||||
- Ich öffne die RepApp im Browser
|
||||
|
|
|
|||
|
|
@ -53,10 +53,13 @@ INSTALLED_APPS = [
|
|||
"crispy_forms",
|
||||
"crispy_bootstrap5",
|
||||
"import_export",
|
||||
"ckeditor",
|
||||
"ckeditor_uploader",
|
||||
]
|
||||
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||
CKEDITOR_UPLOAD_PATH = "uploads/"
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
|
|
@ -166,7 +169,7 @@ AUTHENTICATION_BACKENDS = [
|
|||
"repapp.backends.OneTimeLoginBackend",
|
||||
]
|
||||
|
||||
LOGIN_REDIRECT_URL = "/guest/profile/"
|
||||
LOGIN_REDIRECT_URL = "/guest/"
|
||||
|
||||
OIDC_RP_CLIENT_ID = "repapp"
|
||||
OIDC_RP_CLIENT_SECRET = os.getenv("DJANGO_OIDC_RP_CLIENT_SECRET", None)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ from django.conf.urls.static import static
|
|||
urlpatterns = [
|
||||
path("", include("repapp.urls")),
|
||||
path('oidc/', include('mozilla_django_oidc.urls')),
|
||||
path('accounts/', include('django.contrib.auth.urls')),
|
||||
# user management for guests not needed, use one-time-login only
|
||||
# path('accounts/', include('django.contrib.auth.urls')),
|
||||
path('ckeditor/', include('ckeditor_uploader.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ Django forms for RepApp.
|
|||
"""
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from crispy_forms.layout import Field
|
||||
from ckeditor.widgets import CKEditorWidget
|
||||
from .models import Question, Device
|
||||
|
||||
|
||||
EMPTY_VALUES = (None, '')
|
||||
|
|
@ -16,7 +17,7 @@ class HoneypotWidget(forms.TextInput):
|
|||
"""
|
||||
is_hidden = True
|
||||
|
||||
def __init__(self, attrs=None, html_comment=False, *args, **kwargs):
|
||||
def __init__(self, *args, attrs=None, html_comment=False, **kwargs):
|
||||
self.html_comment = html_comment
|
||||
super(HoneypotWidget, self).__init__(attrs, *args, **kwargs)
|
||||
if 'class' not in self.attrs:
|
||||
|
|
@ -113,3 +114,18 @@ class RegisterGuest(forms.Form):
|
|||
max_length=200
|
||||
)
|
||||
accept_agb = HoneypotField(label="")
|
||||
|
||||
|
||||
class Mail(forms.Form):
|
||||
"""
|
||||
Form for rejecting a device.
|
||||
"""
|
||||
message = forms.CharField(widget=CKEditorWidget(), label="")
|
||||
|
||||
|
||||
class UpdateDeviceForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Question
|
||||
fields = ['question']
|
||||
question = forms.CharField(widget=CKEditorWidget(
|
||||
attrs={'style': 'width:100%;'}), label="Frage")
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ def process_message(message: MailMessage, guest: Guest):
|
|||
if question:
|
||||
if question.device.guest.mail == message.from_:
|
||||
question.answer = content
|
||||
question.answered = True
|
||||
question.save()
|
||||
# TODO: save attachments and add to message
|
||||
logger.info(
|
||||
|
|
|
|||
18
rc_hip/repapp/migrations/0005_device_status.py
Normal file
18
rc_hip/repapp/migrations/0005_device_status.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-05-29 10:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('repapp', '0004_alter_onetimelogin_secret'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='status',
|
||||
field=models.IntegerField(default=0, verbose_name='Status'),
|
||||
),
|
||||
]
|
||||
18
rc_hip/repapp/migrations/0006_alter_question_answer.py
Normal file
18
rc_hip/repapp/migrations/0006_alter_question_answer.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-05-29 12:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('repapp', '0005_device_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='question',
|
||||
name='answer',
|
||||
field=models.TextField(null=True, verbose_name='Antwort'),
|
||||
),
|
||||
]
|
||||
18
rc_hip/repapp/migrations/0007_question_open.py
Normal file
18
rc_hip/repapp/migrations/0007_question_open.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-05-29 12:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('repapp', '0006_alter_question_answer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='question',
|
||||
name='open',
|
||||
field=models.BooleanField(default=True, verbose_name='offen'),
|
||||
),
|
||||
]
|
||||
18
rc_hip/repapp/migrations/0008_question_sent.py
Normal file
18
rc_hip/repapp/migrations/0008_question_sent.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-05-29 14:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('repapp', '0007_question_open'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='question',
|
||||
name='sent',
|
||||
field=models.BooleanField(default=False, verbose_name='gesendet'),
|
||||
),
|
||||
]
|
||||
18
rc_hip/repapp/migrations/0009_question_answered.py
Normal file
18
rc_hip/repapp/migrations/0009_question_answered.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-05-29 14:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('repapp', '0008_question_sent'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='question',
|
||||
name='answered',
|
||||
field=models.BooleanField(default=False, verbose_name='beantwortet'),
|
||||
),
|
||||
]
|
||||
|
|
@ -113,6 +113,7 @@ class Device(models.Model):
|
|||
type_plate_picture = models.FileField(
|
||||
upload_to=device_directory_path, null=True, verbose_name=_("Foto vom Typenschild"))
|
||||
confirmed = models.BooleanField(verbose_name=_("Bestätigung gesendet?"))
|
||||
status = models.IntegerField(verbose_name=_("Status"), default=0)
|
||||
guest = models.ForeignKey(
|
||||
Guest, on_delete=models.CASCADE, null=True, verbose_name=_("Gast"))
|
||||
cafe = models.ForeignKey(
|
||||
|
|
@ -123,7 +124,7 @@ class Device(models.Model):
|
|||
verbose_name_plural = _('Geräte')
|
||||
|
||||
def __str__(self):
|
||||
return f'Gerät {self.device}'
|
||||
return f'Gerät {self.device} - {self.manufacturer}'
|
||||
|
||||
|
||||
class Appointment(models.Model):
|
||||
|
|
@ -153,7 +154,7 @@ class Question(models.Model):
|
|||
A Question is a request for information form a Organisator or a Reparateur for a Device.
|
||||
"""
|
||||
question = models.TextField(verbose_name=_("Frage"))
|
||||
answer = models.TextField(verbose_name=_("Antwort"))
|
||||
answer = models.TextField(verbose_name=_("Antwort"), null=True)
|
||||
date = models.DateField(verbose_name=_(
|
||||
"Erstellungsdatum"), default=django.utils.timezone.now)
|
||||
organisator = models.ForeignKey(
|
||||
|
|
@ -162,6 +163,10 @@ class Question(models.Model):
|
|||
Reparateur, on_delete=models.CASCADE, null=True, verbose_name=_("Reparateur"))
|
||||
device = models.ForeignKey(
|
||||
Device, on_delete=models.CASCADE, verbose_name=_("Gerät"))
|
||||
open = models.BooleanField(verbose_name=_("offen"), default=True)
|
||||
sent = models.BooleanField(verbose_name=_("gesendet"), default=False)
|
||||
answered = models.BooleanField(
|
||||
verbose_name=_("beantwortet"), default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Frage')
|
||||
|
|
|
|||
3
rc_hip/repapp/static/repapp/css/repapp.css
Normal file
3
rc_hip/repapp/static/repapp/css/repapp.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.django-ckeditor-widget {
|
||||
display: block !important
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
{% endblock title %}
|
||||
</title>
|
||||
<link rel="stylesheet" href="{% static 'repapp/css/bootstrap.min.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'repapp/css/repapp.css' %}" />
|
||||
{% block head %}
|
||||
{% endblock head %}
|
||||
</head>
|
||||
|
|
@ -39,10 +40,6 @@
|
|||
<li class="nav-item">
|
||||
<a href="{% url 'logout' %}" class="nav-link">Abmelden</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'login' %}" class="nav-link">Gast Login</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'member' %}" class="nav-link">Mitglieder</a>
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
Guten Tag,
|
||||
wir haben ein Benutzerkonto für Sie erstellt. Sie können sich im Web-Broswer unter {{ link }} um ihre Daten einzusehen und zu ändern.
|
||||
Ihr Anmeldedaten sind:
|
||||
Benutzername: {{ username }}
|
||||
Passwort: {{ password }}
|
||||
Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
Guten Tag,
|
||||
wir haben für Sie einen Einmal-Ammeldelink erstellt. Wenn sie {{ link }} im Web-Broswer öffnen können sie auf ihre Informationen zugreifen.
|
||||
Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<p>Guten Tag,</p>
|
||||
<p>Wir benötigen mehr Informationen zu ihrem Gerät {{ device.device }} - {{ device.manufacturer }}.</p>
|
||||
<p>HIER FRAGE EINFÜGEN</p>
|
||||
<p>Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!</p>
|
||||
|
|
@ -0,0 +1 @@
|
|||
Repair-Café Hilpoltstein - Q#{{ question.pk }}: Rückfrage zu {{ device.device }}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
Guten Tag,
|
||||
dies ist eine automatische Nachricht, die du erhältst, nachdem du unser Anmeldeformular abgesendet hast und deine Angaben in unserer Datenbank gespeichert wurden.
|
||||
Vielen Dank für die Anmeldung des Geräts {{ device.device }} - {{ device.manufacturer }} zur Reparatur im Repair-Café am {{ cafe.event_date|date }}.
|
||||
Sie können die Daten zu ihrer Geräteanmeldung <a href="{{ login_url }}">hier</a> einsehen.
|
||||
Die Organisation unserer Repair-Cafés beginnt i.a. einige Tage vor dem jeweiligen Termin. Dann erstellen wir anhand der angemeldeten Geräte und der Spezialgebiete der anwesenden Reparateure einen Zeitplan.
|
||||
Sobald dieser Zeitplan fertig gestellt ist, erhältst du von einem Mitglied unseres Organisationsteams eine Mail. Diese beinhaltet im Normalfall den Reparaturtermin inkl. der Uhrzeit, für die die Reparatur vorgesehen ist. In Ausnahmefällen (z.B. wenn kein Spezialist für die Gerätegruppe anwesend sein wird oder überbucht ist) ist es aber auch möglich, dass wir deinen gewünschten Termin absagen und dir einen Alternativtermin vorschlagen müssen.
|
||||
Falls du in der Zwischenzeit Rückfragen hast oder deinen Wunschtermin doch nicht wahrnehmen kannst, antworte bitte einfach auf diese Nachricht.
|
||||
Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<p>Guten Tag,</p>
|
||||
<p>Leider können wir das Gerät {{ device.device }} - {{ device.manufacturer }} nicht annehmen.</p>
|
||||
<p>
|
||||
In unserem Repair-Café gibt es leider keinen passenden Reparateur mit den notwenidgen Fähigkeiten um das Gerät zu reparieren.
|
||||
</p>
|
||||
<p>Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!</p>
|
||||
|
|
@ -0,0 +1 @@
|
|||
Repair-Café Hilpoltstein - Geräteablehnung {{ device.device }}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
{% block content %}
|
||||
<p>
|
||||
<a href="{% url 'create_cafe' %}" class="btn btn-primary">Neues Repair-Cafés anlegen</a>
|
||||
<a href="javascript:history.back()" class="btn btn-primary">Zurück</a>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
</p>
|
||||
<table class="table">
|
||||
<thead>
|
||||
|
|
|
|||
23
rc_hip/repapp/templates/repapp/orga/device_question.html
Normal file
23
rc_hip/repapp/templates/repapp/orga/device_question.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{% extends "repapp/base.html" %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
- Rückfrage zum Gerät
|
||||
{% endblock title %}
|
||||
{% block page_title %}
|
||||
Rückfrage zum Gerät
|
||||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<p>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Rückfrage zum Gerät</h1>
|
||||
<form action="{% url 'device_question' device.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary submit_button">Senden</button>
|
||||
</form>
|
||||
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
|
||||
{% endblock content %}
|
||||
28
rc_hip/repapp/templates/repapp/orga/edit_question.html
Normal file
28
rc_hip/repapp/templates/repapp/orga/edit_question.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{% extends "repapp/base.html" %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
- Rückfrage zum Gerät
|
||||
{% endblock title %}
|
||||
{% block page_title %}
|
||||
Rückfrage zum Gerät
|
||||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<p>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Rückfrage zum Gerät</h1>
|
||||
<h2>{{ question.device.device }} - {{ question.device.manufacturer }} vom {{ question.date|date }}</h2>
|
||||
{% if show_guest %}<p>Gast: {{ question.device.guest.name }} ({{ question.device.guest.mail }})</p>{% endif %}
|
||||
{% if question.device.cafe %}<p>Repair-Café: {{ question.device.cafe.event_date|date }}</p>{% endif %}
|
||||
{% if question.organisator %}<p>Organisator: {{ question.organisator.name }}</p>{% endif %}
|
||||
{% if question.reparateur %}<p>Reparateur: {{ question.reparateur.name }}</p>{% endif %}
|
||||
<form action="{% url 'edit_device_question' question.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary submit_button">Als eMail senden</button>
|
||||
</form>
|
||||
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
|
||||
{% endblock content %}
|
||||
|
|
@ -7,8 +7,7 @@
|
|||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<p>
|
||||
<a href="{% url 'create_guest' %}" class="btn btn-primary">Neuen Gast anlegen</a>
|
||||
<a href="javascript:history.back()" class="btn btn-primary">Zurück</a>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
</p>
|
||||
<table class="table">
|
||||
<thead>
|
||||
|
|
@ -19,7 +18,6 @@
|
|||
<th scope="col">Wohnort</th>
|
||||
<th scope="col">eMail Adresse</th>
|
||||
<th scope="col">Bearbeiten</th>
|
||||
<th scope="col">Löschen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
|||
|
|
@ -10,17 +10,129 @@
|
|||
<p>
|
||||
<a href="{% url 'cafe' %}" class="btn btn-primary">Repair-Cafés</a>
|
||||
<a href="{% url 'guest' %}" class="btn btn-primary">Gäste</a>
|
||||
<a href="{% url 'device_questions' %}" class="btn btn-primary">Rückfragen</a>
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Repair-Cafés</h1>
|
||||
<a href="{% url 'create_cafe' %}" class="btn btn-primary">Neues Repair-Café anlegen</a>
|
||||
// TODO: Button nächstes Repair-Café
|
||||
<p>
|
||||
<a href="{% url 'create_cafe' %}" class="btn btn-primary">Neues Repair-Café anlegen</a>
|
||||
<a href="{% url 'edit_cafe' next_cafe.pk %}" class="btn btn-primary">Nächstes Repair-Café</a>
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Geräte</h1>
|
||||
// TODO: Liste mit neuen Geräten
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Art des Geräts</th>
|
||||
<th scope="col">Hersteller & Modell/Typ</th>
|
||||
<th scope="col">Name des Gastes</th>
|
||||
<th scope="col">eMail des Gastes</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for device in devices %}
|
||||
<tr>
|
||||
<td>{{ device.device }}</td>
|
||||
<td>{{ device.manufacturer }}</td>
|
||||
<td>{{ device.guest.name }}</td>
|
||||
<td>{{ device.guest.mail }}</td>
|
||||
<td>
|
||||
<a href="{% url 'review_device' device.pk %}" class="btn btn-primary">überpüfen</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="4">Es gibt keine neuen Geräte.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Rückfragen</h1>
|
||||
// TODO: Liste mit Antworten auf Rückfragen
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Geräts</th>
|
||||
<th scope="col">Hersteller</th>
|
||||
<th scope="col">Gast</th>
|
||||
<th scope="col">Organisator</th>
|
||||
<th scope="col">Reparateur</th>
|
||||
<th scope="col">gesendet</th>
|
||||
<th scope="col">beantwortet</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for question in questions_not_sent %}
|
||||
<tr>
|
||||
<td>{{ question.device.device }}</td>
|
||||
<td>{{ question.device.manufacturer }}</td>
|
||||
<td>{{ question.device.guest.name }}</td>
|
||||
<td>{{ question.organisator.mail }}</td>
|
||||
<td>{{ question.reparateur.mail }}</td>
|
||||
<td>
|
||||
{% if question.sent %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if question.answered %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'view_device_question' question.pk %}"
|
||||
class="btn btn-primary">ansehen</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if not question.sent %}
|
||||
<a href="{% url 'edit_device_question' question.pk %}"
|
||||
class="btn btn-primary">senden</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for question in questions_open_and_answered %}
|
||||
<tr>
|
||||
<td>{{ question.device.device }}</td>
|
||||
<td>{{ question.device.manufacturer }}</td>
|
||||
<td>{{ question.device.guest.name }}</td>
|
||||
<td>{{ question.organisator.mail }}</td>
|
||||
<td>{{ question.reparateur.mail }}</td>
|
||||
<td>
|
||||
{% if question.sent %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if question.answered %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'view_device_question' question.pk %}"
|
||||
class="btn btn-primary">ansehen</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if not question.sent %}
|
||||
<a href="{% url 'edit_device_question' question.pk %}"
|
||||
class="btn btn-primary">senden</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Nachrichten</h1>
|
||||
// TODO: Liste mit neuen Nachrichten
|
||||
|
|
|
|||
41
rc_hip/repapp/templates/repapp/orga/question.html
Normal file
41
rc_hip/repapp/templates/repapp/orga/question.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{% extends "repapp/base.html" %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
- Rückfrage zum Gerät
|
||||
{% endblock title %}
|
||||
{% block page_title %}
|
||||
Rückfrage zum Gerät
|
||||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<p>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Rückfrage zum Gerät</h1>
|
||||
<h2>{{ question.device.device }} - {{ question.device.manufacturer }} vom {{ question.date|date }}</h2>
|
||||
{% if show_guest %}<p>Gast: {{ question.device.guest.name }} ({{ question.device.guest.mail }})</p>{% endif %}
|
||||
{% if question.device.cafe %}<p>Repair-Café: {{ question.device.cafe.event_date|date }}</p>{% endif %}
|
||||
{% if question.organisator %}<p>Organisator: {{ question.organisator.name }}</p>{% endif %}
|
||||
{% if question.reparateur %}<p>Reparateur: {{ question.reparateur.name }}</p>{% endif %}
|
||||
<p>
|
||||
Offen:
|
||||
{% if question.open %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h3>Frage</h3>
|
||||
<div>{{ question.question|safe }}</div>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h3>Antwort</h3>
|
||||
{% if question.answer %}
|
||||
<div>
|
||||
<pre>{{ question.answer|safe }}</pre>
|
||||
</div>
|
||||
{% else %}
|
||||
Der Gast hat noch nicht geantwortet.
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
63
rc_hip/repapp/templates/repapp/orga/questions.html
Normal file
63
rc_hip/repapp/templates/repapp/orga/questions.html
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{% extends "repapp/base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
- Rückfragen
|
||||
{% endblock title %}
|
||||
{% block page_title %}
|
||||
Rückfragen
|
||||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<p>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
</p>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Geräts</th>
|
||||
<th scope="col">Hersteller</th>
|
||||
<th scope="col">Gast</th>
|
||||
<th scope="col">Organisator</th>
|
||||
<th scope="col">Reparateur</th>
|
||||
<th scope="col">gesendet</th>
|
||||
<th scope="col">beantwortet</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for question in questions %}
|
||||
<tr>
|
||||
<td>{{ question.device.device }}</td>
|
||||
<td>{{ question.device.manufacturer }}</td>
|
||||
<td>{{ question.device.guest.name }}</td>
|
||||
<td>{{ question.organisator.mail }}</td>
|
||||
<td>{{ question.reparateur.mail }}</td>
|
||||
<td>
|
||||
{% if question.sent %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if question.answered %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'view_device_question' question.pk %}"
|
||||
class="btn btn-primary">ansehen</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if not question.sent %}
|
||||
<a href="{% url 'edit_device_question' question.pk %}"
|
||||
class="btn btn-primary">senden</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock content %}
|
||||
72
rc_hip/repapp/templates/repapp/orga/review_device.html
Normal file
72
rc_hip/repapp/templates/repapp/orga/review_device.html
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
{% extends "repapp/base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
- Gerät überprüfen
|
||||
{% endblock title %}
|
||||
{% block page_title %}
|
||||
Gerät überprüfen
|
||||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<p>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
<a href="{% url 'review_device_accept' device.pk %}"
|
||||
class="btn btn-primary">Annehmen</a>
|
||||
<a href="{% url 'review_device_reject' device.pk %}"
|
||||
class="btn btn-primary">Ablehnen</a>
|
||||
<a href="{% url 'device_question' device.pk %}" class="btn btn-primary">Rückfrage</a>
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Gerät</h1>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Art des Geräts</th>
|
||||
<td>{{ device.device }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Hersteller & Modell/Typ</th>
|
||||
<td>{{ device.manufacturer }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Fehlerbeschreibung</th>
|
||||
<td>{{ device.error }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Folgetermin</th>
|
||||
<td>
|
||||
{% if device.follow_up %}
|
||||
Ja
|
||||
{% else %}
|
||||
Nein
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Foto vom Gerät</th>
|
||||
<td>
|
||||
{% if device.device_picture %}
|
||||
<img alt="Foto vom Gerät"
|
||||
height="100%"
|
||||
width="50%"
|
||||
src="/media/{{ device.device_picture }}" />
|
||||
{% else %}
|
||||
kein Bild
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Foto vom Typenschild</th>
|
||||
<td>
|
||||
{% if device.device_picture %}
|
||||
<img alt="Foto vom Typenschild"
|
||||
height="100%"
|
||||
width="50%"
|
||||
src="/media/{{ device.type_plate_picture }}" />
|
||||
{% else %}
|
||||
kein Bild
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{% extends "repapp/base.html" %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
- Gerät ablehnen
|
||||
{% endblock title %}
|
||||
{% block page_title %}
|
||||
Gerät ablehnen
|
||||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<p>
|
||||
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||
</p>
|
||||
<hr class="mt-0 mb-4">
|
||||
<h1 class="mt-2">Gerät ablehnen</h1>
|
||||
<form action="{% url 'review_device_reject' device.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary submit_button">Senden</button>
|
||||
</form>
|
||||
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
|
||||
{% endblock content %}
|
||||
|
|
@ -25,8 +25,22 @@ urlpatterns = [
|
|||
path("onetimelogin/<str:secret>/",
|
||||
views.one_time_login, name="one_time_login"),
|
||||
path("member/", views.member, name="member"),
|
||||
path("logout/", views.user_logout, name="logout"),
|
||||
path("orga/select/", views.select_role, name="select_role"),
|
||||
path("orga/", views.orga, name="orga"),
|
||||
path("orga/review/<int:device>/", views.review_device, name="review_device"),
|
||||
path("orga/review/<int:device>/accept/",
|
||||
views.review_device_accept, name="review_device_accept"),
|
||||
path("orga/review/<int:device>/reject/",
|
||||
views.RejectDeviceFormView.as_view(), name="review_device_reject"),
|
||||
path("member/device/<int:device>/question/",
|
||||
views.QuestionDeviceFormView.as_view(), name="device_question"),
|
||||
path("member/device/<int:pk>/question/edit",
|
||||
views.QuestionUpdateView.as_view(), name="edit_device_question"),
|
||||
path("member/question/<int:question>/",
|
||||
views.question, name="view_device_question"),
|
||||
path("member/question/",
|
||||
views.questions, name="device_questions"),
|
||||
path("repa/", views.repa, name="repa"),
|
||||
path("cafe/", views.CafeView.as_view(), name="cafe"),
|
||||
path("cafe/create", views.CafeCreateView.as_view(), name="create_cafe"),
|
||||
|
|
@ -38,5 +52,4 @@ urlpatterns = [
|
|||
path("cron", views.cron, name="cron"),
|
||||
path("bootstrap", views.bootstrap, name="bootstrap"),
|
||||
path("process_mails", views.process_mails, name="process_mails"),
|
||||
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import time
|
|||
import os
|
||||
import logging
|
||||
from hashlib import sha256
|
||||
from bs4 import BeautifulSoup
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
from django.core.mail import send_mail
|
||||
|
|
@ -19,10 +20,10 @@ from django.http import HttpResponseRedirect, HttpResponse
|
|||
from django.core.exceptions import PermissionDenied
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.contrib.auth import authenticate, login
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.utils.timezone import now
|
||||
from .forms import RegisterDevice, RegisterGuest
|
||||
from .models import (Cafe, Device, Guest, OneTimeLogin,
|
||||
from .forms import RegisterDevice, RegisterGuest, Mail, UpdateDeviceForm
|
||||
from .models import (Cafe, Device, Guest, OneTimeLogin, Question,
|
||||
CustomUser, Organisator, Reparateur)
|
||||
from . import mail_interface
|
||||
|
||||
|
|
@ -38,12 +39,11 @@ def send_one_time_login_mail(secret, mail, request):
|
|||
f'/onetimelogin/{secret}/')
|
||||
subject = render_to_string(
|
||||
'repapp/mail/mail_one_time_login_subject.html').replace('\n', '')
|
||||
text = render_to_string('repapp/mail/mail_one_time_login_text.html', {
|
||||
'link': url,
|
||||
})
|
||||
html = render_to_string('repapp/mail/mail_one_time_login_html.html', {
|
||||
'link': url,
|
||||
})
|
||||
soup = BeautifulSoup(html)
|
||||
text = soup.get_text('\n')
|
||||
|
||||
send_ok = send_mail(
|
||||
subject=subject,
|
||||
|
|
@ -90,18 +90,14 @@ def send_confirmation_mails(device, guest, cafe, request):
|
|||
subject = render_to_string('repapp/mail/mail_register_device_subject.html', {
|
||||
'cafe': cafe,
|
||||
}).replace('\n', '')
|
||||
text = render_to_string('repapp/mail/mail_register_device_text.html', {
|
||||
'guest': guest,
|
||||
'device': device,
|
||||
'cafe': cafe,
|
||||
'login_url': login_url,
|
||||
})
|
||||
html = render_to_string('repapp/mail/mail_register_device_html.html', {
|
||||
'guest': guest,
|
||||
'device': device,
|
||||
'cafe': cafe,
|
||||
'login_url': login_url,
|
||||
})
|
||||
soup = BeautifulSoup(html)
|
||||
text = soup.get_text('\n')
|
||||
|
||||
mail_count = send_mail(
|
||||
subject=subject,
|
||||
|
|
@ -127,16 +123,13 @@ def send_guest_account_mail(guest, password, request):
|
|||
url = request.build_absolute_uri('/guest/profile/')
|
||||
subject = render_to_string(
|
||||
'repapp/mail/mail_new_guest_subject.html').replace('\n', '')
|
||||
text = render_to_string('repapp/mail/mail_new_guest_text.html', {
|
||||
'link': url,
|
||||
'username': guest.user.email,
|
||||
'password': password,
|
||||
})
|
||||
html = render_to_string('repapp/mail/mail_new_guest_html.html', {
|
||||
'link': url,
|
||||
'username': guest.user.email,
|
||||
'password': password,
|
||||
})
|
||||
soup = BeautifulSoup(html)
|
||||
text = soup.get_text('\n')
|
||||
|
||||
send_ok = send_mail(
|
||||
subject=subject,
|
||||
|
|
@ -152,6 +145,62 @@ def send_guest_account_mail(guest, password, request):
|
|||
'Sie haben ihre Benutzerdaten per eMail erhalten.')
|
||||
|
||||
|
||||
def send_device_reject_mail(device, reply, request):
|
||||
"""
|
||||
Send a mail to reject the given device.
|
||||
"""
|
||||
subject = render_to_string(
|
||||
'repapp/mail/mail_reject_device_subject.html', {
|
||||
'device': device
|
||||
}).replace('\n', '')
|
||||
|
||||
soup = BeautifulSoup(reply)
|
||||
text = soup.get_text('\n')
|
||||
|
||||
send_ok = send_mail(
|
||||
subject=subject,
|
||||
message=text,
|
||||
from_email=os.getenv("DJANGO_SENDER_ADDRESS", ""),
|
||||
recipient_list=[device.guest.mail],
|
||||
fail_silently=True,
|
||||
html_message=reply
|
||||
)
|
||||
|
||||
if send_ok > 0:
|
||||
messages.add_message(request, messages.INFO,
|
||||
f'Das Gerät {device.device} wurde abgelehnt.')
|
||||
|
||||
|
||||
def send_device_question_mail(question, request):
|
||||
"""
|
||||
Send a mail to ask more details about an device.
|
||||
"""
|
||||
subject = render_to_string(
|
||||
'repapp/mail/mail_question_device_subject.html', {
|
||||
'device': question.device,
|
||||
'question': question,
|
||||
}).replace('\n', '')
|
||||
|
||||
soup = BeautifulSoup(question.question)
|
||||
text = soup.get_text('\n')
|
||||
|
||||
send_ok = send_mail(
|
||||
subject=subject,
|
||||
message=text,
|
||||
from_email=os.getenv("DJANGO_SENDER_ADDRESS", ""),
|
||||
recipient_list=[question.device.guest.mail],
|
||||
fail_silently=True,
|
||||
html_message=question.question
|
||||
)
|
||||
|
||||
if send_ok > 0:
|
||||
question.sent = True
|
||||
question.save()
|
||||
|
||||
messages.add_message(request, messages.INFO,
|
||||
f'Die Rückfrage zum Gerät {question.device.device} wurde gesendet.')
|
||||
|
||||
|
||||
def is_member(user):
|
||||
"""
|
||||
Test is a user is a Repair-Café member.
|
||||
|
|
@ -325,7 +374,8 @@ class RegisterGuestFormView(generic.edit.FormView):
|
|||
guest.user = user
|
||||
guest.save()
|
||||
|
||||
send_guest_account_mail(guest, password, self.request)
|
||||
# Simplify flow for guests, hide account and use only one-time-login
|
||||
# send_guest_account_mail(guest, password, self.request)
|
||||
|
||||
send_confirmation_mails(device, guest, cafe, self.request)
|
||||
|
||||
|
|
@ -450,9 +500,255 @@ def orga(request):
|
|||
logger.warning('The user %s is no organisator!', str(request.user))
|
||||
raise PermissionDenied('Not organisator!')
|
||||
|
||||
devices = Device.objects.filter(status=0).order_by('-date')
|
||||
questions_not_sent = Question.objects.filter(sent=False).order_by('-date')
|
||||
questions_open_and_answered = Question.objects.filter(
|
||||
open=True, answered=True).order_by('-date')
|
||||
|
||||
next_cafe = Cafe.objects.filter(
|
||||
event_date__gte=datetime.datetime.today()).order_by('event_date').first()
|
||||
|
||||
return render(
|
||||
request,
|
||||
"repapp/orga/main.html"
|
||||
"repapp/orga/main.html",
|
||||
{
|
||||
'devices': devices,
|
||||
'questions_not_sent': questions_not_sent,
|
||||
'questions_open_and_answered': questions_open_and_answered,
|
||||
'next_cafe': next_cafe,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required(login_url=reverse_lazy('member'))
|
||||
def review_device(request, device):
|
||||
"""
|
||||
Review new device registration.
|
||||
"""
|
||||
# TODO: test
|
||||
if not is_organisator(request.user):
|
||||
logger.warning('The user %s is no organisator!', str(request.user))
|
||||
raise PermissionDenied('Not organisator!')
|
||||
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"repapp/orga/review_device.html",
|
||||
{
|
||||
'device': device
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required(login_url=reverse_lazy('member'))
|
||||
def review_device_accept(request, device):
|
||||
"""
|
||||
Review new device registration.
|
||||
"""
|
||||
# TODO: test
|
||||
if not is_organisator(request.user):
|
||||
logger.warning('The user %s is no organisator!', str(request.user))
|
||||
raise PermissionDenied('Not organisator!')
|
||||
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
device.status = 3
|
||||
device.save()
|
||||
|
||||
return HttpResponseRedirect(reverse_lazy('orga'))
|
||||
|
||||
|
||||
class RejectDeviceFormView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.FormView):
|
||||
"""
|
||||
View for rejecting a device.
|
||||
"""
|
||||
template_name = "repapp/orga/review_device_reject.html"
|
||||
form_class = Mail
|
||||
login_url = reverse_lazy('member')
|
||||
|
||||
def test_func(self):
|
||||
return is_organisator(self.request.user)
|
||||
|
||||
def form_valid(self, form):
|
||||
device = self.kwargs['device']
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
|
||||
message = form.cleaned_data['message']
|
||||
|
||||
send_device_reject_mail(device, message, self.request)
|
||||
|
||||
device.status = -1
|
||||
device.save()
|
||||
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy('orga')
|
||||
)
|
||||
|
||||
def get_initial(self, **kwargs):
|
||||
device = self.kwargs['device']
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
|
||||
text = render_to_string('repapp/mail/mail_reject_device_html.html', {
|
||||
'device': device,
|
||||
})
|
||||
|
||||
initial = super(RejectDeviceFormView, self).get_initial()
|
||||
initial['message'] = text
|
||||
|
||||
return initial
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
device = self.kwargs['device']
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
|
||||
context = super(RejectDeviceFormView, self).get_context_data(
|
||||
**kwargs
|
||||
)
|
||||
context["device"] = device
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class QuestionDeviceFormView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.FormView):
|
||||
"""
|
||||
View for rejecting a device.
|
||||
"""
|
||||
template_name = "repapp/orga/device_question.html"
|
||||
form_class = Mail
|
||||
login_url = reverse_lazy('member')
|
||||
|
||||
def test_func(self):
|
||||
return is_organisator(self.request.user)
|
||||
|
||||
def form_valid(self, form):
|
||||
device = self.kwargs['device']
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
|
||||
question = None
|
||||
message = form.cleaned_data['message']
|
||||
mail = self.request.user.email
|
||||
organisator = Organisator.objects.filter(mail=mail).first()
|
||||
if organisator:
|
||||
question = Question(
|
||||
question=message,
|
||||
organisator=organisator,
|
||||
device=device
|
||||
)
|
||||
else:
|
||||
raise PermissionDenied('Not valid member found!')
|
||||
question.save()
|
||||
|
||||
send_device_question_mail(question, self.request)
|
||||
|
||||
device.status = 1
|
||||
device.save()
|
||||
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy('orga')
|
||||
)
|
||||
|
||||
def get_initial(self, **kwargs):
|
||||
device = self.kwargs['device']
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
|
||||
text = render_to_string('repapp/mail/mail_question_device_html.html', {
|
||||
'device': device,
|
||||
})
|
||||
|
||||
initial = super(QuestionDeviceFormView, self).get_initial()
|
||||
initial['message'] = text
|
||||
|
||||
return initial
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
device = self.kwargs['device']
|
||||
device = get_object_or_404(Device, pk=device)
|
||||
|
||||
context = super(QuestionDeviceFormView, self).get_context_data(
|
||||
**kwargs
|
||||
)
|
||||
context["device"] = device
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class QuestionUpdateView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.UpdateView):
|
||||
"""
|
||||
Organisator view for edit a Repair-Café.
|
||||
"""
|
||||
# TODO: create test
|
||||
login_url = reverse_lazy('member')
|
||||
template_name = "repapp/orga/edit_question.html"
|
||||
model = Question
|
||||
form_class = UpdateDeviceForm
|
||||
success_url = reverse_lazy('orga')
|
||||
|
||||
def test_func(self):
|
||||
return is_organisator(self.request.user)
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super(QuestionUpdateView, self).form_valid(form)
|
||||
|
||||
question = self.kwargs['pk']
|
||||
question = get_object_or_404(Question, pk=question)
|
||||
send_device_question_mail(question, self.request)
|
||||
|
||||
return response
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
question = self.kwargs['pk']
|
||||
question = get_object_or_404(Question, pk=question)
|
||||
|
||||
context = super(QuestionUpdateView, self).get_context_data(
|
||||
**kwargs
|
||||
)
|
||||
context["question"] = question
|
||||
context["show_guest"] = True
|
||||
|
||||
return context
|
||||
|
||||
|
||||
@login_required(login_url=reverse_lazy('member'))
|
||||
def question(request, question):
|
||||
"""
|
||||
Review new device registration.
|
||||
"""
|
||||
# TODO: test
|
||||
if not is_member(request.user):
|
||||
raise PermissionDenied('Not member!')
|
||||
|
||||
question = get_object_or_404(Question, pk=question)
|
||||
show_guest = False
|
||||
if is_organisator(request.user):
|
||||
show_guest = True
|
||||
|
||||
return render(
|
||||
request,
|
||||
"repapp/orga/question.html",
|
||||
{
|
||||
'question': question,
|
||||
'show_guest': show_guest
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required(login_url=reverse_lazy('member'))
|
||||
def questions(request):
|
||||
"""
|
||||
List of all questions.
|
||||
"""
|
||||
# TODO: test
|
||||
if not is_member(request.user):
|
||||
raise PermissionDenied('Not organisator!')
|
||||
|
||||
questions = Question.objects.order_by('-date')
|
||||
|
||||
return render(
|
||||
request,
|
||||
"repapp/orga/questions.html",
|
||||
{
|
||||
'questions': questions,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -633,3 +929,11 @@ class GuestUpdateView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.Upda
|
|||
|
||||
def test_func(self):
|
||||
return is_organisator(self.request.user)
|
||||
|
||||
|
||||
def user_logout(request):
|
||||
"""
|
||||
Log current user out.
|
||||
"""
|
||||
logout(request)
|
||||
return HttpResponseRedirect(reverse_lazy('index'))
|
||||
|
|
|
|||
|
|
@ -12,3 +12,5 @@ djlint==1.23.3
|
|||
django-import-export==3.2.0
|
||||
selenium==4.9.0
|
||||
imap-tools==1.0.0
|
||||
django-ckeditor==6.5.1
|
||||
beautifulsoup4==4.12.2
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@ crispy-bootstrap5==0.7
|
|||
mozilla-django-oidc==3.0.0
|
||||
django-import-export==3.2.0
|
||||
imap-tools==1.0.0
|
||||
django-ckeditor==6.5.1
|
||||
beautifulsoup4==4.12.2
|
||||
|
|
|
|||
Reference in a new issue