mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-02-25 09:26:29 +00:00
Modification vers couleur Surviva[list]
This commit is contained in:
parent
4d03d9460d
commit
3018c1e0fe
42
apps/api/filters.py
Normal file
42
apps/api/filters.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import re
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from rest_framework.filters import SearchFilter
|
||||||
|
|
||||||
|
|
||||||
|
class RegexSafeSearchFilter(SearchFilter):
|
||||||
|
@lru_cache
|
||||||
|
def validate_regex(self, search_term) -> bool:
|
||||||
|
try:
|
||||||
|
re.compile(search_term)
|
||||||
|
return True
|
||||||
|
except re.error:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_search_fields(self, view, request):
|
||||||
|
"""
|
||||||
|
Ensure that given regex are valid.
|
||||||
|
If not, we consider that the user is trying to search by substring.
|
||||||
|
"""
|
||||||
|
search_fields = super().get_search_fields(view, request)
|
||||||
|
search_terms = self.get_search_terms(request)
|
||||||
|
|
||||||
|
for search_term in search_terms:
|
||||||
|
if not self.validate_regex(search_term):
|
||||||
|
# Invalid regex. We assume we don't query by regex but by substring.
|
||||||
|
search_fields = [f.replace('$', '') for f in search_fields]
|
||||||
|
break
|
||||||
|
|
||||||
|
return search_fields
|
||||||
|
|
||||||
|
def get_search_terms(self, request):
|
||||||
|
"""
|
||||||
|
Ensure that search field is a valid regex query. If not, we remove extra characters.
|
||||||
|
"""
|
||||||
|
terms = super().get_search_terms(request)
|
||||||
|
if not all(self.validate_regex(term) for term in terms):
|
||||||
|
# Invalid regex. If a ^ is prefixed to the search term, we remove it.
|
||||||
|
terms = [term[1:] if term[0] == '^' else term for term in terms]
|
||||||
|
# Same for dollars.
|
||||||
|
terms = [term[:-1] if term[-1] == '$' else term for term in terms]
|
||||||
|
return terms
|
108
apps/wei/management/commands/make_teams.py
Normal file
108
apps/wei/management/commands/make_teams.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
from argparse import ArgumentParser, FileType
|
||||||
|
|
||||||
|
from django.core.management import BaseCommand
|
||||||
|
from django.db import transaction
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from ...forms import CurrentSurvey
|
||||||
|
from ...models import Bus, WEIMembership
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Attribute to each first year member a team for the WEI"
|
||||||
|
|
||||||
|
def add_arguments(self, parser: ArgumentParser):
|
||||||
|
parser.add_argument('--doit', '-d', action='store_true', help='Finally run the algorithm in non-dry mode.')
|
||||||
|
parser.add_argument('--output', '-o', nargs='?', type=FileType('w'), default=self.stdout,
|
||||||
|
help='Output file for the algorithm result. Default is standard output.')
|
||||||
|
|
||||||
|
def attribute_teams(self, bus):
|
||||||
|
if not bus.size:
|
||||||
|
return
|
||||||
|
|
||||||
|
teams = bus.teams.all()
|
||||||
|
|
||||||
|
old_members = WEIMembership.objects.filter(registration__first_year=False, bus=bus, team__in=teams)
|
||||||
|
new_members = WEIMembership.objects.filter(registration__first_year=True, bus=bus)
|
||||||
|
|
||||||
|
n_chef_tot = old_members.count()
|
||||||
|
n_equipe = teams.count()
|
||||||
|
size_goal = {}
|
||||||
|
for team in teams:
|
||||||
|
size_goal[team] = int(old_members.filter(team=team).count() * new_members.count() / n_chef_tot)
|
||||||
|
i = 0
|
||||||
|
non_men = list(new_members.filter(~Q(registration__gender='male')))
|
||||||
|
men = list(new_members.filter(registration__gender='male'))
|
||||||
|
|
||||||
|
print(bus.name, size_goal.values())
|
||||||
|
return
|
||||||
|
|
||||||
|
while non_men:
|
||||||
|
if new_members.filter(team=teams[i]).count() < size_goal[teams[i]]:
|
||||||
|
non_man = non_men.pop()
|
||||||
|
non_man.team = teams[i]
|
||||||
|
non_man.save()
|
||||||
|
i += 1
|
||||||
|
i %= n_equipe
|
||||||
|
|
||||||
|
for i, team in enumerate(teams):
|
||||||
|
print(i, new_members.filter(team=team).count())
|
||||||
|
if new_members.filter(team=team).count() == 1:
|
||||||
|
# Si une fille est seule on l'enlève.
|
||||||
|
g_alone = new_members.get(team=team)
|
||||||
|
g_alone.team = teams[i + 1]
|
||||||
|
g_alone.save()
|
||||||
|
remain = True
|
||||||
|
i = 0
|
||||||
|
while men and remain:
|
||||||
|
if new_members.filter(team=teams[i]).count() < size_goal[teams[i]]:
|
||||||
|
m = men.pop()
|
||||||
|
m.team = teams[i]
|
||||||
|
m.save()
|
||||||
|
i += 1
|
||||||
|
i %= n_equipe
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
"""
|
||||||
|
Run the WEI algorithm to attribute a bus to each first year member.
|
||||||
|
"""
|
||||||
|
sid = transaction.savepoint()
|
||||||
|
|
||||||
|
algorithm = CurrentSurvey.get_algorithm_class()()
|
||||||
|
|
||||||
|
try:
|
||||||
|
from tqdm import tqdm
|
||||||
|
display_tqdm = True
|
||||||
|
except ImportError:
|
||||||
|
display_tqdm = False
|
||||||
|
|
||||||
|
for bus in Bus.objects.all():
|
||||||
|
self.attribute_teams(bus)
|
||||||
|
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
output = options['output']
|
||||||
|
registrations = algorithm.get_registrations()
|
||||||
|
per_bus = {bus: [r for r in registrations if 'selected_bus_pk' in r.information
|
||||||
|
and r.information['selected_bus_pk'] == bus.pk]
|
||||||
|
for bus in algorithm.get_buses()}
|
||||||
|
for bus, members in per_bus.items():
|
||||||
|
output.write(bus.name + "\n")
|
||||||
|
output.write("=" * len(bus.name) + "\n")
|
||||||
|
order = -1
|
||||||
|
for r in members:
|
||||||
|
survey = CurrentSurvey(r)
|
||||||
|
for order, (b, _score) in enumerate(survey.ordered_buses()):
|
||||||
|
if b == bus:
|
||||||
|
break
|
||||||
|
output.write(f"{r.user.username} ({order + 1})\n")
|
||||||
|
output.write("\n")
|
||||||
|
|
||||||
|
if not options['doit']:
|
||||||
|
self.stderr.write(self.style.WARNING("Running in dry mode. "
|
||||||
|
"Use --doit option to really execute the algorithm."))
|
||||||
|
transaction.savepoint_rollback(sid)
|
||||||
|
return
|
133
note_kfet/static/css/custom.css.save
Normal file
133
note_kfet/static/css/custom.css.save
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
.validate:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opaque tooltip with white background */
|
||||||
|
.tooltip.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-inner {
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
|
||||||
|
border: 1px solid rgba(0, 0, 0, .250);
|
||||||
|
color: #000;
|
||||||
|
margin: 0 .5rem .25rem .5rem;
|
||||||
|
padding: 0;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bs-tooltip-bottom .arrow::before {
|
||||||
|
border-bottom-color: rgba(0, 0, 0, .250);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixed width picture column */
|
||||||
|
.picture-col {
|
||||||
|
max-width: 202px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit fluid container to a max size */
|
||||||
|
.container-fluid {
|
||||||
|
max-width: 1600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply Bootstrap table-responsive to all Django tables */
|
||||||
|
.table-container {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smaller language selector */
|
||||||
|
select.language {
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
height: calc(1.5em + .5rem);
|
||||||
|
color: #6c757d;
|
||||||
|
-moz-appearance: none;
|
||||||
|
width: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove horizontal padding on mark */
|
||||||
|
.mark,
|
||||||
|
mark {
|
||||||
|
padding: .2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make navbar more readable */
|
||||||
|
.navbar-dark .navbar-nav .nav-link {
|
||||||
|
color: rgba(255, 255, 255, .75);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last BDE colors */
|
||||||
|
.bg-primary {
|
||||||
|
background-color: rgb(0, 0, 0) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scrollbar-color: rgba(0, 0, 0, 1) rgba(35, 35, 39, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: rgba(64, 64, 64, 1);
|
||||||
|
background-image: url(../img/background-texture.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary:hover,
|
||||||
|
.btn-outline-primary:not(:disabled):not(.disabled).active,
|
||||||
|
.btn-outline-primary:not(:disabled):not(.disabled):active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
border-color: rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary {
|
||||||
|
color: rgb(0, 0, 0);
|
||||||
|
background-color: rgba(248, 249, 250, 0.9);
|
||||||
|
border-color: rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.turbolinks-progress-bar {
|
||||||
|
background-color: #12432E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover,
|
||||||
|
.btn-primary:not(:disabled):not(.disabled).active,
|
||||||
|
.btn-primary:not(:disabled):not(.disabled):active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
border-color: rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: rgba(248, 249, 250, 0.9);
|
||||||
|
background-color: rgb(28, 114, 10);
|
||||||
|
border-color: rgb0à, 67, 46);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-primary {
|
||||||
|
border-color: rgb(28, 114, 10) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgb(28, 114, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: rgb(122, 163, 75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(122, 163, 75, 0.25);
|
||||||
|
border-color: rgb(122, 163, 75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary.focus {
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(122, 163, 75, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user