Commit aa5295a3 authored by Pierre-antoine Comby's avatar Pierre-antoine Comby

le dépot est propre

parents
__pycache__/
*.csv
""" This script contains both the standard and extended Gale-Shapley algorithms
for solving matching games.
A matching game is defined by two sets called suitors and reviewers. Each suitor
(and reviewer) has associated with it an ordered preference of the elements of
the corresponding set. A solution to a matching game is any mapping between the
set of suitors and reviewers.
"""
from collections import Counter
from copy import deepcopy
import numpy as np
def galeshapley(suitor_pref_dict, reviewer_pref_dict):
""" The Gale-Shapley algorithm. This is known to provide a unique, stable
suitor-optimal matching. The algorithm is as follows:
(1) Assign all suitors and reviewers to be unmatched.
(2) Take any unmatched suitor, s, and their most preferred reviewer, r.
- If r is unmatched, match s to r.
- Else, if r is matched, consider their current partner, r_partner.
- If r prefers s to r_partner, unmatch r_partner from r and
match s to r.
- Else, leave s unmatched and remove r from their preference
list.
(3) Go to (2) until all suitors are matched, then end.
Parameters
----------
suitor_pref_dict : dict
A dictionary with suitors as keys and their respective preference lists
as values
review_pref_dict : dict
A dictionary with reviewers as keys and their respective preference
lists as values
Returns
-------
matching : dict
The suitor-optimal (stable) matching with suitors as keys and the
reviewer they are matched with as values
"""
suitors = [s for s in suitor_pref_dict]
matching = {s: None for s in suitors}
while suitors != []:
s = suitors.pop(0)
r = suitor_pref_dict[s][0]
if r not in matching.values():
matching[s] = r
else:
for suitr, revwr in matching.items():
if revwr == r:
r_partner = suitr
if reviewer_pref_dict[r].index(s) < reviewer_pref_dict[r].index(
r_partner
):
matching[r_partner] = None
matching[s] = r
suitors.append(r_partner)
else:
suitor_pref_dict[s].remove(r)
suitors.append(s)
return matching
def get_free_residents(resident_prefs, matching):
""" Return a list of all residents who are currently unmatched but have a
non-empty preference list. """
return [
resident
for resident in resident_prefs
if resident_prefs[resident]
and not any([resident in match for match in matching.values()])
]
def get_worst_idx(hospital, hospital_prefs, matching):
""" Find the index of the worst resident currently assigned to `hospital`
according to their preferences. """
return max(
[
hospital_prefs[hospital].index(resident)
for resident in hospital_prefs[hospital]
if resident in matching[hospital]
]
)
def resident_hospital(resident_prefs, hospital_prefs, capacities):
""" Provide a stable, resident-optimal matching for the given instance of
HR using the algorithm set out in [Gale, Shapley 1962]. """
matching = {hospital: [] for hospital in hospital_prefs}
free_residents = get_free_residents(resident_prefs, matching)
while free_residents:
resident = free_residents[0]
hospital = resident_prefs[resident][0]
i = 0
while capacities[hospital] <= 0:
i += 1
hospital = resident_prefs[resident][i]
<<<<<<< HEAD
=======
>>>>>>> 7158617afbb760d3a35b62a2705688d11b9d0b1b
matching[hospital].append(resident)
if len(matching[hospital]) > capacities[hospital]:
worst = get_worst_idx(hospital, hospital_prefs, matching)
resident = hospital_prefs[hospital][worst]
matching[hospital].remove(resident)
if len(matching[hospital]) == capacities[hospital]:
worst = get_worst_idx(hospital, hospital_prefs, matching)
successors = hospital_prefs[hospital][worst + 1 :]
if successors:
for resident in successors:
hospital_prefs[hospital].remove(resident)
if hospital in resident_prefs[resident]:
resident_prefs[resident].remove(hospital)
free_residents = get_free_residents(resident_prefs, matching)
for hospital, matches in matching.items():
sorted_matches = sorted(matches, key=hospital_prefs[hospital].index)
matching[hospital] = sorted_matches
return matching
def hrt_super_res(resident_prefs, hospital_prefs, capacities):
""" Determine whether a super-stable, resident-optimal matching exists for
the given instance of HR. If so, return the matching. """
# ==================================
# Needs adjusting for ties in prefs.
# ==================================
matching = {h: [] for h in hospital_prefs.keys()}
fulls = {h: False for h in hospital_prefs.keys()}
free_residents = [r for r in resident_prefs.keys()]
while [r for r in free_residents if resident_prefs[r]]:
r = free_residents.pop(0)
r_prefs = resident_prefs[r]
h_best = r_prefs[0]
matching[h_best] += [r]
if len(matching[h_best]) > capacities[h_best]:
r_worst = hospital_prefs[h_best][-1]
if r_worst in matching[h_best]:
matching[h_best].remove(r_worst)
resident_prefs[r_worst].remove(h_best)
hospital_prefs[h_best].remove(r_worst)
if len(matching[h_best]) == capacities[h_best]:
fulls[h_best] = True
worst_idx = np.max(
[
hospital_prefs[h_best].index(resident)
for resident in hospital_prefs[h_best]
if resident in matching[h_best]
]
)
successors = hospital_prefs[h_best][worst_idx + 1 :]
if successors:
for resident in successors:
hospital_prefs[h_best].remove(resident)
resident_prefs[resident].remove(h_best)
resident_match_counts = Counter([tuple(res) for res in matching.values()])
if np.any(
[count > 1 for count in resident_match_counts.values()]
) or np.any(fulls.values()):
raise ValueError("No super-stable matching exists.")
return matching
#! /usr/bin/env python3
BUS = ['Nausi[car] de la vallee du [van]',
'Au [bus]cher',
'La grosse [caisse]',
'Ha[van]a Club',
'l houm[bus]',
'[Van]ted',
'Pernod-Ri[Car]d']
NB_BUS=8
class Bus(object):
"""Un bus contient des équipes , remplis par des 1A en fonction de leur réponse à un questionnaire
"""
def __init__(self, nom, places, places_2a,reponse_criteres,size_equipe):
self.nom = nom # nom du bus
self.criteres = reponse_criteres # critère et pondération
self.note = dict() # note de tous les 1A par rapport à ce bus.
self.rank_unA = []
self.passengers = []
self.old_passengers = []
self.size_team = [int(s) for s in size_equipe]
self.teams = []
self.places = places
self.places_2a = places_2a
self.places_1a = places-places_2a
def __repr__(self):
ret = self.nom
return"Bus_"+ret
def __str__(self):
return "Bus {}".format(self.nom)
def __str__(self):
return "Bus {}".format(self.nom)
def gen_note(self, list_unA):
for unA in list_unA:
score_unA = unA.scores_bus[self]
rank_unA = unA.rank_bus.index(self)
if rank_unA == NB_BUS-1:
note_bus = score_unA/rank_unA
else:
#note_bus = (score_unA-unA.scores_bus[unA.rank_bus[rank_unA+1]] + 2)/(rank_unA+1)
p = NB_BUS - rank_unA
nextu = unA.scores_bus[unA.rank_bus[rank_unA+1]]
tmp = ((NB_BUS - rank_unA) * score_unA - (NB_BUS - rank_unA - 1) * nextu) / (rank_unA + 1)
note_bus = tmp
#note_bus = (score_unA**p - next**(p - 1))
# note_bus = (score_unA-unA.scores_bus[unA.rank_bus[rank_unA+1]])
self.note[unA] = note_bus
def gen_rank(self):
self.rank_unA = list(self.note.items())
self.rank_unA = sorted(self.rank_unA,key=lambda x:x[1],reverse = True)
self.rank_unA = [r[0] for r in self.rank_unA]
def partial_rank(self, students):
return [student for student in self.rank_unA if student in students]
def make_teams(self):
print(self.nom)
n_chef_tot = sum(self.size_team)
n_equipe = len(self.size_team)
N = len(self.passengers)
size_goal = [0]*len(self.size_team)
for i in range(len(self.size_team)):
size_goal[i] = int(self.size_team[i]*N/n_chef_tot)+1
i = 0
places_equipe=sum(size_goal)
girls = [p for p in self.passengers
if p.infos["genre"] == 'F' or p.infos["genre"] == 'N']
not_girls = [p for p in self.passengers
if p.infos["genre"] == 'M']
#
# for e in size_goal:
# equipe = []
# for k in range(int(len(girls)*e/places_equipe)):
# if girls:
# equipe.append(girls.pop())
# for k in range(int(len(not_girls)*e/places_equipe)):
# if not_girls:
# equipe.append(not_girls.pop())
# print(len(equipe))
# self.teams.append(equipe)
i=0
self.teams=[[None]*s for s in size_goal]
while girls :
if None in self.teams[i]:
self.teams[i].append(girls.pop())
self.teams[i].remove(None)
i +=1; i %= n_equipe;
for i in range(len(self.size_team)):
if len(self.teams[i])-self.teams[i].count(None) == 1:
#Si une fille est seule on l'enlève.
g_alone=self.teams[i].pop()
self.teams[i].append(None)
next_ind = (i+1)%n_equipe
self.teams[next_ind].remove(None)
self.teams[next_ind].append(g_alone)
remain =True
while not_girls and remain:
if None in self.teams[i]:
self.teams[i].append(not_girls.pop())
self.teams[i].remove(None)
i +=1; i %= n_equipe;
#cleaned unused places
for l in self.teams:
while None in l:
l.remove(None)
#affichage
for k in range(len(self.size_team)):
print(self.size_team[k],size_goal[k],len(self.teams[k]),self.teams[k])
print(len(self.passengers),sum(size_goal))
def partial_rank(self, students):
return [student for student in self.rank_unA if student in students]
{
"aspique":[978,1004,1112,1042,1092,1083,1145,963,975,1059,934,931,1082,1046,1084,961,1097,1195,944,1106,952,942,1071,1134],
"chytem":[1002],
"fanfare":[947,997,929,930, 1066],
"hooligens":[1050,1006,962,1013],
"med":[951,956],
"saphire":[1118,1111],
"satelist":[1016,1114],
"vieux":[974, 1066,943]
}
#!/usr/bin/env python3
# -*- coding:utf8 -*
"""
Script pour le remplissages des 1A dans les bus du WEI
TODO :
- adapter parse_wiki
- géneration structure donnée
- implémentation algorithme
"""
import sys
import csv
from collections import OrderedDict
import copy
from unA import *
from bus import *
import parsewiki
import algorithms
import random
import json
def get_1A_from_csv(database):
list_unA = []
with open(database, newline='') as csvfile:
table = csv.DictReader(csvfile, delimiter=',')
for people in table:
info = {i: people[i] for i in INFOS}
if info["dept"] == 'A"2': # on aime pas les chimiste
info["dept"] = "A''2"
criteres = {c: int(people[c]) for c in CRITERES}
list_unA.append(UnA(info, criteres))
return list_unA
def get_bus_from_csv(database):
list_bus = []
with open(database, newline='') as csvfile:
table = csv.DictReader(csvfile, delimiter=',')
for bus in table:
nom = bus["bus"]
places_2a = int(bus["places_2a"])
places = int(bus["places"])
size_equipe=list(bus["equipes"])
criteres = {c: int(bus[c]) for c in CRITERES}
list_bus.append(Bus(nom,places,places_2a, criteres,size_equipe))
return list_bus
def rang_moyen(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.rank_bus.index(bus) + 1
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def note_moyenne(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.scores_bus[bus]
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def rang_moyen(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.rank_bus.index(bus) + 1
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def note_moyenne(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.scores_bus[bus]
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def main(unA_file, bus_file):
list_unA = get_1A_from_csv(unA_file)
#list_unA = []
#for x in list_unAB:
# if x.infos['genre'] == 'F':
# list_unA.append(x)
list_bus = get_bus_from_csv(bus_file)
for unA in list_unA:
unA.gen_score(list_bus)
unA.gen_rank()
for bus in list_bus:
random.shuffle(list_unA)
bus.gen_note(list_unA)
bus.gen_rank()
return list_bus, list_unA
def split_students(students, bus_places):
<<<<<<< HEAD
"""On remplis les bus au fur et à mesure """
=======
>>>>>>> 7158617afbb760d3a35b62a2705688d11b9d0b1b
bus_ranks = {bus: bus.partial_rank(students) for bus in list_bus}
students_ranks = {student: student.rank_bus for student in students}
return algorithms.resident_hospital(students_ranks, bus_ranks, bus_places)
def merge_bus(bus_matches_by_dpt):
final_match = {bus: [] for bus in list_bus}
for dpt in bus_matches_by_dpt:
for bus in bus_matches_by_dpt[dpt]:
match = bus_matches_by_dpt[dpt][bus]
final_match[bus].extend(match)
return final_match
<<<<<<< HEAD
def get_unA(idwei, students):
for s in students:
if int(s.infos["idwei"]) == idwei:
return s
def hardcode(file, bus_by_name,students):
with open(file) as config:
assigned =json.load(config)
# assignment = {bus_by_name[b]: list(map(lambda x:get_unA(x,students),assigned[b])) for b in assigned}
for b in assigned:
bus = bus_by_name[b]
bus.places_1a -= len(assigned[b])
to_place=[]
for idwei in assigned[b]:
u = get_unA(idwei,students)
if u != None:
to_place.append(u)
bus.passengers.extend(to_place)
for s in to_place:
try:
students.remove(s)
except:
print(s)
def make_repartition(list_bus,students):
list_dpt = set([s.infos["dept"] for s in students])
N = len(students)
places_by_bus = {bus:bus.places_1a for bus in list_bus}
part_students = [f for f in students if f.infos['genre'] == 'F']
n_filles = len(part_students)
# pas de fille dans le bus aspique cette année.
bus_places= {bus: int((bus.places_1a*n_filles)/N)+1 for bus in list_bus}
bus_matches_by_dpt = dict()
# on commence par répartir les filles qui sont considéré comme un département
bus_matches_by_dpt["filles"] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt["filles"]:
places_by_bus[bus] -= len(bus_matches_by_dpt["filles"][bus])
# pour le reste des départements.
for dpt in list_dpt:
random.shuffle(list_bus)
students = [s for s in students if s not in part_students]
part_students = [s for s in students if s.infos['dept'] == dpt]
places_need = len(part_students)
bus_places = {bus: 0 for bus in list_bus}
working_places = {bus: places_by_bus[bus] for bus in list_bus}
while places_need > 0:
for bus in list_bus:
if places_need > 0 and working_places[bus] > 0:
bus_places[bus] += 1
working_places[bus] -= 1
places_need -= 1
for bus in list_bus:
if bus_places[bus] == 0 and working_places[bus] > 0:
bus_places[bus] += 1
bus_matches_by_dpt[dpt] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt[dpt]:
places_by_bus[bus] -= len(bus_matches_by_dpt[dpt][bus])
final_match = merge_bus(bus_matches_by_dpt)
return final_match
def csv_bus_output(list_bus):
fieldnames= [
"nom",
"prenom",
"genre",
"dept",
"tel",
"urgence_nom",
"urgence_tel",
"mail",
"infos"
]
for bus in match:
with open("bus/"+bus.nom+".csv",'w+',newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames,delimiter=',')
writer.writeheader()
for s in bus.passengers:
to_write = {i:s.infos[i] for i in fieldnames}
# to_write.update({"rank_bus":s.rank_bus.index(bus),"fav_bus":s.rank_bus[0]})
writer.writerow(to_write)
def csv_team_output(list_bus):
for bus in list_bus:
infos = [
"nom",
"prenom",
"genre",
"dept",
"tel",
]
fieldnames = infos + ["equipe"]
with open("bus_team/"+bus.nom+".csv","w+",newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames,delimiter=',')
writer.writeheader()
for t in bus.teams:
for s in t:
to_write = {i:s.infos[i] for i in infos}
to_write["equipe"] = bus.teams.index(t)+1
writer.writerow(to_write)
list_bus, students = main("unA.csv", "bus.csv")
bus_by_name={bus.nom:bus for bus in list_bus} # pour harcoder sans se prendre la tete
print(len(students), [bus.places_1a for bus in list_bus], sum([bus.places_1a for bus in list_bus]))
hardcode("hardcode.json",bus_by_name,students)
print([len(bus.passengers) for bus in list_bus])
match = make_repartition(list_bus,students)
for bus in match:
bus.passengers.extend(match[bus])
bus.make_teams()
csv_bus_output(list_bus)
csv_team_output(list_bus)
n=0
m=0
for bus in list_bus:
n += bus.places
m += len(bus.passengers)+bus.places_2a
print(n,m)
=======
def latex_output(final_match):
print("\\documentclass[fontsize=12pt,twoside=false,parskip=half, french]{scrartcl}")
print("\\usepackage[utf8]{inputenc}")
print("\\usepackage[T1]{fontenc}")
print("\\usepackage{ltablex}")
print("\\usepackage{babel}")
print("\\usepackage{palatino}\n")
print("\\renewcommand{\\arraystretch}{1.5}\n")
print("\\begin{document}")
print("\\setlength{\\tabcolsep}{0.2cm}")
for bus in final_match:
print(" \\section{{{}}}".format(bus))
match = final_match[bus]
print(" \\begin{center}")
print(" \\begin{tabularx}{\\textwidth}{|l|l|} \\hline")
i = 0
for student in match:
if i % 2 == 0:
print(" {} &".format(student))
else:
print(" {} \\\\ \\hline".format(student))
i += 1
if i % 2 == 1:
print(" \\\\ \\hline")
print(" \\end{tabularx}")
print(" \\end{center}\n")
print("\\newpage")
print("\\end{document}")
#for dpt in bus_matches_by_dpt:
# print("\n\n\n#{}".format(dpt.upper()))
# for bus in bus_matches_by_dpt[dpt]:
# match = bus_matches_by_dpt[dpt][bus]
# print("\n {} {} {}\n".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
# for student in match:
# print(" {}".format(student))
list_bus, students = main("test.csv", "bus_test.csv")
list_dpt = set([s.infos["dept"] for s in students])
places_by_bus = {bus: 43 for bus in list_bus}
bus_matches_by_dpt = {}
part_students = [f for f in students if f.infos['genre'] == 'F']
bus_places = {bus:int(2 + len(part_students)/7) for bus in list_bus}
bus_matches_by_dpt["filles"] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt["filles"]:
places_by_bus[bus] -= len(bus_matches_by_dpt["filles"][bus])
for dpt in list_dpt:
random.shuffle(list_bus)
students = [s for s in students if s not in part_students]
part_students = [s for s in students if s.infos['dept'] == dpt]
places_need = len(part_students)
bus_places = {bus: 0 for bus in list_bus}
working_places = {bus: places_by_bus[bus] for bus in list_bus}
while places_need > 0:
#student_by_bus = int(places_need / 7)
#for bus in list_bus:
# if places_need == 0:
# bus_places[bus] += 0
# if places_by_bus[bus] <= 0:
# bus_places[bus] += 0
# else:
# bus_places[bus] += min(places_by_bus[bus], student_by_bus)
# places_need -= bus_places[bus]
for bus in list_bus:
if places_need > 0 and working_places[bus] > 0:
bus_places[bus] += 1
working_places[bus] -= 1
places_need -= 1
for bus in list_bus:
if bus_places[bus] == 0 and working_places[bus] > 0:
bus_places[bus] += 1
bus_matches_by_dpt[dpt] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt[dpt]:
places_by_bus[bus] -= len(bus_matches_by_dpt[dpt][bus])
final_match = merge_bus(bus_matches_by_dpt)
latex_output(final_match)
#print("BUS RESULTS")
#for dpt in bus_matches_by_dpt:
# print("\n\n\n#{}".format(dpt.upper()))
# for bus in bus_matches_by_dpt[dpt]:
# match = bus_matches_by_dpt[dpt][bus]
# print("\n {} {} {}\n".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
# for student in match:
# print(" {}".format(student))
#print("Name, average rank, avarage mark")
#for bus in final_match:
# match = final_match[bus]
# print("{} {} {}".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
#print("\n\n\nStudent by dpt and bus\n\n")
#for dpt in bus_matches_by_dpt:
# print("\n\n\n#{}".format(dpt.upper()))
# for bus in bus_matches_by_dpt[dpt]:
# match = bus_matches_by_dpt[dpt][bus]
# print("\n {} {} {}".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
# for student in match:
# print(" {}".format(student))
>>>>>>> 7158617afbb760d3a35b62a2705688d11b9d0b1b
#!/usr/bin/env python3
# -*- coding:utf-8 -*
"""Pour récupérer des infos à partir du wiki."""
import time
import re
#import urllib.request
import pprint
#: URL de la page wiki des bus
URL = "https://wiki.crans.org/OrganisationWei{}/Bus?action=raw".format(
time.localtime()[0])
FILE = "rawpage"
def get_raw():
"""Récupère le contenu de la page."""
return open(FILE).read()
# page = urllib.request.urlopen(URL)
# text = page.read().decode("utf-8")
return text
def parse_names(raw):
"""Récupère les tableaux de Bus (= un tableau après un titre commençant par '== Bus')."""
names = []
l = re.findall("== (?:Bi)?Bus.*==[^|]*\n((?:\|\|.*\|\|\n)+)", raw)
for table in l:
obj = re.search("Nom de l'équipe[^|]*\|\|", table)
after = table[obj.end():]
end = after.index("\n")
after = after[:end]
teams = re.findall("([^|]+)\|\|", after)
teams = [t.strip() for t in teams]
names.append(teams)
return names
def get_team_names():
raw = get_raw()
teams = parse_names(raw)
return teams
if __name__ == "__main__":
for t in get_team_names():
print(t)
##acl +GroupeBde:admin
#acl +GroupeBde:admin GroupeWei:read,write GroupeAdmin:read,write,revert,delete,admin All:
## Ajouter un # au début de la 1ère ligne et
## Retirer le 1er # de la ligne précédente pour rendre la page inaccessible aux 1As
= Les Bus du WEI 2018 =
Deadline : 30 juin 2018
* Lol. -- [[Wiki20-100|Un vieux con de GC WEI]] <<DateTime(2018-06-01T09:13:09+0200)>>
* Merci pour ta sagesse, nous sommes jeunes et naïfs -- WikiPagnyx <<DateTime(2018-06-02T11:00:41Z)>>
{{{#!wiki caution
L'accès de cette page est verrouillé pour ne pas spoiler le WEI aux 1As.
Ajoutez vos amis bloqués dehors au GroupeWei pour qu'ils puissent revenir !
}}}
{{{#!wiki tip
Un '''référent''' de Bus est un des organisateurs du bus, impliqué dans son ambiance et qui connaît bien les autres chefs d'équipe.
Il est le lien privilégié entre les chefs WEI et le bus en amont du WEI.
C'est par lui que les chefs WEI feront passer leur communication (deadline des T-shirts, harcèlement pour les nom d'équipe, ce qu'il faut prévoir dans le bus…).
Ne pas confondre avec le '''chef de Bus''' qui lui est un 2A, souvent du BDE, et qui sera le lien (sobre et responsable) avec les chefs WEI ''pendant'' le WEI.
Les référents d'un bus doivent envoyer un mail à ensps.wei2018@gmail.com pour se signaler comme référent de ce bus.