3
0
Fork 0
mirror of https://github.com/nanoy42/coope synced 2024-11-25 22:22:27 +00:00

Improvements

This commit is contained in:
Yoann Pétri 2019-09-12 09:40:43 +02:00
parent 192696d026
commit 73e7697a3c
11 changed files with 299 additions and 8 deletions

View file

@ -130,3 +130,5 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
MEDIA_URL = '/media/' MEDIA_URL = '/media/'
INTERNAL_IPS = ["127.0.0.1"] INTERNAL_IPS = ["127.0.0.1"]
EMAIL_SUBJECT_PREFIX = "[Coope Admin] "

View file

@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin from simple_history.admin import SimpleHistoryAdmin
from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile, Improvement
class CotisationAdmin(SimpleHistoryAdmin): class CotisationAdmin(SimpleHistoryAdmin):
""" """
@ -40,8 +40,18 @@ class DivideHistoryAdmin(SimpleHistoryAdmin):
list_display = ('date', 'total_cotisations', 'total_cotisations_amount', 'total_ptm_amount', 'coopeman') list_display = ('date', 'total_cotisations', 'total_cotisations_amount', 'total_ptm_amount', 'coopeman')
ordering = ('-date',) ordering = ('-date',)
class ImprovementAdmin(SimpleHistoryAdmin):
"""
The admin class for Improvement.
"""
list_display = ('title', 'mode', 'seen', 'done', 'date')
ordering = ('-date',)
search_fields = ('title', 'description')
list_filter = ('mode', 'seen', 'done')
admin.site.register(PaymentMethod, PaymentMethodAdmin) admin.site.register(PaymentMethod, PaymentMethodAdmin)
admin.site.register(GeneralPreferences, GeneralPreferencesAdmin) admin.site.register(GeneralPreferences, GeneralPreferencesAdmin)
admin.site.register(Cotisation, CotisationAdmin) admin.site.register(Cotisation, CotisationAdmin)
admin.site.register(PriceProfile, PriceProfileAdmin) admin.site.register(PriceProfile, PriceProfileAdmin)
admin.site.register(DivideHistory, DivideHistoryAdmin) admin.site.register(DivideHistory, DivideHistoryAdmin)
admin.site.register(Improvement, ImprovementAdmin)

View file

@ -1,7 +1,7 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from .models import Cotisation, PaymentMethod, GeneralPreferences, PriceProfile from .models import Cotisation, PaymentMethod, GeneralPreferences, PriceProfile, Improvement
class CotisationForm(forms.ModelForm): class CotisationForm(forms.ModelForm):
""" """
@ -50,3 +50,11 @@ class GeneralPreferencesForm(forms.ModelForm):
'home_text': forms.Textarea(attrs={'placeholder': 'Ce message sera affiché sur la page d\'accueil'}) 'home_text': forms.Textarea(attrs={'placeholder': 'Ce message sera affiché sur la page d\'accueil'})
} }
class ImprovementForm(forms.ModelForm):
"""
Form to create an improvement
"""
class Meta:
model = Improvement
fields = ["title", "mode", "description"]

View file

@ -0,0 +1,31 @@
# Generated by Django 2.1 on 2019-09-08 09:59
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('preferences', '0018_auto_20190627_2302'),
]
operations = [
migrations.CreateModel(
name='Improvement',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('mode', models.IntegerField(choices=[(0, 'Bug'), (1, 'Amélioration'), (2, 'Nouvelle fonctionnalité')])),
('description', models.TextField()),
('seen', models.BooleanField(default=False)),
('done', models.BooleanField(default=False)),
('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='improvement_submitted', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Amélioration',
},
),
]

View file

@ -0,0 +1,39 @@
# Generated by Django 2.1 on 2019-09-08 10:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0019_improvement'),
]
operations = [
migrations.AddField(
model_name='improvement',
name='date',
field=models.DateTimeField(auto_now_add=True, default='2019-09-08 00:00'),
preserve_default=False,
),
migrations.AlterField(
model_name='improvement',
name='done',
field=models.BooleanField(default=False, verbose_name='Fait ?'),
),
migrations.AlterField(
model_name='improvement',
name='mode',
field=models.IntegerField(choices=[(0, 'Bug'), (1, 'Amélioration'), (2, 'Nouvelle fonctionnalité')], verbose_name='Type'),
),
migrations.AlterField(
model_name='improvement',
name='seen',
field=models.BooleanField(default=False, verbose_name='Vu ?'),
),
migrations.AlterField(
model_name='improvement',
name='title',
field=models.CharField(max_length=255, verbose_name='Titre'),
),
]

