Skip to content

Instantly share code, notes, and snippets.

@jathak
Last active November 27, 2018 10:48
Show Gist options
  • Save jathak/26d16cced7058d081ae67e18141bd1eb to your computer and use it in GitHub Desktop.
Save jathak/26d16cced7058d081ae67e18141bd1eb to your computer and use it in GitHub Desktop.
Converts a CSV of availabilities into Prolog code that can make assignments
# csv_to_prolog.py
#
# Takes a CSV from stdin in the following format:
# <Any>, Time1, Time2, Time3, Time4...
# counts, 2, 3, 4, 5... (number of spots for each time)
# Person1, Yes, Maybe, Yes, No... (Yes, Maybe, or No for availability)
# Person2, Yes, Maybe, Yes, No...
#
# and outputs Prolog to do assignments.
#
# Example usage for bash (assuming SWI Prolog is installed and on path):
# swipl -s <(cat input.csv | python3 csv_to_prolog.py) -g run -t halt
# and for fish:
# swipl -s (cat input.csv | python3 csv_to_prolog.py | psub) -g run -t halt
#
# Replace `run` with `run($x)` to allow $x assignments to Maybe availability.
import sys
prolog_body = """
/* Code */
last_count(_Type, 0, []).
last_count(Type, Count, [[Type, Count]|_Rest]).
last_count(Type, Count, [[OtherType, _OtherCount]|Rest]) :-
Type \== OtherType,
last_count(Type, Count, Rest).
assign_helper([], [], 0).
assign_helper([Person|PeopleRest], [[Time, Count]|Rest], 0) :-
assign_helper(PeopleRest, Rest, 0),
available(Person, Time, 'Yes'),
last_count(Time, LastCount, Rest),
Count is LastCount + 1,
spots_at_time(Time, MaxCount),
Count =< MaxCount.
assign_helper([Person|PeopleRest], [[Time, Count]|Rest], MaybeCount) :-
assign_helper(PeopleRest, Rest, LastMaybeCount),
available(Person, Time, 'Maybe'),
last_count(Time, LastCount, Rest),
Count is LastCount + 1,
MaybeCount is LastMaybeCount + 1,
spots_at_time(Time, MaxCount),
Count =< MaxCount.
times_process([], [], []).
times_process([P|PRest], [[T, _C]|TCRest], [[P, T]|Rest]) :-
times_process(PRest, TCRest, Rest).
assign(Assignments, MaxMaybes) :-
bagof(E, person(E), People),
assign_helper(People, TimesCounts, MaybeCount),
MaybeCount =< MaxMaybes,
times_process(People, TimesCounts, Assignments).
assign(Assignments) :- assign(Assignments, 0).
display([Person, Time]) :-
write(Person),
write(", "),
writeln(Time).
run :- run(0).
run(MaxMaybes) :-
assign(Assignments, MaxMaybes),
maplist(display, Assignments).
"""
print('/* Generated by csv_to_prolog.py */')
print()
print('/* Data */')
times = None
counts = None
people = []
for line in sys.stdin:
line = line.strip()
parts = [x.strip() for x in line.split(',')]
if not times:
times = parts[1:]
continue
name = parts[0]
if not name:
continue
if name == 'counts':
counts = [int(c) for c in parts[1:]]
for time, count in zip(times, counts):
print("spots_at_time('{}', {}).".format(time, count))
continue
people.append((name, parts[1:],
sum([5*c if x == 'Yes' else \
(1*c if x == 'Maybe' else 0) for c, x in zip(counts, parts[1:])])))
if len(people) > sum(counts):
print("Warning! {} spots for {} people".format(sum(counts), len(people)), \
file=sys.stderr)
people = sorted(people, key=lambda x: -x[2]) # least constrained first
if len(sys.argv) > 1:
people = people[:int(sys.argv[1])]
for name, _, _ in people:
print("person('{}').".format(name))
for name, prefs, score in people:
how_many = 0
for time, pref in zip(times, prefs):
if pref == 'No':
continue
assert(pref in ['Yes', 'Maybe'])
how_many += 1
print("available('{}', '{}', '{}').".format(name, time, pref))
if not how_many:
print("Warning! {} has no availability".format(name), file=sys.stderr)
#print('all_people([{}]).'.format(', '.join(people)))
print(prolog_body)
Name Wed 4-5:30pm Wed 5-6:30pm Wed 5:30-7pm Th 9:30-11am Th 11am-12:30pm Th 12:30-2pm Th 2-3:30pm Th 3:30-5pm Th 5-6:30pm Fri 9:30-11am Fri 11am-12:30pm Fri 12:30-2pm
counts 1 1 2 3 3 3 5 6 5 3 3 3
A No No No No No Maybe Yes No No No Maybe Maybe
B Maybe No No No No No Maybe Maybe No No Yes Maybe
C Yes Yes Yes No Maybe No No Maybe No No No Yes
D No No Maybe No Maybe Maybe No No Yes Maybe No Yes
E No No No No No No Maybe Maybe No Yes No Yes
F No No No Yes Yes Maybe No No Maybe No Maybe No
G No Maybe Maybe Maybe No Yes Maybe No Maybe Maybe Yes Yes
H Maybe Yes Maybe No No No No No No No No No
I Yes Yes Maybe No No No No No No No No Maybe
J Yes Maybe No No Yes Yes No Yes Maybe Yes No Yes
K Yes Yes Yes Maybe No No Maybe Maybe No No No Maybe
L Yes Maybe Maybe Yes No No No No Maybe Yes Yes Yes
M Yes Yes Yes No No No Maybe No No No No Yes
N No Yes Maybe No Maybe Yes Yes Yes Yes No No Yes
O No No No No Yes No Maybe No Yes Maybe Yes No
P No No No Yes Yes No No Yes Yes No Maybe Yes
Q Maybe No No Yes No Maybe No Maybe Maybe No No No
R No No No No No Maybe No No No No No Yes
S No No No No No Yes Yes Maybe No No Maybe Maybe
T No No Maybe No No No No Yes Yes Maybe No No
U Maybe Maybe Maybe No Yes Yes No No Maybe Maybe No Yes
V No No No No Maybe Yes Yes Yes No No No No
W Yes Yes Yes No No Maybe Yes Yes No No No No
X Maybe No No No Maybe Maybe Yes Yes Maybe No No No
Y No Maybe Maybe No Yes Yes Maybe No Maybe No No Maybe
Z No No No No No Yes No No Maybe No No Maybe
AA No No No No Maybe Maybe No No No No Maybe Yes
BB No Yes Yes Maybe Maybe No Yes No Yes Maybe No No
CC Yes Maybe No No Yes Yes Yes Maybe No No No No
DD No Maybe Yes Yes Maybe No Maybe Maybe Yes Yes Yes Yes
EE No No No Yes No Maybe Maybe No No No No No
FF Yes No No Yes No Yes No No Maybe Yes Yes No
GG Maybe Maybe No No Maybe No No Yes Maybe Yes Maybe Maybe
HH No Maybe Yes No No No No No Yes Maybe No No
II No No No No Yes Yes No No Maybe No No No
JJ Yes Maybe No No No No Yes Maybe No No Maybe No
KK No No Yes Maybe No No No No No Maybe Yes Yes
LL Maybe Maybe No Yes No No Yes Yes Maybe Maybe No No
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment