1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-03-14 17:57:39 +00:00

Merge branch 'notekfet_wrapped' into 'main'

Notekfet wrapped

See merge request bde/nk20!287
This commit is contained in:
quark 2025-03-05 22:03:51 +01:00
commit 6028bfeb56
39 changed files with 2482 additions and 294 deletions

View File

@ -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.

View File

@ -3829,7 +3829,7 @@
"mask": 3,
"field": "",
"permanent": false,
"description": "Voir les profils des membres du club"
"description": "Voir les profils des membres du club"
}
},
{
@ -4008,6 +4008,86 @@
"description": "Voir le tableau des ouvreur⋅ses pour les activités organisées par son club"
}
},
{
"model": "permission.permission",
"pk": 255,
"fields": {
"model": [
"wrapped",
"wrapped"
],
"query": "{\"public\": true}",
"type": "view",
"mask": 1,
"field": "",
"permanent": false,
"description": "Voir les wrapped public"
}
},
{
"model": "permission.permission",
"pk": 256,
"fields": {
"model": [
"wrapped",
"wrapped"
],
"query": "{\"note__noteuser__user\": [\"user\"]}",
"type": "view",
"mask": 1,
"field": "",
"permanent": true,
"description": "Voir ses propres wrapped, pour toujours"
}
},
{
"model": "permission.permission",
"pk": 257,
"fields": {
"model": [
"wrapped",
"wrapped"
],
"query": "{\"note__noteuser__user\": [\"user\"]}",
"type": "change",
"mask": 1,
"field": "public",
"permanent": true,
"description": "Modifier la visibilité de ses wrapped, pour toujours"
}
},
{
"model": "permission.permission",
"pk": 258,
"fields": {
"model": [
"wrapped",
"wrapped"
],
"query": "{\"note__noteclub__club\": [\"club\"]}",
"type": "view",
"mask": 1,
"field": "",
"permanent": false,
"description": "Voir les wrapped de son club"
}
},
{
"model": "permission.permission",
"pk": 259,
"fields": {
"model": [
"wrapped",
"wrapped"
],
"query": "{\"note__noteclub__club\": [\"club\"]}",
"type": "change",
"mask": 1,
"field": "public",
"permanent": false,
"description": "Modifier la visibilité des wrapped de son club"
}
},
{
"model": "permission.role",
"pk": 1,
@ -4059,7 +4139,10 @@
205,
206,
248,
249
249,
255,
256,
257
]
}
},
@ -4148,7 +4231,10 @@
227,
233,
234,
237
237,
247,
258,
259
]
}
},

4
apps/wrapped/__init__.py Normal file
View 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'

17
apps/wrapped/admin.py Normal file
View File

@ -0,0 +1,17 @@
# 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

View File

View 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__'

12
apps/wrapped/api/urls.py Normal file
View File

@ -0,0 +1,12 @@
# 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)

35
apps/wrapped/api/views.py Normal file
View File

@ -0,0 +1,35 @@
# 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
View 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')

View File

@ -0,0 +1,592 @@
# Copyright (C) 2028-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import json
from argparse import ArgumentParser
from django.core.management import BaseCommand
from django.db.models import Q
from note.models import Note, Transaction
from member.models import User, Club, Membership
from activity.models import Activity, Entry
from wei.models import WEIClub
from ...models import Bde, Wrapped
class Command(BaseCommand):
help = "Generate wrapper for the annual BDE change"
def add_arguments(self, parser: ArgumentParser):
parser.add_argument(
'-b', '--bde',
type=str,
required=False,
help="A list of BDE name, BDE1,BDE2,... (a BDE name cannot have ',')",
dest='bde',
)
parser.add_argument(
'-i', '--id',
type=str,
required=False,
help="A list of BDE id, id1,id2,...",
dest='bde_id',
)
parser.add_argument(
'-u', '--users',
type=str,
required=False,
help="""User will have their(s) wrapped generated,
all = all users
adh = all users who have a valid memberships to BDE during the BDE considered
supersuser = all superusers
custom user1,user2,... = a list of username,
custom_id id1,id2,... = a list of user id""",
dest='user',
)
parser.add_argument(
'-c', '--club',
type=str,
required=False,
help="""Club will have their(s) wrapped generated,
all = all clubs,
active = all clubs with at least one transaction during the BDE mandate considered,
custom club1,club2,... = a list of club name,
custom_id id1,id2,... = a list of club id""",
dest='club',
)
parser.add_argument(
'-f', '--force-change',
required=False,
action='store_true',
help="if wrapped already exist change data_json",
dest='change',
)
parser.add_argument(
'-n', '--no-creation',
required=False,
action='store_false',
help="if wrapped don't already exist, don't generate it",
dest='create',
)
def handle(self, *args, **options):
# useful string for output
red = '\033[31;1m'
yellow = '\033[33;1m'
green = '\033[32;1m'
abort = red + 'ABORT'
warning = yellow + 'WARNING'
success = green + 'SUCCESS'
# Traitement des paramètres
verb = options['verbosity']
bde = []
if options['bde']:
bde_list = options['bde'].split(',')
bde = [Bde.objects.get(name=bde_name) for bde_name in bde_list]
if options['bde_id']:
if bde:
if verb >= 1:
print(warning)
print(yellow + 'You already defined bde with their name !')
if verb >= 0:
print(abort)
return
bde_id = options['bde_id'].split(',')
bde = [Bde.objects.get(pk=i) for i in bde_id]
user = []
if options['user']:
if options['user'] == 'all':
user = ['all', None]
elif options['user'] == 'adh':
user = ['adh', None]
elif options['user'] == 'superuser':
user = ['superuser', None]
elif options['user'].split(' ')[0] == 'custom':
user_list = options['user'].split(' ')[1].split(',')
user = ['custom', [User.objects.get(username=u) for u in user_list]]
elif options['user'].split(' ')[0] == 'custom_id':
user_id = options['user'].split(' ')[1].split(',')
user = ['custom_id', [User.objects.get(pk=u) for u in user_id]]
else:
if verb >= 1:
print(warning)
print(yellow + 'You user option is not recognized')
if verb >= 0:
print(abort)
return
club = []
if options['club']:
if options['club'] == 'all':
club = ['all', None]
elif options['club'] == 'active':
club = ['active', None]
elif options['club'].split(' ')[0] == 'custom':
club_list = options['club'].split(' ')[1].split(',')
club = ['custom', [Club.objects.get(name=club_name) for club_name in club_list]]
elif options['club'].split(' ')[0] == 'custom_id':
club_id = options['club'].split(' ')[1].split(',')
club = ['custom_id', [Club.objects.get(pk=c) for c in club_id]]
else:
if verb >= 1:
print(warning)
print(yellow + 'You club option is not recognized')
if verb >= 0:
print(abort)
return
change = options['change']
create = options['create']
# check if parameters are sufficient for generate wrapped with the desired option
if not bde:
if verb >= 1:
print(warning)
print(yellow + 'You have not selectionned a BDE !')
if verb >= 0:
print(abort)
return
if not (user or club):
if verb >= 1:
print(warning)
print(yellow + 'No club or user selected !')
if verb >= 0:
print(abort)
return
if verb >= 3:
print('\033[1mOptions:\033[m')
bde_str = ''
for b in bde:
bde_str += str(b)
print('BDE: ' + bde_str)
if user:
print('User: ' + user[0])
if club:
print('Club: ' + club[0])
print('change: ' + str(change))
print('create: ' + str(create))
print('')
if not (change or create):
if verb >= 1:
print(warning)
print(yellow + 'change and create is set to false, none wrapped will be created')
if verb >= 0:
print(abort)
return
if verb >= 1 and change:
print(warning)
print(yellow + 'change is set to true, some wrapped may be replaced !')
if verb >= 1 and not create:
print(warning)
print(yellow + 'create is set to false, wrapped will not be created !')
if verb >= 3 or change or not create:
a = str(input('\033[mContinue ? (y/n) ')).lower()
if a in ['n', 'no', 'non', '0']:
if verb >= 0:
print(abort)
return
note = self.convert_to_note(change, create, bde=bde, user=user, club=club, verb=verb)
if verb >= 1:
print("\033[32mUser and/or Club given has successfully convert in their note\033[m")
global_data = self.global_data(bde, verb=verb)
if verb >= 1:
print("\033[32mGlobal data has been successfully generated\033[m")
unique_data = self.unique_data(bde, note, global_data=global_data, verb=verb)
if verb >= 1:
print("\033[32mUnique data has been successfully generated\033[m")
self.make_wrapped(unique_data, note, bde, change, create, verb=verb)
if verb >= 1:
print(green + "The wrapped has been generated !")
if verb >= 0:
print(success)
return
def convert_to_note(self, change, create, bde=None, user=None, club=None, verb=1):
notes = []
for b in bde:
note_for_bde = Note.objects.filter(pk__lte=-1)
if user:
if 'custom' in user[0]:
for u in user[1]:
query = Q(noteuser__user=u)
note_for_bde |= Note.objects.filter(query)
elif user[0] == 'all':
query = Q(noteuser__user__pk__gte=-1)
note_for_bde |= Note.objects.filter(query)
elif user[0] == 'adh':
m = Membership.objects.filter(club=1,
date_start__lt=b.date_end,
date_end__gt=b.date_start,
).distinct('user')
for membership in m:
note_for_bde |= Note.objects.filter(noteuser__user=membership.user)
elif user[0] == 'superuser':
query |= Q(noteuser__user__is_superuser=True)
note_for_bde |= Note.objects.filter(query)
if club:
if 'custom' in club[0]:
for c in club[1]:
query = Q(noteclub__club=c)
note_for_bde |= Note.objects.filter(query)
elif club[0] == 'all':
query = Q(noteclub__club__pk__gte=-1)
note_for_bde |= Note.objects.filter(query)
elif club[0] == 'active':
nc = Note.objects.filter(noteclub__club__pk__gte=-1)
for noteclub in nc:
if Transaction.objects.filter(
Q(created_at__gte=b.date_start,
created_at__lte=b.date_end) & (Q(source=noteclub) | Q(destination=noteclub))):
note_for_bde |= Note.objects.filter(pk=noteclub.pk)
note_for_bde = self.filter_note(b, note_for_bde, change, create, verb=verb)
notes.append(note_for_bde)
if verb >= 2:
print("\033[m{nb} note selectionned for bde {bde}".format(nb=len(note_for_bde), bde=b.name))
return notes
def global_data(self, bde, verb=1):
data = {}
for b in bde:
if b.name == 'Rave Part[list]':
if verb >= 2:
print("Begin to make global data")
if verb >= 3:
print('nb_transaction')
# nb total de transactions
data['nb_transaction'] = Transaction.objects.filter(
created_at__gte=b.date_start,
created_at__lte=b.date_end,
valid=True).count()
if verb >= 3:
print('nb_vieux_con')
# nb total de vielleux con·ne·s derrière le bar
button_id = [2884, 2585]
transactions = Transaction.objects.filter(
created_at__gte=b.date_start,
created_at__lte=b.date_end,
valid=True,
recurrenttransaction__template__pk__in=button_id)
q = 0
for t in transactions:
q += t.quantity
data['nb_vieux_con'] = q
if verb >= 3:
print('nb_soiree')
# nb total de soirée
a_type_id = [1, 2, 4, 5, 7, 10]
data['nb_soiree'] = Activity.objects.filter(
date_end__gte=b.date_start,
date_start__lte=b.date_end,
valid=True,
activity_type__pk__in=a_type_id).count()
if verb >= 3:
print('pots, nb_entree_pot')
# nb d'entrée totale aux pots
pot_id = [1, 4, 10]
pots = Activity.objects.filter(
date_end__gte=b.date_start,
date_start__lte=b.date_end,
valid=True,
activity_type__pk__in=pot_id)
data['pots'] = pots # utile dans unique_data
data['nb_entree_pot'] = 0
for pot in pots:
data['nb_entree_pot'] += Entry.objects.filter(activity=pot).count()
if verb >= 3:
print('top3_buttons')
# top 3 des boutons les plus cliqués
transactions = Transaction.objects.filter(
created_at__gte=b.date_start,
created_at__lte=b.date_end,
valid=True,
amount__gt=0,
recurrenttransaction__template__pk__gte=-1)
d = {}
for t in transactions:
if t.recurrenttransaction.template.name in d:
d[t.recurrenttransaction.template.name] += t.quantity
else:
d[t.recurrenttransaction.template.name] = t.quantity
data['top3_buttons'] = list(sorted(d.items(), key=lambda item: item[1], reverse=True))[:3]
if verb >= 3:
print('class_conso_all')
# le classement des plus gros consommateurs (BDE + club)
transactions = Transaction.objects.filter(
created_at__gte=b.date_start,
created_at__lte=b.date_end,
valid=True,
source__noteuser__user__pk__gte=-1,
destination__noteclub__club__pk__gte=-1)
d = {}
for t in transactions:
if t.source in d:
d[t.source] += t.total
else:
d[t.source] = t.total
data['class_conso_all'] = dict(sorted(d.items(), key=lambda item: item[1], reverse=True))
if verb >= 3:
print('class_conso_bde')
# le classement des plus gros consommateurs BDE
transactions = Transaction.objects.filter(
created_at__gte=b.date_start,
created_at__lte=b.date_end,
valid=True,
source__noteuser__user__pk__gte=-1,
destination=5)
d = {}
for t in transactions:
if t.source in d:
d[t.source] += t.total
else:
d[t.source] = t.total
data['class_conso_bde'] = dict(sorted(d.items(), key=lambda item: item[1], reverse=True))
else:
# make your wrapped or reuse previous wrapped
raise NotImplementedError("The BDE: {bde_name} has not personalized wrapped, make it !"
.format(bde_name=b.name))
return data
def unique_data(self, bde, note, global_data=None, verb=1):
data = []
for i in range(len(bde)):
data_bde = []
if bde[i].name == 'Rave Part[list]':
if verb >= 3:
total = len(note[i])
current = 0
print('Make {nb} data for wrapped sponsored by {bde}'
.format(nb=total, bde=bde[i].name))
for n in note[i]:
d = {}
if 'user' in n.__dir__():
# première conso du mandat
transactions = Transaction.objects.filter(
valid=True,
recurrenttransaction__template__id__gte=-1,
created_at__gte=bde[i].date_start,
created_at__lte=bde[i].date_end,
source=n,
destination=5).order_by('created_at')
if transactions:
d['first_conso'] = transactions[0].template.name
else:
d['first_conso'] = ''
# Wei + bus
wei = WEIClub.objects.filter(
date_start__lte=bde[i].date_end,
date_end__gte=bde[i].date_start)
if not wei:
d['wei'] = ''
d['bus'] = ''
else:
w = wei[0]
memberships = Membership.objects.filter(club=w, user=n.user)
if not memberships:
d['wei'] = ''
d['bus'] = ''
else:
alias = []
for a in w.note.alias.iterator():
alias.append(str(a))
d['wei'] = alias[-1]
d['bus'] = memberships[0].weimembership.bus.name
# top3 conso
transactions = Transaction.objects.filter(
valid=True,
created_at__gte=bde[i].date_start,
created_at__lte=bde[i].date_end,
source=n,
amount__gt=0,
recurrenttransaction__template__id__gte=-1)
dt = {}
dc = {}
for t in transactions:
if t.template.name in dt:
dt[t.template.name] += t.quantity
else:
dt[t.template.name] = t.quantity
if t.template.category.name in dc:
dc[t.template.category.name] += t.quantity
else:
dc[t.template.category.name] = t.quantity
d['top3_conso'] = list(sorted(dt.items(), key=lambda item: item[1], reverse=True))[:3]
# catégorie de bouton préférée
if dc:
d['top_category'] = list(sorted(dc.items(), key=lambda item: item[1], reverse=True))[0][0]
else:
d['top_category'] = ''
# nombre de pot, et nombre d'entrée pot
pots = global_data['pots']
d['nb_pots'] = pots.count()
p = 0
for pot in pots:
if Entry.objects.filter(activity=pot, note=n):
p += 1
d['nb_pot_entry'] = p
# ton nombre de rechargement
d['nb_rechargement'] = Transaction.objects.filter(
valid=True,
created_at__gte=bde[i].date_start,
created_at__lte=bde[i].date_end,
destination=n,
source__pk__in=[1, 2, 3, 4]).count()
# ajout info globale spécifique user
# classement et montant conso all
d['class_part_all'] = len(global_data['class_conso_all'])
if n in global_data['class_conso_all']:
d['class_conso_all'] = list(global_data['class_conso_all']).index(n) + 1
d['amount_conso_all'] = global_data['class_conso_all'][n] / 100
else:
d['class_conso_all'] = 0
d['amount_conso_all'] = 0
# classement et montant conso bde
d['class_part_bde'] = len(global_data['class_conso_bde'])
if n in global_data['class_conso_bde']:
d['class_conso_bde'] = list(global_data['class_conso_bde']).index(n) + 1
d['amount_conso_bde'] = global_data['class_conso_bde'][n] / 100
else:
d['class_conso_bde'] = 0
d['amount_conso_bde'] = 0
if 'club' in n.__dir__():
# plus gros consommateur
transactions = Transaction.objects.filter(
valid=True,
created_at__lte=bde[i].date_end,
created_at__gte=bde[i].date_start,
destination=n,
source__noteuser__user__pk__gte=-1)
dt = {}
for t in transactions:
if t.source.user.username in dt:
dt[t.source.user.username] += t.total
else:
dt[t.source.user.username] = t.total
if dt:
d['big_consumer'] = list(sorted(dt.items(), key=lambda item: item[1], reverse=True))[0]
d['big_consumer'] = (d['big_consumer'][0], d['big_consumer'][1] / 100)
else:
d['big_consumer'] = ''
# plus gros créancier
transactions = Transaction.objects.filter(
valid=True,
created_at__lte=bde[i].date_end,
created_at__gte=bde[i].date_start,
source=n,
destination__noteuser__user__pk__gte=-1)
dt = {}
for t in transactions:
if t.destination.user.username in dt:
dt[t.destination.user.username] += t.total
else:
dt[t.destination.user.username] = t.total
if dt:
d['big_creancier'] = list(sorted(dt.items(), key=lambda item: item[1], reverse=True))[0]
d['big_creancier'] = (d['big_creancier'][0], d['big_creancier'][1] / 100)
else:
d['big_creancier'] = ''
# nb de soirée organisée
d['nb_soiree_orga'] = Activity.objects.filter(
valid=True,
date_start__lte=bde[i].date_end,
date_end__gte=bde[i].date_start,
organizer=n.club).count()
# nb de membres cumulé
d['nb_member'] = Membership.objects.filter(
date_start__lte=bde[i].date_end,
date_end__gte=bde[i].date_start,
club=n.club).distinct('user').count()
# ajout info globale
# top3 button
d['glob_top3_conso'] = global_data['top3_buttons']
# nb entree pot
d['glob_nb_entree_pot'] = global_data['nb_entree_pot']
# nb soiree
d['glob_nb_soiree'] = global_data['nb_soiree']
# nb vieux con
d['glob_nb_vieux_con'] = global_data['nb_vieux_con']
# nb transaction
d['glob_nb_transaction'] = global_data['nb_transaction']
data_bde.append(json.dumps(d))
if verb >= 3:
current += 1
print('\033[2K' + '({c}/{t})'.format(c=current, t=total) + '\033[1A')
else:
# make your wrapped or reuse previous wrapped
raise NotImplementedError("The BDE: {bde_name} has not personalized wrapped, make it !"
.format(bde_name=bde[i].name))
data.append(data_bde)
return data
def make_wrapped(self, unique_data, note, bde, change, create, verb=1):
if verb >= 3:
current = 0
total = 0
for n in note:
total += len(n)
print('\033[mMake {nb} wrapped'.format(nb=total))
for i in range(len(bde)):
for j in range(len(note[i])):
if create and not Wrapped.objects.filter(bde=bde[i], note=note[i][j]):
Wrapped(bde=bde[i],
note=note[i][j],
data_json=unique_data[i][j],
public=False,
generated=True).save()
elif change:
w = Wrapped.objects.get(bde=bde[i], note=note[i][j])
w.data_json = unique_data[i][j]
w.save()
if verb >= 3:
current += 1
print('\033[2K' + '({c}/{t})'.format(c=current, t=total) + '\033[1A')
return
def filter_note(self, bde, note, change, create, verb=1):
if change and create:
return list(note)
if change and not create:
note_new = []
for n in note:
if Wrapped.objects.filter(bde=bde, note=n):
note_new.append(n)
return note_new
if not change and create:
note_new = []
for n in note:
if not Wrapped.objects.filter(bde=bde, note=n):
note_new.append(n)
return note_new

View File

@ -0,0 +1,86 @@
# Generated by Django 4.2.15 on 2025-02-13 01:38
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")},
},
),
]

View File

80
apps/wrapped/models.py Normal file
View File

@ -0,0 +1,80 @@
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
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

View File