View file

@ -202,3 +202,31 @@ class PriceProfile(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
class Improvement(models.Model):
"""
Stores bugs and amelioration proposals.
"""
BUG = 0
AMELIORATION = 1
NEWFEATURE = 2
MODES = (
(BUG, "Bug"),
(AMELIORATION, "Amélioration"),
(NEWFEATURE, "Nouvelle fonctionnalité")
)
class Meta:
verbose_name = "Amélioration"
title = models.CharField(max_length=255, verbose_name="Titre")
mode = models.IntegerField(choices=MODES, verbose_name="Type")
description = models.TextField()
seen = models.BooleanField(default=False, verbose_name="Vu ?")
done = models.BooleanField(default=False, verbose_name="Fait ?")
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="improvement_submitted")
date = models.DateTimeField(auto_now_add=True)

View file

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block entete %}Amélioration {{improvement.title}}{% endblock %}
{% block navbar %}
<ul>
<li><a href="#first">{{improvement.title}}</a></li>
</ul>
{% endblock %}
{% block content %}
<section id="first" class="main">
<header class="major">
<h2>{{improvement.title}}</h2>
</header>
<a href="{% url 'preferences:improvementsIndex' %}" class="button">Retour à la liste des améliorations</a><br><br>
<strong>Titre : </strong> {{improvement.title}}<br>
<strong>Type : </strong> {{improvement.get_mode_display}}<br>
<strong>Date : </strong> {{improvement.date}}<br>
<strong>Fait : </strong> {{improvement.done|yesno:"Oui,Non"}}<br>
<strong>Coopeman : </strong> {{improvement.coopeman}}<br>
<strong>Description : </strong> {{improvement.description}}<br>
</section>
{% endblock %}

View file

@ -0,0 +1,68 @@
{% extends "base.html" %}
{% block entete %}Améliorations{% endblock %}
{% block navbar %}
<ul>
<li><a href="#first">Liste des améliorations à faire</a></li>
<li><a href="#seconde">Liste des améliorations faîtes</a></li>
</ul>
{% endblock %}
{% block content %}
<section id="first" class="main">
<header class="major">
<h2>Liste des améliorations à faire</h2>
</header>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Titre</th>
<th>Type</th>
<th>Vu ?</th>
<th>Date</th>
<th>Administration</th>
</tr>
</thead>
<tbody>
{% for improvement in todo_improvements %}
<tr>
<td>{{improvement.title}}</td>
<td>{{improvement.get_mode_display}}</td>
<td>{{improvement.seen|yesno:"Oui,Non"}}</td>
<td>{{improvement.date}}</td>
<td><a href="{% url 'preferences:improvementProfile' improvement.pk %}" class="button small"><i class="fa fa-eye"></i> Voir</a> <a href="{% url 'preferences:changeImprovementState' improvement.pk %}" class="button small"><i class="fa fa-check"></i> Passer en fait</a> <a href="{% url 'preferences:deleteImprovement' improvement.pk %}" class="button small"><i class="fa fa-trash"></i> Supprimer</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section id="second" class="main">
<header class="major">
<h2>Liste des améliorations faîtes</h2>
</header>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Titre</th>
<th>Type</th>
<th>Vu ?</th>
<th>Date</th>
<th>Administration</th>
</tr>
</thead>
<tbody>
{% for improvement in done_improvements %}
<tr>
<td>{{improvement.title}}</td>
<td>{{improvement.get_mode_display}}</td>
<td>{{improvement.seen|yesno:"Oui,Non"}}</td>
<td>{{improvement.date}}</td>
<td><a href="{% url 'preferences:improvementProfile' improvement.pk %}" class="button small"><i class="fa fa-eye"></i> Voir</a> <a href="{% url 'preferences:changeImprovementState' improvement.pk %}" class="button small"><i class="fa fa-check"></i> Passer en non fait</a> <a href="{% url 'preferences:deleteImprovement' improvement.pk %}" class="button small"><i class="fa fa-trash"></i> Supprimer</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
{% endblock %}

View file

@ -19,5 +19,10 @@ urlpatterns = [
path('deletePriceProfile/<int:pk>', views.delete_price_profile, name="deletePriceProfile"), path('deletePriceProfile/<int:pk>', views.delete_price_profile, name="deletePriceProfile"),
path('inactive', views.inactive, name="inactive"), path('inactive', views.inactive, name="inactive"),
path('getConfig', views.get_config, name="getConfig"), path('getConfig', views.get_config, name="getConfig"),
path('getCotisation/<int:pk>', views.get_cotisation, name="getCotisation") path('getCotisation/<int:pk>', views.get_cotisation, name="getCotisation"),
,] path('addImprovement', views.add_improvement, name="addImprovement"),
path('improvementsIndex', views.improvements_index, name="improvementsIndex"),
path('improvementProfile/<int:pk>', views.improvement_profile, name="improvementProfile"),
path('deleteImprovement/<int:pk>', views.delete_improvement, name="deleteImprovement"),
path('changeImprovementState/<int:pk>', views.change_improvement_state, name="changeImprovementState"),
]

