mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-03-14 17:57:39 +00:00
Wrapped apps
This commit is contained in:
parent
7ed544b3ac
commit
cd942779ca
@ -47,6 +47,10 @@ if "wei" in settings.INSTALLED_APPS:
|
||||
from wei.api.urls import register_wei_urls
|
||||
register_wei_urls(router, 'wei')
|
||||
|
||||
if "wrapped" in settings.INSTALLED_APPS:
|
||||
from wrapped.api.urls import register_wrapped_urls
|
||||
register_wrapped_urls(router, 'wrapped')
|
||||
|
||||
app_name = 'api'
|
||||
|
||||
# Wire up our API using automatic URL routing.
|
||||
|
4
apps/wrapped/__init__.py
Normal file
4
apps/wrapped/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'activity.apps.WrappedConfig'
|
16
apps/wrapped/admin.py
Normal file
16
apps/wrapped/admin.py
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib import admin
|
||||
from note_kfet.admin import admin_site
|
||||
|
||||
from .models import Bde, Wrapped
|
||||
|
||||
|
||||
@admin.register(Bde, site=admin_site)
|
||||
class BdeAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
@admin.register(Wrapped, site=admin_site)
|
||||
class WrappedAdmin(admin.ModelAdmin):
|
||||
pass
|
0
apps/wrapped/api/__init__.py
Normal file
0
apps/wrapped/api/__init__.py
Normal file
28
apps/wrapped/api/serializers.py
Normal file
28
apps/wrapped/api/serializers.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..models import Wrapped, Bde
|
||||
|
||||
|
||||
class WrappedSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for Wrapped.
|
||||
The djangorestframework plugin will analyse the model `Wrapped` and parse all fields in the API.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = Wrapped
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class BdeSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for Bde.
|
||||
The djangorestframework plugin will analyse the model `Bde` and parse all fields in the API.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = Bde
|
||||
fields = '__all__'
|
11
apps/wrapped/api/urls.py
Normal file
11
apps/wrapped/api/urls.py
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from .views import WrappedViewSet, BdeViewSet
|
||||
|
||||
def register_wrapped_urls(router, path):
|
||||
"""
|
||||
Configure router for Wrapped REST API.
|
||||
"""
|
||||
router.register(path + '/wrapped', WrappedViewSet)
|
||||
router.register(path + '/bde', BdeViewSet)
|
33
apps/wrapped/api/views.py
Normal file
33
apps/wrapped/api/views.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from api.viewsets import ReadProtectedModelViewSet
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import SearchFilter
|
||||
|
||||
from .serializers import WrappedSerializer, BdeSerializer
|
||||
from ..models import Wrapped, Bde
|
||||
|
||||
class WrappedViewSet(ReadProtectedModelViewSet):
|
||||
"""
|
||||
REST API View set.
|
||||
The djangorestframework plugin will get all `Wrapped` objects, serialize it to JSON with the given
|
||||
serializer, then render it on /api/wrapped/wrapped/
|
||||
"""
|
||||
queryset = Wrapped.objects.order_by('id')
|
||||
serializer_class = WrappedSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['note', 'bde', ]
|
||||
search_fields = ['$note', ]
|
||||
|
||||
class BdeViewSet(ReadProtectedModelViewSet):
|
||||
"""
|
||||
REST API View set.
|
||||
The djangorestframework plugin will get all `Bde` objects, serialize it to JSON with the given
|
||||
serializer, then render it on /api/wrapped/bde/
|
||||
"""
|
||||
queryset = Bde.objects.order_by('id')
|
||||
serializer_class = BdeSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', ]
|
||||
search_fields = ['$name', ]
|
10
apps/wrapped/apps.py
Normal file
10
apps/wrapped/apps.py
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class WrappedConfig(AppConfig):
|
||||
name = 'wrapped'
|
||||
verbose_name = _('wrapped')
|
86
apps/wrapped/migrations/0001_initial.py
Normal file
86
apps/wrapped/migrations/0001_initial.py
Normal file
@ -0,0 +1,86 @@
|
||||
# Generated by Django 4.2.15 on 2025-02-10 12:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("note", "0007_alter_note_polymorphic_ctype_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Bde",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=255, verbose_name="name")),
|
||||
("date_start", models.DateTimeField(verbose_name="date start")),
|
||||
("date_end", models.DateTimeField(verbose_name="date end")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "BDE",
|
||||
"verbose_name_plural": "BDE",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Wrapped",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"generated",
|
||||
models.BooleanField(default=False, verbose_name="generated"),
|
||||
),
|
||||
("public", models.BooleanField(default=False, verbose_name="public")),
|
||||
(
|
||||
"data_json",
|
||||
models.TextField(
|
||||
default="{}",
|
||||
help_text="data in the wrapped and generated by the script generate_wrapped",
|
||||
verbose_name="data json",
|
||||
),
|
||||
),
|
||||
(
|
||||
"bde",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="+",
|
||||
to="wrapped.bde",
|
||||
verbose_name="bde",
|
||||
),
|
||||
),
|
||||
(
|
||||
"note",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="+",
|
||||
to="note.note",
|
||||
verbose_name="note",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Wrapped",
|
||||
"verbose_name_plural": "Wrappeds",
|
||||
"unique_together": {("note", "bde")},
|
||||
},
|
||||
),
|
||||
]
|
0
apps/wrapped/migrations/__init__.py
Normal file
0
apps/wrapped/migrations/__init__.py
Normal file
89
apps/wrapped/models.py
Normal file
89
apps/wrapped/models.py
Normal file
@ -0,0 +1,89 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from note.models import Note
|
||||
|
||||
|
||||
class Bde(models.Model):
|
||||
"""
|
||||
describe a BDE
|
||||
"""
|
||||
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('name'),
|
||||
)
|
||||
|
||||
date_start = models.DateTimeField(
|
||||
verbose_name=_('date start'),
|
||||
)
|
||||
|
||||
date_end = models.DateTimeField(
|
||||
verbose_name=_('date end'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name=_('BDE')
|
||||
verbose_name_plural=_('BDE')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Wrapped(models.Model):
|
||||
"""
|
||||
A Wrapped is associated to a note, a BDE year,
|
||||
"""
|
||||
generated = models.BooleanField(
|
||||
verbose_name=_('generated'),
|
||||
default=False,
|
||||
)
|
||||
|
||||
public = models.BooleanField(
|
||||
verbose_name=_('public'),
|
||||
default=False,
|
||||
)
|
||||
|
||||
bde = models.ForeignKey(
|
||||
Bde,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='+',
|
||||
verbose_name=_('bde'),
|
||||
)
|
||||
|
||||
note = models.ForeignKey(
|
||||
Note,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='+',
|
||||
verbose_name=_('note'),
|
||||
)
|
||||
|
||||
data_json = models.TextField(
|
||||
default='{}',
|
||||
verbose_name=_('data json'),
|
||||
help_text=_('data in the wrapped and generated by the script generate_wrapped'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name=_('Wrapped')
|
||||
verbose_name_plural=_('Wrappeds')
|
||||
unique_together=('note','bde')
|
||||
|
||||
def __str__(self):
|
||||
return 'NoteKfet Wrapped of {note} sponsored by {bde}'.format(bde=str(self.bde),note=str(self.note))
|
||||
def makepublic(self):
|
||||
self.public = not self.public
|
||||
self.save()
|
||||
return
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return json.load(self.data_json)
|
||||
|
||||
@data.setter
|
||||
def data(self, data):
|
||||
self.data_json = json.dumps(data, indent=2)
|
72
apps/wrapped/tables.py
Normal file
72
apps/wrapped/tables.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from note_kfet.middlewares import get_current_request
|
||||
import django_tables2 as tables
|
||||
from django_tables2 import A
|
||||
from permission.backends import PermissionBackend
|
||||
from note.templatetags.pretty_money import pretty_money
|
||||
|
||||
from .models import Wrapped, Bde
|
||||
|
||||
class WrappedTable(tables.Table):
|
||||
"""
|
||||
List all wrapped
|
||||
"""
|
||||
class Meta:
|
||||
attrs = {
|
||||
'class': 'table table-condensed table-striped table-hover',
|
||||
'id': 'wrapped_table'
|
||||
}
|
||||
row_attrs = {
|
||||
'class': lambda record: 'bg-danger' if not record.generated else '',
|
||||
}
|
||||
model = Wrapped
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
fields = ('note', 'bde', 'public', )
|
||||
|
||||
view = tables.LinkColumn(
|
||||
'wrapped:wrapped_detail',
|
||||
args=[A('pk')],
|
||||
|
||||
attrs={
|
||||
'td': {'class': 'col-sm-2'},
|
||||
'a': {
|
||||
'class': 'btn btn-sm btn-primary',
|
||||
'data-turbolinks': 'false',
|
||||
}
|
||||
},
|
||||
text=_('view the wrapped'),
|
||||
accessor='pk',
|
||||
verbose_name=_('View'),
|
||||
orderable=False,
|
||||
)
|
||||
|
||||
public = tables.Column(
|
||||
accessor="pk",
|
||||
orderable=False,
|
||||
attrs={
|
||||
"td": {
|
||||
"id": lambda record: "makepublic_"+ str(record.pk),
|
||||
"class" : 'col-sm-1',
|
||||
"data-toggle": "tooltip",
|
||||
"title": lambda record:
|
||||
(_("Click to make this wrapped private") if record.public else
|
||||
_("Click to make this wrapped public")) if PermissionBackend.check_perm(
|
||||
get_current_request(), "wrapped.change_wrapped_public", record) else None,
|
||||
"onclick" : lambda record:
|
||||
'makepublic(' + str(record.id) + ', ' + str(not record.public).lower() + ')'
|
||||
if PermissionBackend.check_perm(get_current_request(), "wrapped.change_wrapped_public",
|
||||
record) else None
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
def render_public(self, value, record):
|
||||
val = "✔" if record.public else "✖"
|
||||
return val
|
||||
|
8
apps/wrapped/templates/wrapped/1/wrapped_view.html
Normal file
8
apps/wrapped/templates/wrapped/1/wrapped_view.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% comment %}
|
||||
Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% block content %}
|
||||
{{ wrapped.data_json }}
|
||||
{% endblock %}
|
||||
|
62
apps/wrapped/templates/wrapped/wrapped_list.html
Normal file
62
apps/wrapped/templates/wrapped/wrapped_list.html
Normal file
@ -0,0 +1,62 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-10">
|
||||
<div class="card card-border shadow">
|
||||
<div class="card-header text-center">
|
||||
<h5> {{ title }}</h5>
|
||||
</div>
|
||||
<div class="card-body px-0 py-0" id="wrapped_table">
|
||||
{% render_table table %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script type="text/javascript">
|
||||
function refreshTable() {
|
||||
$("#wrapped_table").load(location.pathname + " #wrapped_table");
|
||||
}
|
||||
|
||||
function makepublic(id, isprivate) {
|
||||
const makepublic_obj = $('#makepublic_'+id)
|
||||
|
||||
if (makepublic_obj.data('pending'))
|
||||
// The button is already clicked
|
||||
{ return }
|
||||
|
||||
makepublic_obj.html('<strong style="font-size: 16pt;">⟳</strong>')
|
||||
makepublic_obj.data('pending', true)
|
||||
|
||||
$.ajax({
|
||||
url: '/api/wrapped/wrapped/' + id + '/',
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
'X-CSRFTOKEN': CSRF_TOKEN
|
||||
},
|
||||
data: {
|
||||
public: isprivate
|
||||
},
|
||||
success: function() {
|
||||
if(!isprivate)
|
||||
addMsg("{% trans "Wrapped is private" %}", 'success', 2000)
|
||||
else addMsg("{% trans "Wrapped is public" %}", 'success', 2000)
|
||||
refreshTable()
|
||||
},
|
||||
error: function (err) {
|
||||
addMsg("{% trans "An error occured" %}", 'danger')
|
||||
refreshTable()
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
13
apps/wrapped/urls.py
Normal file
13
apps/wrapped/urls.py
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'wrapped'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.WrappedListView.as_view(), name='wrapped_list'),
|
||||
path('<int:pk>/', views.WrappedDetailView.as_view(), name='wrapped_detail'),
|
||||
]
|
63
apps/wrapped/views.py
Normal file
63
apps/wrapped/views.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from hashlib import md5
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import transaction
|
||||
from django.db.models import F, Q
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.views.generic import DetailView, TemplateView, UpdateView
|
||||
from django.views.generic.list import ListView
|
||||
from django_tables2.views import MultiTableMixin, SingleTableMixin, SingleTableView
|
||||
from api.viewsets import is_regex
|
||||
from note.models import Alias, NoteSpecial, NoteUser
|
||||
from permission.backends import PermissionBackend
|
||||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||
|
||||
from .models import Wrapped
|
||||
from .tables import WrappedTable
|
||||
|
||||
class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
|
||||
"""
|
||||
Display all Wrapped, and classify by year
|
||||
"""
|
||||
model = Wrapped
|
||||
table_class = WrappedTable
|
||||
template_name = 'wrapped/wrapped_list.html'
|
||||
extra_context = {'title': _("List of wrapped")}
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
return super().get_queryset(**kwargs).distinct()
|
||||
|
||||
def get_table_data(self):
|
||||
return Wrapped.objects.filter(PermissionBackend.filter_queryset(
|
||||
self.request, Wrapped, "change", field='public')).distinct().order_by("-bde__date_start")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
class WrappedDetailView(ProtectQuerysetMixin, DetailView):
|
||||
"""
|
||||
View a wrapped
|
||||
"""
|
||||
model = Wrapped
|
||||
template_name = 'wrapped/0/wrapped_view.html' #by default
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
bde_id = Wrapped.objects.get(pk=kwargs['pk']).bde.id
|
||||
self.template_name = 'wrapped/' + str(bde_id) + '/wrapped_view.html'
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
return context
|
@ -79,6 +79,7 @@ INSTALLED_APPS = [
|
||||
'scripts',
|
||||
'treasury',
|
||||
'wei',
|
||||
'wrapped',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -107,6 +107,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% url 'wei:current_wei_detail' as url %}
|
||||
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-bus"></i> {% trans 'WEI' %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if "wrapped.wrapped"|model_list_length >= 1 %}
|
||||
<li class="nav-item">
|
||||
{% url 'wrapped:wrapped_list' as url %}
|
||||
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-birthday-cake"></i> {% trans 'Wrapped' %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
|
@ -22,6 +22,7 @@ urlpatterns = [
|
||||
path('treasury/', include('treasury.urls')),
|
||||
path('wei/', include('wei.urls')),
|
||||
path('food/',include('food.urls')),
|
||||
path('wrapped/',include('wrapped.urls')),
|
||||
|
||||
# Include Django Contrib and Core routers
|
||||
path('i18n/', include('django.conf.urls.i18n')),
|
||||
|
Loading…
x
Reference in New Issue
Block a user