@ -0,0 +1,73 @@
:root {
--accent-primary: #FF0065;
--accent-secondary: #FFCB20;
}
@font-face {
font-family: "JEMROKtrial-Regular";
src: url("/static/wrapped/fonts/1/JEMROKtrial-Regular.ttf");
}
body {
font-family: "JEMROKtrial-Regular", sans-serif;
background: url("/static/wrapped/img/1/bg.png");
color: white;
text-align: center;
padding: 50px;
}
#name {
font-size: 2em;
font-weight: bold;
text-shadow: 2px 2px 15px var(--accent-secondary);
}
.wrap-container {
max-width: 500px;
margin: auto;
padding: 20px;
background: rgba(0, 0, 0, 0.8);
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
}
.category {
display: flex;
justify-content: space-between;
padding: 10px;
background: rgba(255, 255, 255, 0.2);
border-radius: 5px;
margin: 10px 0;
padding: 10px;
}
h1 {
font-size: 2.5em;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 2px;
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
-webkit-background-clip: text;
}
.list {
list-style: none;
padding: 0;
}
.list li {
display: flex;
justify-content: space-between;
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
margin: 10px 0;
padding: 10px;
border-radius: 5px;
font-weight: normal;
}
.ranking-bar {
width: 100%;
height: 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
overflow: hidden;
margin-top: 10px;
position: relative;
}
.ranking-progress {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
border-radius: 10px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/static/favicon/1/mstile-150x150.png"/>
<TileColor>#00a300</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,503 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="933.000000pt" height="933.000000pt" viewBox="0 0 933.000000 933.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,933.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M4285 8909 c-1611 -113 -3013 -1115 -3640 -2600 -322 -763 -413
-1617 -259 -2439 160 -853 598 -1670 1217 -2266 689 -665 1526 -1060 2497
-1181 206 -25 734 -25 940 0 539 67 996 204 1455 436 609 308 1121 746 1528
1304 368 504 642 1152 745 1762 47 277 57 397 57 730 0 335 -6 419 -51 695
-189 1175 -882 2234 -1884 2882 -772 499 -1702 741 -2605 677z m336 -175 c88
-16 130 -33 167 -67 29 -27 30 -110 2 -192 -30 -87 -35 -175 -16 -283 19 -107
45 -165 141 -312 97 -148 117 -198 123 -301 4 -84 3 -87 -24 -118 -39 -44
-100 -51 -336 -40 -255 12 -305 25 -371 98 -47 52 -68 124 -66 226 1 63 -1 80
-12 78 -8 -1 -51 -7 -97 -13 l-82 -12 2 -142 c3 -126 1 -146 -18 -187 -42 -91
-145 -159 -314 -210 -197 -58 -280 -8 -267 162 7 87 32 158 127 357 127 270
164 434 141 631 -15 123 -9 188 20 225 44 56 188 100 384 116 145 11 393 4
496 -16z m849 -107 c172 -44 281 -89 332 -138 39 -37 41 -40 34 -85 -3 -27
-27 -85 -56 -138 -59 -110 -74 -171 -90 -372 -15 -181 -9 -236 30 -276 32 -31
58 -35 98 -14 35 19 62 54 154 202 100 158 123 205 153 311 51 175 80 209 168
200 104 -10 335 -126 424 -213 67 -63 71 -96 17 -148 -44 -43 -81 -61 -192
-93 -114 -32 -167 -62 -235 -131 -78 -79 -120 -155 -168 -301 -47 -144 -74
-195 -129 -245 -89 -80 -176 -70 -506 57 -253 98 -339 173 -351 305 -7 74 14
158 66 260 61 122 75 180 68 292 -8 140 -32 201 -128 325 -63 81 -79 116 -79
169 0 35 5 48 23 61 36 27 202 15 367 -28z m-2409 -257 c92 -26 148 -98 156
-204 11 -135 -61 -297 -205 -459 -24 -27 -42 -50 -40 -52 2 -2 29 2 61 7 63
11 129 0 174 -29 29 -19 55 -68 78 -148 13 -49 26 -69 70 -110 154 -146 134
-194 -155 -372 -214 -132 -295 -164 -353 -140 -55 24 -71 70 -64 193 7 131 -9
207 -52 246 -17 16 -40 28 -51 28 -56 0 -183 -72 -280 -159 l-45 -40 25 -21
c14 -12 71 -44 127 -72 155 -78 243 -175 243 -268 0 -62 -43 -124 -191 -272
-175 -174 -263 -225 -347 -197 -59 19 -91 49 -122 114 -25 49 -33 85 -45 200
-18 164 -34 225 -83 318 -56 109 -110 161 -217 212 -101 48 -169 104 -180 148
-18 71 74 200 269 379 386 355 806 642 1012 692 71 17 165 20 215 6z m3941
-474 c91 -39 319 -242 542 -483 78 -84 151 -172 164 -195 46 -86 22 -175 -85
-308 -146 -180 -264 -199 -360 -57 -79 117 -56 264 48 313 24 12 63 42 87 68
52 57 55 91 12 134 -69 69 -185 48 -316 -57 -82 -65 -104 -135 -64 -209 33
-62 45 -72 91 -76 47 -4 69 -25 69 -64 0 -41 -49 -112 -77 -112 -8 0 -41 23
-75 51 -64 53 -104 76 -114 66 -3 -3 -10 -22 -14 -43 -21 -93 41 -237 127
-297 53 -36 91 -35 129 4 44 46 61 53 89 35 60 -40 72 -184 22 -282 -50 -98
-113 -130 -239 -121 -114 8 -195 49 -328 168 -58 52 -171 149 -252 216 -227
191 -260 238 -222 320 29 62 129 153 223 203 46 25 105 64 131 87 81 73 161
250 195 430 20 107 37 149 77 191 36 36 81 42 140 18z m-2454 -617 c3 -19 -23
-49 -43 -49 -18 0 -45 27 -45 45 0 27 83 31 88 4z m382 -79 c8 -14 8 -26 0
-40 -19 -35 -80 -20 -80 20 0 40 61 55 80 20z m409 -11 c42 -12 134 -44 205
-71 501 -195 938 -549 1235 -1002 83 -126 202 -359 252 -493 19 -51 37 -93 40
-93 12 0 58 98 90 193 18 53 37 97 43 97 6 0 47 -7 90 -15 117 -23 322 -29
463 -16 171 17 327 62 452 132 29 17 59 28 66 26 22 -9 107 -131 150 -217 142
-286 151 -529 30 -887 -19 -56 -40 -142 -46 -191 -11 -79 -10 -95 5 -142 25
-77 65 -130 140 -186 80 -59 80 -49 -7 -247 -147 -334 -310 -554 -479 -650
-131 -75 -197 -91 -363 -92 -164 0 -130 -18 -340 175 -200 183 -319 290 -325
290 -3 0 -13 -24 -23 -52 -34 -98 -165 -352 -244 -473 -416 -633 -1062 -1051
-1804 -1170 -151 -24 -382 -34 -554 -24 -465 28 -886 169 -1280 430 -92 61
-143 102 -161 129 -62 94 -135 369 -137 516 -1 68 3 89 24 129 32 64 69 90
124 89 61 -1 115 -37 221 -146 49 -52 119 -113 153 -136 107 -71 157 -53 182
66 14 69 4 119 -45 231 -33 75 -66 209 -66 269 0 22 -14 47 -49 89 -138 166
-233 361 -283 584 -31 138 -33 407 -4 538 104 479 427 851 879 1011 43 15 80
30 82 32 5 4 -17 96 -30 131 -9 23 10 22 43 -2 15 -10 54 -34 87 -52 l60 -33
202 -1 c164 0 204 2 208 13 3 8 2 63 -1 122 -8 128 -2 169 32 210 33 39 44 38
44 -5 0 -50 29 -118 88 -209 50 -75 52 -81 52 -148 0 -39 -7 -84 -15 -106 -25
-58 -34 -113 -28 -159 l5 -41 41 46 c69 80 87 131 87 250 0 56 3 102 8 102 17
0 114 -95 132 -130 26 -51 34 -127 21 -190 -6 -29 -10 -55 -7 -57 7 -7 125 87
150 120 25 33 49 104 40 119 -3 4 -23 8 -44 8 -98 0 -136 17 -217 98 -71 70
-77 74 -89 57 -8 -10 -14 -25 -14 -32 0 -24 -18 -14 -63 37 -62 69 -82 154
-53 224 27 65 76 106 191 162 122 58 230 131 240 163 9 28 -11 60 -41 67 -35
9 -119 -1 -179 -21 -27 -9 -117 -49 -200 -88 -175 -82 -220 -91 -289 -57 -152
73 -93 290 102 377 53 23 61 24 191 17 136 -7 136 -7 167 19 18 16 41 52 57
94 15 37 37 80 49 95 28 33 134 98 162 98 11 0 54 -10 95 -21z m-901 -195 c37
-26 23 -74 -23 -74 -43 0 -63 58 -27 79 23 14 24 14 50 -5z m-3516 -1078 c80
-44 254 -103 344 -116 99 -14 316 -12 479 5 197 20 182 23 199 -32 30 -102 67
-186 110 -253 27 -42 43 -76 39 -86 -14 -38 -131 125 -173 243 -13 35 -27 60
-32 57 -30 -19 -116 -190 -151 -300 -48 -152 -60 -243 -54 -419 5 -169 15
-238 74 -505 34 -156 36 -175 36 -355 1 -135 -4 -218 -16 -285 -22 -130 -61
-277 -91 -344 -14 -31 -22 -56 -18 -56 4 1 104 93 222 205 197 188 229 212
230 174 0 -6 -326 -312 -452 -422 -47 -42 -98 -81 -113 -87 -39 -15 -219 -12
-300 5 -38 8 -112 35 -164 61 -121 59 -227 159 -320 299 -77 117 -216 389
-251 493 l-25 73 35 27 c203 157 227 254 135 542 -103 320 -111 513 -33 745
43 126 185 365 218 365 5 0 38 -16 72 -34z m1564 -2071 c26 -39 -31 -86 -64
-53 -18 18 -15 55 6 67 23 14 42 10 58 -14z m434 -265 c8 -14 8 -26 0 -40 -19
-35 -80 -20 -80 20 0 40 61 55 80 20z m-392 -162 c4 -34 -27 -54 -58 -38 -21
11 -25 27 -14 55 5 11 16 15 38 13 25 -2 32 -8 34 -30z m168 -223 c11 -43 -34
-73 -64 -43 -13 13 -16 50 -5 61 3 4 19 7 34 7 22 0 30 -6 35 -25z m4258 -79
c42 -57 100 -125 128 -152 99 -89 238 -168 428 -244 190 -76 276 -126 364
-215 92 -92 128 -197 88 -256 -34 -50 -96 -119 -108 -119 -7 0 -28 22 -47 48
-55 77 -61 91 -48 119 16 35 -13 95 -80 162 -68 68 -126 106 -319 216 -192
109 -304 185 -398 271 -59 54 -77 65 -97 60 -18 -5 -28 0 -44 20 -36 46 -37
85 -1 142 17 29 37 52 44 52 8 0 48 -47 90 -104z m-4627 -91 c88 -132 97 -158
91 -246 -7 -87 -45 -167 -112 -234 -72 -72 -133 -98 -216 -93 -75 4 -100 19
-156 93 -19 25 -38 45 -42 45 -4 0 -33 -33 -66 -74 -83 -107 -238 -253 -292
-278 -39 -17 -50 -18 -75 -7 -38 15 -95 81 -110 127 -37 111 96 236 358 335
209 80 308 154 413 309 77 114 100 140 119 136 9 -2 48 -53 88 -113z m4568
-154 c89 -67 204 -184 220 -223 14 -35 14 -39 -5 -58 -24 -24 -69 -26 -113 -4
-40 21 -77 54 -77 70 0 13 -46 54 -61 54 -17 0 -8 -37 19 -81 64 -101 201
-197 362 -255 167 -59 279 -144 315 -237 24 -63 11 -107 -51 -176 -79 -88
-133 -102 -200 -53 -50 36 -133 139 -219 271 -77 117 -131 174 -257 269 -48
36 -85 68 -82 71 6 7 132 -81 195 -138 30 -27 98 -109 149 -181 52 -73 125
-166 163 -206 89 -96 129 -108 193 -59 43 33 39 40 -9 20 -20 -8 -45 -15 -56
-15 -33 0 -138 107 -229 233 -96 133 -213 253 -300 308 -63 39 -127 66 -116
47 5 -7 -1 -9 -20 -4 -22 5 -26 3 -26 -16 0 -24 26 -58 46 -58 16 0 73 -65 85
-98 20 -52 -11 -102 -64 -102 -98 0 -220 130 -241 255 -10 58 4 87 85 174 39
42 99 111 132 153 73 92 87 96 162 39z m-4283 -208 c64 -61 108 -124 115 -168
7 -46 -17 -113 -76 -204 -84 -131 -140 -252 -162 -347 -61 -265 -99 -371 -154
-429 -26 -28 -40 -35 -72 -35 -70 0 -179 86 -216 169 -38 87 5 218 110 332
l56 60 -23 24 c-13 14 -29 25 -35 25 -6 0 -55 -47 -109 -105 -54 -58 -115
-115 -137 -125 -81 -42 -154 -16 -185 65 -16 43 -16 47 1 92 37 96 120 161
345 271 193 93 259 148 363 305 93 139 101 142 179 70z m3835 -224 c72 -35
133 -128 133 -203 0 -36 -5 -48 -26 -65 -32 -25 -55 -26 -94 -6 -34 18 -45 35
-54 90 -8 44 -40 85 -67 85 -27 0 -59 -48 -59 -89 0 -48 42 -132 86 -171 45
-39 92 -40 189 -1 140 57 189 52 298 -30 77 -58 139 -132 176 -209 21 -46 26
-69 25 -135 0 -68 -4 -89 -29 -138 -36 -72 -131 -164 -210 -203 -160 -78 -292
-16 -437 205 -27 41 -47 82 -46 90 2 12 21 18 76 22 130 12 237 89 212 153
-18 49 -49 52 -136 13 -102 -45 -134 -51 -194 -37 -69 17 -130 73 -175 162
-62 125 -45 241 51 347 43 48 128 108 180 126 51 18 51 18 101 -6z m-3403
-138 c50 -32 129 -80 176 -106 153 -85 210 -162 210 -285 0 -117 -76 -226
-174 -251 l-45 -11 27 -40 c36 -56 42 -149 13 -232 -12 -33 -21 -82 -21 -110
0 -75 -19 -177 -39 -211 -34 -58 -115 -62 -230 -10 -79 37 -121 72 -142 120
-33 74 -6 145 96 253 65 70 115 152 115 192 0 40 -32 90 -76 120 -38 27 -64
37 -64 26 0 -46 -46 -282 -65 -332 -71 -188 -137 -228 -272 -165 -80 38 -147
101 -169 157 -40 105 44 246 222 375 159 114 218 207 259 405 25 121 44 164
72 164 9 0 57 -26 107 -59z m2891 -157 c26 -10 44 -56 61 -156 18 -106 63
-210 119 -275 20 -23 73 -69 119 -102 141 -102 212 -199 223 -305 10 -94 -39
-155 -174 -218 -81 -38 -156 -47 -201 -24 -72 37 -123 186 -139 404 -10 126
-12 137 -51 215 -22 45 -70 120 -105 167 -36 47 -70 102 -77 124 -11 34 -10
41 7 65 20 27 115 84 168 100 17 5 31 10 32 10 1 1 9 -2 18 -5z m-2249 -154
c107 -37 231 -70 384 -100 83 -16 161 -37 173 -45 45 -29 71 -93 75 -186 3
-51 0 -101 -7 -122 -17 -50 -73 -97 -115 -97 -68 0 -136 74 -136 148 0 19 6
35 14 38 21 8 27 31 16 60 -7 18 -16 25 -32 22 -48 -7 -99 -156 -99 -289 0
-47 9 -103 32 -181 61 -212 35 -383 -64 -435 -41 -21 -140 -21 -226 2 -160 41
-212 104 -198 238 9 89 44 163 133 279 105 139 119 177 119 325 0 104 -2 114
-23 135 -23 23 -23 23 -38 4 -17 -23 -18 -58 -3 -73 14 -14 -17 -88 -49 -113
-13 -11 -40 -20 -59 -20 -104 0 -147 101 -107 253 20 78 47 130 80 156 33 26
56 26 130 1z m1218 -43 c14 -54 -1 -87 -44 -101 -54 -17 -62 -43 -55 -176 9
-168 53 -559 76 -675 32 -159 70 -225 128 -225 45 0 61 -31 61 -116 0 -98 -6
-102 -163 -119 -185 -19 -230 -5 -268 81 -43 96 -45 360 -3 544 36 161 29 358
-22 565 -27 112 -31 202 -10 230 17 23 80 33 201 31 l90 -1 9 -38z m406 -57
c22 -22 22 -22 24 -200 2 -100 6 -136 23 -182 61 -163 189 -247 293 -193 35
18 41 38 38 120 l-3 60 36 3 c73 6 177 -107 220 -241 27 -84 21 -166 -17 -244
-35 -72 -70 -98 -182 -132 -231 -72 -428 -110 -497 -97 -52 10 -100 58 -122
119 -26 75 -23 227 5 334 41 151 24 263 -65 434 -36 70 -44 94 -41 130 4 53
29 70 133 92 98 21 131 21 155 -3z"/>
<path d="M4450 8721 c14 -4 54 -14 90 -20 36 -7 88 -23 116 -35 41 -19 54 -32
72 -70 12 -25 22 -57 22 -71 0 -40 16 -29 30 19 31 111 -57 174 -255 182 -65
3 -91 1 -75 -5z"/>
<path d="M3914 8686 c-144 -47 -171 -81 -160 -206 3 -42 12 -96 19 -121 l13
-44 12 108 c7 65 19 122 31 145 21 41 103 103 164 122 20 6 37 13 37 16 0 10
-50 1 -116 -20z"/>
<path d="M4157 8572 c-26 -28 -45 -82 -67 -191 -48 -235 -66 -471 -37 -471 8
0 58 7 113 15 54 8 104 15 111 15 17 0 53 115 60 190 9 109 -44 368 -89 433
-23 32 -66 37 -91 9z"/>
<path d="M4336 8308 c32 -150 3 -361 -53 -382 -9 -4 -58 -12 -109 -18 -127
-16 -122 -33 7 -23 l102 7 -9 -39 c-5 -21 -9 -72 -8 -114 0 -62 6 -87 28 -132
46 -95 129 -148 228 -147 33 1 33 1 -12 17 -69 24 -107 59 -142 131 -30 60
-33 75 -32 152 0 58 9 118 27 187 37 139 35 214 -10 358 -23 74 -33 76 -17 3z"/>
<path d="M3566 7668 c-31 -47 -78 -184 -83 -239 -7 -68 10 -124 42 -145 32
-21 104 -20 162 2 68 25 74 34 17 28 -66 -8 -130 10 -151 43 -27 41 -22 150
10 248 28 83 29 102 3 63z"/>
<path d="M5163 8646 c-17 -8 -36 -23 -42 -35 -25 -46 -9 -84 95 -216 57 -73
51 -41 -11 60 -48 78 -52 90 -42 121 9 26 48 44 119 53 l53 7 -50 8 c-27 4
-59 9 -70 12 -11 2 -34 -3 -52 -10z"/>
<path d="M6211 8246 c-12 -13 -37 -71 -55 -128 -41 -130 -64 -175 -176 -342
-88 -132 -136 -184 -182 -198 -20 -5 -19 -6 8 -7 81 -3 211 152 331 394 22 44
46 96 53 115 21 55 59 101 98 117 31 13 46 13 128 -1 101 -17 183 -46 229 -80
39 -29 55 -38 55 -32 0 8 -111 81 -167 110 -79 39 -200 76 -253 76 -36 0 -51
-5 -69 -24z"/>
<path d="M5263 7797 c-63 -150 -77 -200 -69 -250 15 -92 64 -145 196 -212 91
-45 126 -54 68 -16 -68 43 -168 149 -191 200 -25 56 -17 148 23 293 16 57 28
104 26 105 -1 1 -25 -53 -53 -120z"/>
<path d="M2820 8326 c-74 -26 -126 -52 -223 -109 -77 -45 -97 -66 -40 -41 193
83 294 114 373 114 74 0 145 -26 174 -63 l26 -32 0 35 c0 52 -32 97 -77 109
-62 18 -161 12 -233 -13z"/>
<path d="M2400 7949 c-100 -41 -150 -172 -150 -395 0 -121 22 -279 41 -299 7
-7 40 20 97 80 133 138 189 270 180 430 -5 87 -27 147 -63 171 -30 19 -76 25
-105 13z"/>
<path d="M2562 7933 c33 -64 42 -98 46 -164 7 -135 -36 -256 -135 -379 -34
-41 -59 -77 -57 -79 2 -2 18 8 37 22 139 106 207 240 207 410 0 87 -15 134
-56 172 -25 24 -50 34 -42 18z"/>
<path d="M1698 7492 c-106 -109 -113 -211 -20 -276 20 -14 74 -39 119 -56 46
-17 99 -43 119 -56 20 -14 38 -23 40 -21 6 6 -91 98 -166 157 -36 28 -73 66
-83 83 -26 45 -18 115 18 167 15 22 26 42 23 45 -2 2 -25 -17 -50 -43z"/>
<path d="M2710 7365 c0 -3 15 -19 34 -36 51 -44 66 -100 66 -243 0 -102 3
-126 19 -153 35 -57 90 -56 184 3 32 20 57 38 55 40 -2 2 -32 -4 -66 -14 -112
-30 -146 3 -136 129 11 127 -29 224 -108 265 -28 14 -48 18 -48 9z"/>
<path d="M2086 6674 c10 -194 22 -236 83 -287 76 -64 161 -39 279 81 99 102
110 125 32 72 -130 -88 -213 -109 -277 -70 -41 24 -45 33 -81 169 -17 62 -33
116 -36 118 -3 3 -3 -34 0 -83z"/>
<path d="M6902 7869 c-19 -6 -40 -22 -51 -39 -18 -31 -57 -208 -47 -217 3 -4
19 25 36 63 62 143 59 139 117 142 64 4 123 -24 209 -97 63 -54 93 -68 53 -25
-32 36 -164 141 -197 158 -40 20 -83 26 -120 15z"/>
<path d="M7470 7455 c0 -3 31 -42 70 -88 38 -45 80 -102 92 -127 28 -56 33
-139 14 -210 -8 -30 -13 -56 -11 -58 1 -1 17 19 34 47 20 30 34 67 37 97 7 58
-1 72 -138 237 -84 100 -98 115 -98 102z"/>
<path d="M6560 7189 c-14 -11 -56 -36 -95 -56 -77 -40 -190 -144 -201 -187 -3
-14 -3 -38 1 -53 8 -33 85 -108 146 -142 l43 -25 -22 30 c-105 136 -98 179 52
334 107 111 135 147 76 99z"/>
<path d="M6980 7095 c0 -28 6 -60 15 -71 19 -28 75 -54 114 -54 27 0 32 -4 38
-31 7 -38 23 -19 23 29 0 31 -1 32 -43 32 -49 0 -78 23 -121 100 l-26 45 0
-50z"/>
<path d="M6850 6833 c0 -194 171 -375 302 -320 19 8 44 27 56 42 28 35 54 29
69 -15 l12 -35 0 40 c1 88 -47 120 -96 64 -15 -17 -39 -35 -51 -40 -34 -13
-96 6 -137 43 -40 34 -97 132 -122 204 -23 71 -33 76 -33 17z"/>
<path d="M4656 6262 c-14 -28 -16 -56 -14 -177 l3 -145 32 -6 c36 -7 50 -31
30 -51 -7 -7 -33 -19 -57 -26 -42 -11 -47 -17 -80 -81 -30 -60 -34 -79 -34
-140 1 -44 8 -93 21 -129 22 -64 14 -89 -10 -29 -29 74 -40 153 -27 212 6 30
14 67 17 82 7 39 -15 44 -76 17 -48 -20 -51 -24 -52 -58 0 -20 -4 -44 -8 -54
-4 -12 7 -45 31 -95 21 -42 41 -99 44 -127 8 -63 -8 -51 -25 19 -17 68 -67
165 -106 203 l-33 32 -39 -29 c-39 -29 -40 -32 -47 -103 -7 -81 1 -115 47
-189 16 -27 27 -54 25 -60 -3 -7 -17 6 -32 30 -25 40 -38 68 -65 135 -12 31
-58 54 -81 39 -8 -5 -19 -38 -25 -76 -9 -55 -9 -72 4 -98 13 -27 73 -80 104
-91 7 -2 9 -8 5 -12 -12 -12 -98 44 -119 78 -10 18 -24 50 -30 72 l-11 40 -22
-27 c-11 -15 -33 -49 -48 -76 l-28 -49 28 -54 c20 -40 38 -60 70 -77 38 -21
55 -42 34 -42 -22 0 -101 64 -118 95 -22 42 -31 44 -40 8 -10 -40 41 -133 83
-153 40 -18 43 -35 6 -25 -38 9 -50 21 -83 79 l-29 50 -11 -30 c-16 -41 -50
-185 -50 -211 0 -23 45 -56 93 -67 15 -4 25 -11 22 -16 -6 -10 -58 3 -91 25
-12 8 -25 11 -27 7 -8 -12 1 -95 12 -124 9 -24 16 -28 51 -28 25 0 39 -4 35
-10 -3 -5 -19 -10 -36 -10 -17 0 -29 -5 -29 -12 0 -7 20 -44 44 -82 l43 -69 6
119 c6 142 22 209 81 329 96 194 252 330 461 401 115 39 295 45 415 15 274
-68 483 -268 570 -546 34 -110 39 -278 11 -398 -66 -287 -275 -508 -558 -589
-113 -33 -306 -32 -413 1 -100 31 -211 90 -287 152 -66 54 -157 166 -192 237
l-22 43 -30 -26 c-17 -14 -41 -28 -55 -31 -13 -3 -24 -8 -24 -9 0 -2 9 -24 20
-49 l19 -46 37 15 c21 8 43 20 50 26 8 7 14 7 18 0 4 -6 -16 -22 -43 -36 l-50
-25 20 -35 c24 -40 54 -45 122 -21 20 8 37 9 37 4 0 -12 -63 -38 -95 -38 -14
0 -25 -6 -25 -12 0 -7 24 -44 53 -83 l53 -70 49 3 c35 1 65 11 99 33 27 16 51
27 55 24 8 -8 -34 -40 -87 -66 -64 -32 -68 -49 -23 -93 37 -36 40 -37 87 -31
70 11 81 19 96 72 7 25 20 49 27 51 10 3 12 0 6 -14 -4 -11 -13 -41 -21 -69
-9 -35 -24 -59 -49 -82 l-37 -32 69 -45 c37 -25 73 -46 79 -46 7 0 34 17 61
38 52 41 97 114 107 175 4 20 11 37 16 37 11 0 4 -48 -15 -105 -7 -22 -16 -60
-19 -83 -4 -24 -15 -61 -26 -82 l-19 -38 37 -15 c36 -14 38 -14 79 15 50 36
52 41 63 137 6 53 4 86 -5 118 -15 51 -3 67 16 21 17 -41 14 -149 -6 -234 -24
-102 -19 -109 83 -128 47 -9 88 -14 90 -11 3 3 0 16 -6 30 -19 41 -14 85 19
191 30 96 33 149 14 197 -4 9 -3 17 3 17 24 0 35 -70 22 -145 -12 -70 -12 -75
10 -107 46 -66 99 -113 84 -73 -10 25 17 114 43 145 25 30 25 30 8 86 -9 32
-27 73 -40 93 -27 39 -25 53 4 27 40 -36 63 -113 67 -225 3 -58 9 -120 14
-137 13 -40 74 -99 125 -120 44 -18 95 -34 95 -30 0 2 -15 19 -33 38 -33 35
-57 94 -57 139 0 14 9 57 20 97 37 135 20 239 -56 339 -18 23 -21 33 -11 33
34 0 107 -148 107 -218 0 -21 7 -48 16 -60 18 -25 74 -56 121 -67 l33 -7 -15
43 c-9 24 -15 77 -15 127 0 79 -2 89 -31 134 -31 48 -105 98 -144 98 -8 0 -15
5 -15 10 0 28 105 -13 146 -57 14 -15 45 -63 69 -108 49 -89 88 -136 131 -158
16 -8 71 -17 126 -20 53 -3 106 -10 117 -16 25 -14 26 -9 7 27 -19 36 -63 76
-111 100 -76 38 -99 77 -134 227 -33 136 -66 177 -169 204 -55 15 -46 32 10
18 74 -18 114 -51 145 -117 23 -49 35 -62 66 -75 39 -16 94 -19 146 -9 36 7
40 20 10 29 -52 17 -116 97 -133 168 -9 37 -62 84 -110 98 -23 6 -63 8 -95 4
-44 -5 -53 -4 -44 6 13 13 114 18 154 7 15 -4 62 -37 105 -73 121 -100 166
-105 310 -34 59 29 96 41 133 42 28 1 51 4 51 7 0 9 -70 61 -100 74 -21 8 -47
10 -87 4 -32 -5 -81 -11 -110 -14 l-52 -6 -58 55 c-32 30 -76 64 -98 75 -44
21 -129 42 -175 42 -17 0 -30 5 -30 11 0 19 128 0 195 -28 33 -14 70 -25 82
-25 41 0 153 99 153 135 0 3 -18 0 -40 -5 -55 -14 -95 -4 -170 42 -36 22 -75
40 -88 40 -12 0 -46 -16 -74 -35 -29 -19 -54 -32 -57 -29 -10 10 50 54 114 85
54 26 70 29 162 29 144 0 173 14 239 117 18 29 52 66 75 83 66 47 20 36 -92
-22 -111 -58 -99 -57 -249 -14 -86 25 -196 21 -255 -9 -27 -13 -51 -22 -53
-20 -11 11 24 35 71 51 29 9 61 25 73 36 12 10 63 30 115 44 52 14 104 31 116
39 22 15 43 69 43 113 0 26 -2 26 -38 8 -15 -7 -47 -17 -72 -20 -25 -4 -67
-13 -95 -21 -27 -8 -77 -22 -109 -31 -44 -11 -69 -25 -93 -52 -18 -20 -33 -42
-33 -49 0 -7 -4 -13 -10 -13 -33 0 21 84 70 109 19 10 71 28 115 40 142 39
177 74 206 208 19 88 58 184 86 215 15 17 14 18 -8 18 -80 0 -154 -53 -226
-162 -30 -45 -60 -77 -88 -93 -38 -23 -65 -32 -186 -65 -20 -5 -57 -26 -82
-45 -54 -41 -64 -32 -14 12 18 16 42 44 54 63 12 20 37 46 55 60 75 55 118
102 124 137 7 35 -10 114 -24 112 -4 0 -21 -19 -37 -42 -40 -55 -108 -100
-186 -123 -56 -17 -64 -23 -80 -59 -11 -22 -22 -57 -25 -77 -7 -39 -24 -53
-24 -20 0 28 37 129 62 171 12 20 52 68 89 107 93 98 104 122 102 216 -1 42
-9 100 -17 129 -18 60 -20 107 -6 143 12 33 2 33 -47 -3 -47 -34 -73 -90 -73
-160 0 -59 -20 -130 -50 -171 -12 -17 -53 -54 -91 -81 -92 -67 -137 -122 -157
-191 -19 -64 -38 -79 -27 -21 6 35 18 60 64 141 11 19 25 65 32 103 18 100 2
162 -58 229 -24 27 -49 50 -53 50 -4 0 -10 -42 -12 -92 -4 -107 -23 -155 -95
-239 -48 -56 -49 -61 -28 -139 18 -66 17 -60 5 -60 -13 0 -38 88 -51 185 -10
78 -9 86 15 157 17 48 26 96 26 134 0 52 -5 69 -36 120 -70 112 -86 145 -98
192 l-13 47 -17 -33z"/>
<path d="M4060 6014 c0 -2 5 -25 10 -52 l11 -48 54 11 c29 5 56 13 59 15 2 3
-27 21 -65 41 -38 20 -69 35 -69 33z"/>
<path d="M5650 5960 c-19 -5 -70 -14 -112 -21 l-76 -13 1 -97 2 -97 -42 -62
c-24 -34 -43 -72 -43 -85 0 -29 -24 -49 -50 -42 -13 3 -25 -2 -35 -16 -24 -35
-18 -37 44 -16 72 25 143 84 175 147 l23 46 16 -25 c9 -13 21 -45 28 -71 19
-71 2 -116 -68 -175 -113 -95 -138 -127 -82 -104 13 5 57 19 97 31 l72 21 0
47 c0 67 34 130 90 167 27 18 93 44 160 63 183 52 224 79 237 155 7 45 -30 96
-92 127 -44 22 -64 25 -180 27 -71 1 -146 -2 -165 -7z"/>
<path d="M4315 5909 c-520 -53 -966 -396 -1139 -877 -73 -203 -91 -472 -47
-681 52 -243 173 -471 345 -646 196 -199 467 -342 741 -389 119 -21 353 -21
474 0 110 19 197 45 194 59 -1 6 -23 13 -48 17 -275 40 -649 307 -843 602 -64
98 -132 241 -132 281 0 11 10 15 35 15 45 0 92 23 100 49 11 36 -14 98 -75
183 -33 45 -75 112 -93 148 -31 63 -32 68 -31 185 1 86 7 142 22 197 74 275
235 506 467 670 67 48 221 129 295 155 21 8 31 16 25 22 -15 15 -189 21 -290
10z m194 -23 c-2 -2 -38 -22 -79 -43 -405 -209 -664 -603 -665 -1009 0 -96 2
-104 37 -175 21 -41 64 -112 97 -158 62 -85 83 -143 62 -164 -6 -6 -42 -15
-79 -20 -37 -4 -86 -10 -109 -13 -52 -7 -66 13 -40 58 21 36 21 48 3 48 -15 0
-46 -58 -46 -87 0 -37 34 -56 88 -51 l49 5 18 -55 c9 -30 34 -88 55 -128 l38
-74 -42 0 c-29 0 -65 11 -107 33 -61 31 -64 33 -61 66 5 42 -17 39 -31 -5 -5
-16 -20 -40 -33 -55 -13 -14 -24 -28 -24 -32 0 -14 24 -7 45 13 l22 20 62 -32
c46 -24 80 -33 127 -36 l64 -4 45 -63 c164 -231 436 -436 695 -526 l95 -33
-55 -13 c-343 -76 -732 -3 -1030 193 -460 303 -676 860 -535 1382 142 526 610
908 1170 955 64 6 169 7 164 3z"/>
<path d="M3481 5234 c-50 -25 -71 -47 -57 -61 3 -3 23 6 44 20 74 52 151 48
207 -10 19 -20 38 -33 42 -29 13 13 -22 60 -62 83 -54 30 -108 29 -174 -3z"/>
<path d="M3630 5144 c0 -3 11 -20 25 -38 24 -32 50 -129 39 -147 -3 -5 -15 -7
-28 -3 -27 6 -74 -11 -103 -38 l-22 -21 -31 27 c-31 25 -70 35 -70 17 0 -5 15
-18 34 -29 38 -23 122 -120 162 -188 15 -25 30 -42 35 -37 7 7 22 80 49 243
12 71 -8 157 -48 203 -14 16 -42 24 -42 11z m48 -222 c19 -13 4 -67 -28 -97
l-27 -26 -31 37 c-30 36 -31 39 -14 57 28 32 74 45 100 29z"/>
<path d="M3406 4541 c-4 -7 6 -32 23 -59 82 -128 130 -278 134 -412 3 -107 21
-109 25 -1 4 107 -23 214 -83 336 -51 103 -88 155 -99 136z"/>
<path d="M8240 5861 c-66 -35 -213 -81 -304 -96 -109 -18 -448 -20 -579 -4
-49 6 -91 8 -94 6 -3 -3 8 -25 25 -49 79 -115 152 -309 177 -473 13 -80 16
-147 12 -269 -4 -142 -9 -179 -41 -316 -21 -85 -45 -186 -53 -225 -24 -101
-24 -476 -1 -597 30 -154 60 -248 112 -352 l51 -101 85 -3 c103 -4 224 12 298
40 137 51 324 212 427 368 58 89 139 246 184 359 l32 82 -69 67 c-78 76 -117
148 -127 232 -7 60 23 208 71 343 108 311 80 633 -77 892 -79 129 -73 125
-129 96z m76 -75 c64 -64 29 -136 -47 -96 -20 11 -49 60 -49 84 0 19 27 46 47
46 8 0 30 -15 49 -34z m-855 -115 c41 -41 49 -70 24 -91 -36 -30 -125 33 -125
88 0 35 6 42 38 42 15 0 39 -15 63 -39z m544 -106 c99 -28 219 -124 263 -212
72 -141 1 -334 -158 -435 -66 -42 -111 -51 -177 -38 -47 10 -65 21 -108 64
-84 84 -122 218 -112 398 8 148 43 209 134 234 50 13 75 12 158 -11z m212
-760 c72 -30 113 -86 113 -153 0 -49 -68 -395 -109 -557 -62 -242 -110 -332
-220 -408 -42 -29 -53 -32 -121 -32 -64 0 -85 5 -140 32 -134 66 -238 177
-292 312 -20 49 -23 74 -23 191 1 150 14 201 77 308 100 166 294 300 467 322
87 11 202 4 248 -15z m271 -557 c17 -17 15 -79 -4 -106 -21 -30 -61 -29 -75 2
-14 30 -5 70 21 96 23 23 41 25 58 8z m-898 -635 c31 -21 48 -41 61 -73 40
-97 -89 -92 -136 6 -32 68 14 109 75 67z"/>
<path d="M7860 5458 c-48 -32 -64 -70 -68 -159 -6 -115 42 -253 110 -316 36
-33 98 -39 154 -13 97 42 139 92 164 193 34 134 -103 290 -275 312 -43 6 -55
3 -85 -17z"/>
<path d="M7980 4739 c-171 -49 -284 -126 -386 -262 -58 -79 -84 -163 -84 -278
0 -176 81 -321 217 -389 37 -19 64 -24 119 -24 63 0 76 3 123 33 62 40 86 70
139 176 49 100 95 248 132 425 39 190 26 245 -69 298 -48 27 -137 37 -191 21z
m45 -203 c27 -12 36 -23 46 -58 31 -115 -67 -339 -172 -394 -79 -40 -166 3
-194 99 -19 64 -19 81 1 147 12 39 30 69 72 113 92 96 169 125 247 93z"/>
<path d="M4550 5820 c0 -5 5 -10 10 -10 6 0 10 5 10 10 0 6 -4 10 -10 10 -5 0
-10 -4 -10 -10z"/>
<path d="M4358 5736 c-23 -17 -23 -29 1 -50 17 -15 19 -15 25 -1 8 23 8 65 -1
65 -5 0 -16 -7 -25 -14z"/>
<path d="M7205 5720 c-9 -45 -65 -166 -98 -215 -24 -35 -25 -40 -12 -76 22
-61 63 -254 81 -384 12 -84 17 -185 17 -340 0 -292 -33 -521 -111 -772 l-29
-89 195 -185 c107 -101 198 -185 203 -187 5 -2 -2 20 -15 50 -56 127 -93 312
-105 533 -11 194 -1 306 42 490 104 438 88 759 -55 1051 -21 45 -54 99 -72
120 l-33 39 -8 -35z"/>
<path d="M4170 5590 l-35 -29 28 -10 c43 -17 47 -14 47 29 0 22 -1 40 -2 40
-2 0 -19 -14 -38 -30z"/>
<path d="M4065 5470 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
-8 -4 -11 -10z"/>
<path d="M4610 5384 c-232 -44 -437 -200 -541 -409 -62 -125 -82 -225 -76
-364 15 -328 211 -584 527 -691 89 -30 275 -38 380 -16 113 23 261 101 348
181 159 148 241 335 242 551 0 217 -70 387 -224 539 -87 86 -192 149 -310 187
-60 19 -285 33 -346 22z m319 -45 c119 -30 223 -91 321 -189 144 -143 213
-309 213 -510 0 -130 -18 -204 -80 -330 -88 -179 -260 -318 -460 -371 -107
-29 -259 -29 -366 0 -204 54 -376 196 -467 386 -52 109 -70 190 -70 315 0 125
18 206 70 315 71 148 186 263 335 335 166 80 321 95 504 49z"/>
<path d="M4320 4910 c-89 -41 -160 -140 -160 -222 0 -34 3 -38 38 -47 20 -6
119 -11 220 -11 104 0 182 -4 182 -9 0 -5 -7 -37 -16 -71 -30 -119 -1 -163
113 -174 76 -7 180 7 200 27 31 31 30 128 -2 210 -7 16 6 17 191 17 236 0 250
4 247 68 -2 57 -49 139 -101 179 -146 111 -345 39 -398 -145 -16 -53 -16 -57
6 -110 13 -30 25 -77 28 -103 4 -44 1 -51 -27 -80 -31 -30 -34 -31 -103 -27
-59 3 -73 8 -89 27 -25 31 -25 96 1 161 26 66 25 114 -4 175 -44 96 -118 146
-221 152 -45 3 -71 -2 -105 -17z"/>
<path d="M4976 4334 c-3 -9 -6 -26 -6 -39 0 -13 -10 -31 -22 -40 -57 -40 -256
-77 -307 -58 -14 6 -28 23 -35 43 -18 55 -36 50 -29 -9 4 -38 2 -57 -11 -75
-20 -31 -20 -46 -2 -46 8 0 20 13 27 28 12 27 14 28 98 28 90 0 187 21 254 53
36 18 38 18 47 1 11 -20 47 -36 58 -26 3 4 -7 19 -23 36 -24 25 -27 35 -23 74
4 33 2 46 -7 46 -7 0 -16 -7 -19 -16z"/>
<path d="M5890 5289 c-24 -16 -49 -29 -56 -29 -6 0 -18 -8 -25 -17 -13 -15
-11 -16 20 -10 19 4 49 18 66 32 18 13 35 22 37 20 9 -9 -11 -135 -26 -163
-18 -35 -58 -55 -152 -77 -40 -9 -80 -23 -90 -31 -18 -15 -18 -15 6 -9 14 3
59 15 101 25 101 25 126 38 149 75 20 33 46 176 36 201 -7 19 -14 17 -66 -17z"/>
<path d="M5720 4771 c0 -5 33 -29 73 -51 63 -36 78 -40 119 -36 26 3 58 12 72
21 33 22 39 6 15 -42 -24 -49 -77 -103 -124 -129 -19 -10 -35 -22 -35 -27 0
-13 58 24 112 72 54 48 73 78 83 133 8 44 -8 58 -38 30 -68 -61 -131 -55 -252
24 -14 9 -25 12 -25 5z"/>
<path d="M5780 4285 c0 -3 6 -20 14 -38 19 -47 74 -95 128 -115 27 -9 48 -20
48 -24 0 -4 -25 -15 -56 -24 -55 -16 -139 -15 -191 1 -13 4 -23 3 -23 -3 0
-17 88 -35 151 -29 66 6 154 44 154 67 0 9 -14 17 -37 21 -60 9 -129 58 -153
107 -17 36 -35 55 -35 37z"/>
<path d="M5537 3858 c-9 -33 8 -129 28 -168 9 -18 14 -35 10 -38 -11 -12 -119
18 -157 43 -29 19 -38 22 -38 10 0 -21 62 -50 149 -69 81 -19 108 -11 78 22
-28 31 -47 89 -47 144 0 62 -14 94 -23 56z"/>
<path d="M4165 3851 c-6 -11 9 -23 19 -14 9 9 7 23 -3 23 -6 0 -12 -4 -16 -9z"/>
<path d="M4301 3721 c-11 -7 -10 -11 7 -20 16 -9 25 -7 44 9 l23 19 -30 1
c-16 0 -36 -4 -44 -9z"/>
<path d="M5135 3640 c-14 -26 -19 -52 -18 -103 l1 -67 -39 31 c-21 17 -53 51
-71 75 -36 51 -44 46 -54 -30 l-7 -46 51 0 c45 0 59 -5 104 -40 38 -29 54 -36
56 -26 2 8 -2 24 -7 35 -14 25 -14 64 0 78 5 5 9 37 7 69 l-3 59 -20 -35z"/>
<path d="M4585 3605 c-29 -66 -31 -83 -11 -65 17 13 51 108 42 117 -3 3 -17
-20 -31 -52z"/>
<path d="M4710 3500 c-19 -15 -21 -20 -8 -20 9 0 24 7 32 16 31 30 13 33 -24
4z"/>
<path d="M4913 3338 c-29 -11 -53 -25 -53 -30 0 -5 10 -28 22 -51 11 -23 24
-67 27 -97 l7 -55 31 49 c49 74 69 199 33 202 -8 1 -39 -7 -67 -18z"/>
<path d="M4080 3287 c0 -6 11 -30 25 -53 34 -57 102 -99 149 -90 50 10 86 42
86 77 l0 29 -63 -35 c-85 -47 -118 -47 -82 1 28 37 19 49 -50 66 -41 10 -65
11 -65 5z"/>
<path d="M4229 3243 c-5 -13 -9 -26 -9 -28 0 -8 37 7 59 24 l24 18 -32 5 c-28
4 -34 1 -42 -19z"/>
<path d="M796 5833 c-45 -62 -118 -210 -144 -293 -31 -98 -42 -172 -42 -291 0
-129 18 -227 70 -390 54 -168 74 -267 65 -335 -10 -82 -56 -164 -131 -232
l-64 -59 15 -44 c32 -90 135 -289 203 -390 198 -295 449 -437 729 -414 l82 7
39 81 c52 106 97 242 119 359 13 72 17 143 17 313 0 236 3 214 -70 515 -37
152 -38 159 -38 355 -1 224 7 284 61 439 38 110 80 198 125 264 17 24 28 46
25 48 -3 3 -49 0 -104 -6 -54 -7 -188 -12 -298 -13 -168 0 -216 3 -301 21
-102 22 -203 56 -274 92 -22 11 -42 20 -45 20 -2 0 -20 -21 -39 -47z m88 -29
c9 -8 16 -22 16 -30 0 -24 -29 -73 -49 -84 -76 -40 -111 32 -47 96 38 38 57
42 80 18z m874 -132 c3 -25 -3 -39 -26 -62 -35 -35 -76 -47 -97 -30 -25 21
-17 50 25 92 30 30 45 39 67 36 23 -2 29 -8 31 -36z m-482 -97 c54 -16 80 -39
110 -95 16 -29 19 -57 19 -175 0 -196 -30 -291 -118 -372 -98 -90 -233 -74
-350 44 -80 79 -123 192 -111 288 16 133 151 264 314 307 81 21 76 21 136 3z
m-23 -780 c103 -35 173 -81 263 -171 97 -96 155 -202 181 -329 19 -94 9 -201
-27 -297 -88 -235 -361 -407 -523 -329 -95 46 -176 167 -226 336 -50 170 -131
571 -131 648 0 84 60 145 162 167 69 15 224 2 301 -25z m-563 -555 c39 -39 35
-111 -6 -127 -27 -10 -64 40 -64 86 0 59 32 79 70 41z m920 -625 c26 -31 -19
-117 -73 -140 -65 -27 -95 16 -57 83 35 62 101 91 130 57z"/>
<path d="M1145 5471 c-97 -24 -189 -91 -225 -166 -72 -149 54 -350 219 -347
55 2 84 20 121 80 94 149 93 364 -2 422 -34 21 -61 23 -113 11z"/>
<path d="M986 4736 c-86 -32 -126 -86 -126 -169 0 -120 89 -445 161 -589 44
-88 70 -120 130 -159 45 -29 60 -33 118 -33 51 -1 77 5 115 24 62 32 147 126
189 210 28 57 32 76 35 162 3 78 0 112 -17 167 -25 81 -91 176 -172 247 -128
111 -333 177 -433 140z m251 -205 c64 -30 132 -98 164 -166 81 -170 -23 -348
-169 -290 -97 39 -192 218 -192 362 0 47 4 59 29 84 25 24 38 29 78 29 26 0
67 -9 90 -19z"/>
<path d="M6831 3169 c-27 -43 -27 -100 -1 -130 22 -27 24 -20 10 32 -8 27 -6
46 5 79 19 54 12 63 -14 19z"/>
<path d="M7575 2578 c70 -52 169 -156 191 -201 19 -36 23 -56 18 -82 -5 -29
-1 -42 20 -70 15 -19 36 -40 47 -45 20 -11 20 -11 0 29 -12 22 -21 53 -21 69
0 62 -32 124 -103 198 -71 75 -112 107 -157 118 -24 7 -24 6 5 -16z"/>
<path d="M2191 3077 c-26 -43 -43 -87 -32 -87 5 0 24 20 42 45 36 47 47 53 67
33 15 -15 16 -3 2 23 -17 31 -55 24 -79 -14z"/>
<path d="M2141 2810 c-68 -54 -126 -101 -129 -104 -11 -11 32 -46 56 -46 87 0
232 144 232 230 0 37 -34 21 -159 -80z"/>
<path d="M2295 2825 c-27 -56 -126 -154 -164 -161 -17 -4 -31 -11 -31 -16 0
-13 64 -1 100 17 44 23 108 100 115 139 11 54 0 65 -20 21z"/>
<path d="M1629 2646 c-78 -32 -125 -62 -187 -120 -81 -76 -100 -128 -68 -185
13 -24 14 -24 21 17 15 93 134 225 248 278 53 23 68 34 50 34 -5 0 -33 -11
-64 -24z"/>
<path d="M6770 2840 c-36 -46 -32 -51 12 -15 35 30 40 30 98 4 l45 -20 -34 35
c-48 49 -80 48 -121 -4z"/>
<path d="M6898 2704 c12 -8 28 -30 36 -48 10 -23 28 -40 60 -55 25 -13 51 -21
58 -19 7 3 -6 13 -27 24 -24 11 -46 31 -54 49 -21 46 -42 65 -70 65 l-25 0 22
-16z"/>
<path d="M6581 2625 c-45 -49 -50 -76 -28 -148 11 -34 74 -126 88 -127 3 0
-13 36 -34 79 -39 78 -39 80 -27 130 6 28 21 62 32 76 35 45 13 37 -31 -10z"/>
<path d="M2500 2650 c-24 -46 -19 -50 22 -15 23 19 34 23 48 15 35 -19 45 -9
17 15 -41 36 -63 32 -87 -15z"/>
<path d="M2494 2468 c-32 -40 -108 -124 -168 -186 l-110 -113 35 -35 36 -35
51 43 c69 58 122 130 182 250 47 93 62 148 41 148 -5 0 -35 -33 -67 -72z"/>
<path d="M2455 2253 c-35 -56 -142 -163 -163 -163 -8 0 -32 14 -54 32 -29 23
-38 26 -30 12 5 -10 20 -28 31 -38 12 -11 21 -24 21 -30 0 -6 -29 -40 -65 -76
-85 -86 -135 -181 -135 -257 0 -65 18 -108 66 -154 l35 -34 -30 59 c-24 45
-31 71 -31 115 0 78 33 131 176 282 111 117 193 226 208 277 11 33 4 27 -29
-25z"/>
<path d="M2000 2226 c-126 -63 -217 -133 -246 -187 -31 -61 -10 -135 47 -165
34 -17 36 -11 9 23 -25 32 -25 66 -1 119 28 61 72 105 171 174 117 80 121 87
20 36z"/>
<path d="M6239 2366 c-80 -59 -139 -174 -139 -270 0 -58 36 -144 78 -189 65
-69 163 -91 256 -58 80 28 90 40 24 30 -144 -24 -273 56 -308 191 -24 89 8
184 92 276 28 30 48 54 47 54 -2 0 -25 -16 -50 -34z"/>
<path d="M6380 2342 c0 -4 5 -13 11 -19 6 -6 19 -35 29 -64 10 -32 27 -61 43
-73 23 -17 30 -18 54 -6 15 7 24 14 18 15 -5 2 -22 6 -37 9 -20 5 -29 15 -38
43 -7 21 -18 48 -26 60 -15 24 -54 49 -54 35z"/>
<path d="M6606 1752 c-29 -33 -110 -71 -179 -84 -37 -7 -67 -17 -67 -23 0 -24
64 -118 114 -170 133 -136 286 -141 426 -15 l45 41 -48 -31 c-143 -94 -298
-73 -416 56 -69 76 -67 98 11 123 45 15 113 63 128 92 15 28 7 34 -14 11z"/>
<path d="M2915 2300 c-15 -17 -20 -39 -23 -98 l-3 -77 20 35 c11 19 20 42 21
51 0 9 8 27 18 40 19 22 19 22 68 5 87 -30 90 -29 32 13 -72 52 -106 60 -133
31z"/>
<path d="M3260 2146 c0 -3 18 -22 39 -41 26 -24 50 -61 70 -108 30 -70 30 -71
31 -34 0 47 -22 95 -58 129 -32 30 -82 63 -82 54z"/>
<path d="M3105 2099 c-35 -31 -165 -216 -165 -236 0 -15 46 -33 83 -33 63 0
126 65 153 158 19 63 12 117 -16 132 -15 8 -26 4 -55 -21z"/>
<path d="M3181 1949 c-30 -66 -84 -126 -121 -134 -15 -3 -47 -1 -70 5 -54 15
-59 8 -18 -21 42 -30 127 -32 159 -3 32 29 88 154 89 197 0 21 -18 1 -39 -44z"/>
<path d="M2586 1739 c-140 -108 -169 -147 -174 -234 -5 -76 13 -119 69 -170
41 -36 75 -49 47 -17 -9 9 -26 37 -38 61 -19 37 -22 56 -18 112 7 89 36 137
139 230 69 62 110 109 96 109 -2 0 -56 -41 -121 -91z"/>
<path d="M2948 1426 c-103 -110 -131 -173 -108 -242 12 -34 86 -101 99 -88 3
3 -1 11 -9 17 -34 26 -50 62 -50 115 0 63 20 106 84 181 95 109 83 122 -16 17z"/>
<path d="M5835 2098 c-79 -40 -105 -65 -105 -100 0 -36 13 -62 56 -111 19 -20
34 -42 34 -49 0 -6 2 -9 5 -6 3 3 -8 31 -25 63 -34 65 -36 84 -15 125 15 29
82 82 127 100 21 8 21 9 3 9 -11 0 -47 -14 -80 -31z"/>
<path d="M5978 1422 c15 -152 25 -197 54 -258 40 -87 112 -104 226 -54 62 27
62 36 1 20 -63 -17 -121 -5 -160 34 -40 38 -69 117 -99 266 -11 57 -24 108
-28 112 -4 4 -2 -50 6 -120z"/>
<path d="M3591 1977 c-38 -19 -59 -56 -81 -145 -15 -59 -20 -97 -15 -131 10
-72 20 -76 21 -7 2 80 34 155 85 201 39 35 58 40 132 36 20 -2 37 -1 37 2 0 4
-94 40 -135 51 -11 3 -31 0 -44 -7z"/>
<path d="M4040 1861 c8 -5 49 -16 90 -26 65 -15 81 -24 118 -62 45 -47 55 -41
21 14 -27 44 -63 62 -152 73 -88 12 -96 12 -77 1z"/>
<path d="M3781 1648 c0 -85 -19 -175 -47 -230 -9 -17 -45 -68 -80 -114 -77
-101 -88 -122 -113 -211 -13 -49 -17 -82 -12 -109 10 -51 36 -101 61 -114 18
-10 17 -7 -4 25 -35 51 -40 86 -21 151 27 95 73 179 132 245 73 83 114 170
121 259 4 66 -11 165 -28 175 -5 3 -8 -32 -9 -77z"/>
<path d="M4109 1701 c18 -33 13 -78 -10 -94 -19 -14 -19 -18 -8 -57 14 -47 50
-80 86 -80 24 0 24 0 -6 25 -38 33 -46 59 -27 87 29 42 17 106 -25 129 -19 9
-19 9 -10 -10z"/>
<path d="M4665 1958 c-50 -27 -57 -76 -25 -163 l18 -50 1 87 c1 78 3 88 23
102 12 9 42 16 66 16 26 0 41 4 37 10 -8 13 -95 12 -120 -2z"/>
<path d="M4650 875 c0 -178 11 -236 52 -270 27 -23 38 -25 118 -25 102 0 128
15 43 25 -92 10 -110 15 -128 35 -22 24 -41 107 -50 208 -10 130 -17 172 -26
172 -5 0 -9 -65 -9 -145z"/>
<path d="M5138 1866 c-98 -21 -119 -61 -78 -151 22 -49 46 -73 30 -30 -6 15
-10 46 -10 70 0 52 23 73 110 100 30 9 60 18 65 20 28 9 -68 2 -117 -9z"/>
<path d="M5717 1463 c-4 -3 -7 -33 -7 -65 0 -70 -23 -100 -90 -118 -36 -10
-50 -9 -85 5 -24 8 -51 25 -60 35 -10 11 -23 20 -29 20 -14 0 54 -69 84 -85
52 -27 96 -29 145 -5 55 27 68 48 75 125 l5 60 35 -3 35 -2 -25 20 c-24 19
-70 27 -83 13z"/>
<path d="M5147 1215 c-46 -206 2 -360 114 -371 l44 -4 -41 19 c-58 27 -80 60
-94 143 -9 53 -10 92 -1 158 13 104 14 130 3 130 -5 0 -16 -34 -25 -75z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/static/favicon/1/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/favicon/1/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 KiB

87
apps/wrapped/tables.py Normal file
View File

@ -0,0 +1,87 @@
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.utils.html import format_html
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 .models import Wrapped
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
}
},
)
share = tables.Column(
verbose_name=_("Share"),
accessor="pk",
orderable=False,
attrs={
"td": {
"class": 'col-sm-2',
"title": _("Click to copy the link in the press paper"),
}
},
)
def render_share(self, value, record):
val = '<a class="btn btn-sm btn-primary" data-turbolinks="false" '
val += 'onclick="copylink(' + str(record.id) + ')">'
val += _('Copy link')
val += '</a>'
return format_html(val)
def render_public(self, value, record):
val = "" if record.public else ""
return val