View file

@ -7,12 +7,13 @@ from django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponse from django.http import HttpResponse
from django.forms.models import model_to_dict from django.forms.models import model_to_dict
from django.http import Http404 from django.http import Http404
from django.core.mail import mail_admins
from coopeV3.acl import active_required from coopeV3.acl import active_required
from .models import GeneralPreferences, Cotisation, PaymentMethod, PriceProfile from .models import GeneralPreferences, Cotisation, PaymentMethod, PriceProfile, Improvement
from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm, PriceProfileForm from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm, PriceProfileForm, ImprovementForm
@active_required @active_required
@login_required @login_required
@ -245,3 +246,73 @@ def delete_price_profile(request,pk):
price_profile.delete() price_profile.delete()
messages.success(request, message) messages.success(request, message)
return redirect(reverse('preferences:priceProfilesIndex')) return redirect(reverse('preferences:priceProfilesIndex'))
########## Improvements ##########
@active_required
@login_required
def add_improvement(request):
"""
Display a form to create an improvement. Any logged user can access it
"""
form = ImprovementForm(request.POST or None)
if form.is_valid():
improvement = form.save(commit=False)
improvement.coopeman = request.user
improvement.save()
mail_admins("Nouvelle proposition d'amélioration", "Une nouvelle proposition d'amélioration a été postée (" + improvement.title + ", " + improvement.get_mode_display() + "). Le corps est le suivant : " + improvement.description)
messages.success(request, "Votre proposition a bien été envoyée")
return redirect(reverse('home'))
return render(request, "form.html", {"form": form, "form_title": "Proposition d'amélioration", "form_button": "Envoyer", "form_button_icon": "bug"})
@active_required
@login_required
@permission_required('preferences.view_improvement')
def improvements_index(request):
"""
Display all improvements
"""
todo_improvements = Improvement.objects.filter(done=False)
done_improvements = Improvement.objects.filter(done=True)
return render(request, "preferences/improvements_index.html", {"todo_improvements": todo_improvements, "done_improvements": done_improvements})
@active_required
@login_required
@permission_required('preferences.view_improvement')
@permission_required('preferences.change_improvement')
def improvement_profile(request, pk):
"""
Display an improvement
"""
improvement = get_object_or_404(Improvement, pk=pk)
improvement.seen = 1
improvement.save()
return render(request, "preferences/improvement_profile.html", {"improvement": improvement})
@active_required
@login_required
@permission_required('preferences.change_improvement')
def change_improvement_state(request, pk):
"""
Change done state of an improvement
"""
improvement = get_object_or_404(Improvement, pk=pk)
improvement.done = 1 - improvement.done
improvement.save()
messages.success(request, "L'état a bien été changé")
return redirect(reverse('preferences:improvementsIndex'))
@active_required
@login_required
@permission_required('preferences.delete_improvement')
def delete_improvement(request, pk):
"""
Delete an improvement
"""
improvement = get_object_or_404(Improvement, pk=pk)
improvement.delete()
messages.success(request, "L'amélioration a bien été supprimée.")
return redirect(reverse('preferences:improvementsIndex'))

View file

@ -70,6 +70,14 @@
<i class="fa fa-search-dollar"></i> <a href="{% url 'gestion:compute-price' %}">Calcul de prix</a> <i class="fa fa-search-dollar"></i> <a href="{% url 'gestion:compute-price' %}">Calcul de prix</a>
</span> </span>
{% endif %} {% endif %}
<span class="tabulation2">
<i class="fa fa-bug"></i> <a href="{% url 'preferences:addImprovement' %}">Proposition d'amélioration</a>
</span>
{% if perms.preferences.view_improvement %}
<span class="tabulation2">
<i class="fa fa-bug"></i> <a href="{% url 'preferences:improvementsIndex' %}">Améliorations</a>
</span>
{% endif %}
<span class="tabulation2"> <span class="tabulation2">
<i class="fa fa-bed"></i> <a href="{% url 'users:logout' %}">Deconnexion</a> <i class="fa fa-bed"></i> <a href="{% url 'users:logout' %}">Deconnexion</a>
</span> </span>