View File

@ -0,0 +1,82 @@
{% load static i18n pretty_money getenv %}
{% comment %}
Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<html lang="{{ LANGUAGE_CODE|default:"en" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %} class="postition-relative h-100">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>
{% block title %}{{ title }}{% endblock title %} - {{ request.site.name }}
</title>
<meta name="description" content="{% trans "The ENS Paris-Saclay BDE note." %}">
{# Favicon #}
<link rel="apple-touch-icon" sizes="180x180" href="{% static "wrapped/favicon/1/apple-touch-icon.png" %}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static "wrapped/favicon/1/favicon-32x32.png" %}">
<link rel="icon" type="image/png" sizes="16x16" href="{% static "wrapped/favicon/1/favicon-16x16.png" %}">
<link rel="manifest" href="{% static "wrapped/favicon/1/site.webmanifest" %}">
<link rel="mask-icon" href="{% static "wrapped/favicon/1/safari-pinned-tab.svg" %}" color="#5bbad5">
<link rel="shorcut icon" href="{% static "wrapped/favicon/1/favicon.ico" %}">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="{% static "wrapped/favicon/1/browserconfig.xml" %}">
<meta name="theme-color" content="#ffffff">
{# Bootstrap, Font Awesome and custom CSS #}
<link rel="stylesheet" href="{% static "bootstrap4/css/bootstrap.min.css" %}">
<link rel="stylesheet" href="{% static "font-awesome/css/font-awesome.min.css" %}">
<link rel="stylesheet" href="{% static "wrapped/css/1/custom.css" %}">
{# JQuery, Bootstrap and Turbolinks JavaScript #}
<script src="{% static "jquery/jquery.min.js" %}"></script>
<script src="{% static "popper.js/umd/popper.min.js" %}"></script>
<script src="{% static "bootstrap4/js/bootstrap.min.js" %}"></script>
<script src="{% static "js/turbolinks.js" %}"></script>
<script src="{% static "js/base.js" %}"></script>
<script src="{% static "js/konami.js" %}"></script>
{# Translation in javascript files #}
<script src="{% static "js/jsi18n/"|add:LANGUAGE_CODE|add:".js" %}"></script>
{# If extra ressources are needed for a form, load here #}
{% if form.media %}
{{ form.media }}
{% endif %}
{% block extracss %}{% endblock %}
</head>
<body>
{% block content %}
<p>Default content...</p>
{% endblock %}
<br>
<div class="wrap-container">
<h2>{% trans "The NoteKfet this year it's also" %}</h2>
<ul class="list" id="glob_top3_conso">
<li>{{ glob_nb_transaction }} {% trans " transactions" %}</li>
<li>{{ glob_nb_soiree }} {% trans " parties" %}</li>
<li>{{ glob_nb_entree_pot }} {% trans " Pot entries" %}</li>
<script>
let liste = {{ glob_top3_conso | safe }};
let ul = document.getElementById("glob_top3_conso");
liste.forEach(item => {
let li = document.createElement("li");
li.textContent = item[1] + " " + item[0];
ul.appendChild(li);
});
</script>
<li>{{ glob_nb_vieux_con }} {% trans " old dickhead behind the bar" %} </li>
</ul>
</div>
<script>
CSRF_TOKEN = "{{ csrf_token }}";
$(".invalid-feedback").addClass("d-block");
</script>
{% block extrajavascript %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,31 @@
{% extends "wrapped/1/wrapped_base.html" %}
{% comment %}
COPYRIGHT (C) 2018-2024 BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load i18n pretty_money %}
{% block content %}
<div class="wrap-container">
<h2>{% trans "NoteKfet Wrapped" %}</h2>
<h1 id="name">{{ wrapped.note.club.name }}</h1>
{% trans "Your best consumer:" %}
<div class="category" id="consumer"></div>
{% trans "Your worst creditor:" %}
<div class="category" id="creditor"></div>
<ul class="list">
<li>{{ nb_soiree_orga }} {% trans "party·ies organised" %}</li>
<li>{{ nb_member }} {% trans "distinct members" %}</li>
</ul>
</div>
<script>
let con = Boolean({{ big_consumer | safe }});
let cre = Boolean({{ big_creancier | safe }});
let d1 = document.getElementById("consumer");
let d2 = document.getElementById("creditor");
if (con) { d1.textContent = {{ big_consumer | safe }}[0] + " " + gettext("with") + " " + {{ big_consumer | safe}}[1] + "€";}
else { d1.textContent = gettext("Infortunately, you doesn't have consumer this year");};
if (cre) { d2.textContent = {{ big_creancier | safe}}[0] + " " + gettext("with") + " " + {{ big_creancier | safe}}[1] + "€";}
else { d2.textContent = gettext("Congratulations you are a real rat !"); };
</script>
{% endblock %}

View File

@ -0,0 +1,69 @@
{% extends "wrapped/1/wrapped_base.html" %}
{% comment %}
COPYRIGHT (C) 2018-2024 BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load i18n pretty_money %}
{% block content %}
<div class="wrap-container">
<h2>{% trans "NoteKfet Wrapped" %}</h2>
<h1 id="name">{{ wrapped.note.user.username }}</h1>
{% if wei %}
<div class="category" id="wei">
{% trans "You participate to the wei: " %} {{ wei }} {% trans "in the" %} {{ bus }}
</div>
{% endif %}
<div class="ranking-bar">
<div class="ranking-progress" id="pot_bar">
{{ nb_pot_entry }}/{{ nb_pots }} {% trans "pots !" %}
</div>
<script>
const percentage = ({{ nb_pot_entry }} / {{ nb_pots }}) *100;
document.getElementById("pot_bar").style.width = percentage + '%';
</script>
</div>
{% if first_conso %}
<ul class="list" id="user_conso">
<li>{% trans "Your first conso of the year: " %} {{ first_conso }}</li>
<li>{% trans "Your prefered consumtion category: " %} {{ top_category }}</li>
<script>
let top3 = {{ top3_conso | safe }};
let l = document.getElementById("user_conso");
top3.forEach(item => {
let li = document.createElement("li");
li.textContent = item[1] + " " + item[0];
l.appendChild(li);
});
</script>
</ul>
{% endif %}
<div class="category">
{{ nb_rechargement }} {% trans ": it's the number of time your reload your note" %}
</div>
{% if class_conso_all > 0 %}
{% trans "Your overall expenses: " %}
<div class="ranking-bar">
<div class="ranking-progress" id="all_bar">
{{ class_conso_all }}/{{ class_part_all }} {% trans "with" %} {{ amount_conso_all }}€
</div>
</div>
<script>
const p_all = 100 - (({{ class_conso_all }} - 1) / {{ class_part_all }}) * 100;
document.getElementById("all_bar").style.width = p_all + '%';
</script>
{% endif %}
<br>
{% if class_conso_bde > 0 %}
{% trans "Your expenses to BDE: " %}
<div class="ranking-bar">
<div class="ranking-progress" id="bde_bar">
{{ class_conso_bde }}/{{ class_part_bde }} {% trans "with" %} {{ amount_conso_bde }}€
</div>
</div>
<script>
const p_bde = 100 - (({{ class_conso_bde }} - 1) / {{ class_part_all }}) * 100;
document.getElementById("bde_bar").style.width = p_bde + '%';
</script>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,69 @@
{% 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">
let club_not_public = {{ club_not_public }};
if (club_not_public) { (addMsg("{% trans "Do not forget to ask permission to people who are in your wrapped before to make them public" %}", 'warning'));}
function refreshTable() {
$("#wrapped_table").load(location.pathname + " #wrapped_table");
}
function copylink(id) {
navigator.clipboard.writeText({{ request.get_full_path }} + id)
.then(() => { addMsg("{% trans "Link copied" %}", 'success', 1000);});
}
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
View 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'),
]

62
apps/wrapped/views.py Normal file
View File

@ -0,0 +1,62 @@
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import json
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView
from django_tables2.views import SingleTableView
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin
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):
context = super().get_context_data(**kwargs)
w = self.object_list.filter(note__noteclub__club__pk__gte=-1, public=False)
if w:
context['club_not_public'] = 'true'
else:
context['club_not_public'] = 'false'
return context
class WrappedDetailView(ProtectQuerysetMixin, LoginRequiredMixin, 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
note_type = 'user' if 'user' in Wrapped.objects.get(pk=kwargs['pk']).note.__dir__() else 'club'
self.template_name = 'wrapped/' + str(bde_id) + '/wrapped_view_' + note_type + '.html'
return super().get(*args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
d = json.loads(self.object.data_json)
for key in d:
context[key] = d[key]
context['title'] = str(self.object)
return context

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-07 09:07+0200\n"
"POT-Creation-Date: 2025-02-25 13:27+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,11 +17,11 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: apps/member/static/member/js/alias.js:17
#: apps/activity/static/activity/js/opener.js:31
msgid "Opener successfully added"
msgstr "Ouvreureuse ajouté avec succès"
#: apps/member/static/member/js/alias.js:17
#: apps/activity/static/activity/js/opener.js:47
msgid "Opener successfully deleted"
msgstr "Ouvreureuse supprimé avec succès"

View File

@ -79,6 +79,7 @@ INSTALLED_APPS = [
'scripts',
'treasury',
'wei',
'wrapped',
]
MIDDLEWARE = [

View File

@ -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-gift"></i> {% trans 'Wrapped' %}</a>
</li>
{% endif %}
{% if request.user.is_authenticated %}
<li class="nav-item">

View File

@ -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')),

View File

@ -32,7 +32,8 @@ deps =
pep8-naming
pyflakes
commands =
flake8 apps --extend-exclude apps/scripts
flake8 apps --extend-exclude apps/scripts,apps/wrapped/management/commands
flake8 apps/wrapped/management/commands --extend-ignore=C901
[flake8]
ignore = W503, I100, I101, B019