From 32a2ff1da0484c60ce4aee0a550bc937f3fd72f0 Mon Sep 17 00:00:00 2001 From: 3wc <3wc@doesthisthing.work> Date: Wed, 22 Mar 2023 01:41:37 -0400 Subject: [PATCH] Initial import --- .gitignore | 1 + README.md | 6 +++ wraparound.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 wraparound.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a9e73c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/data.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..2422cc0 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# completely-fair-scheduler + +1. ask people to fill out a https://crab.fit poll, answering the question "what + times would you be willing to join a meeting?" +2. `curl https://api.crab.fit/event//people > data.json` +3. `python wraparound.py` diff --git a/wraparound.py b/wraparound.py new file mode 100644 index 0000000..efa0220 --- /dev/null +++ b/wraparound.py @@ -0,0 +1,100 @@ +import argparse +import itertools +import json +import sys +from calendar import day_abbr, day_name +from math import floor + +with open("data.json") as jsonfile: + raw_data = json.loads(jsonfile.read()) + +parser = argparse.ArgumentParser( + prog=sys.argv[0], description="Find autono-meeting times" +) + +parser.add_argument("members", nargs="*", choices=[m["name"] for m in raw_data]) +parser.add_argument("-n", "--number", default=2, type=int) +parser.add_argument("-d", "--days", nargs="+") +parser.add_argument("-v", "--verbose", action='store_true') + +args = parser.parse_args() + +data = {} + + +def slot2name(slot): + time, day = slot.split("-") + name = day_name[int(day) - 1] + return f"{time} {name}" + + +members_count = len(raw_data) +if args.members: + members_count = len(args.members) + +if args.verbose: + print(f"I: finding slots for {members_count} members") + +days = [str(d) for d in list(range(1, 8))] +if args.days: + days = [str(list(day_abbr).index(d) + 1) for d in args.days] + +for member in raw_data: + name = member["name"] + if args.members and name not in args.members: + continue + for slot in member["availability"]: + if "00-" not in slot: + # not starting at an hour + continue + slot_day = slot.split("-")[1] + if slot_day not in days: + # not in specified days + continue + try: + data[slot].append(name) + except KeyError: + data[slot] = [name] + +if args.verbose: + print(f"I: Checking {len(data.keys())} possible slots") + +seen_times = [] +counter = 1 + +l = [list(data.keys())] * args.number +combinations = list(itertools.product(*l)) + +if args.verbose: + print(f"I: Scanning {len(combinations)} combinations") + +for combination in combinations: + if len(set(combination)) < args.number: + continue + if set(combination) in seen_times: + continue + seen_times.append(set(combination)) + + attendees_per_slot = [data[slot] for slot in combination] + attendees = set(itertools.chain(*attendees_per_slot)) + + if ( + (len(attendees) == members_count) + and all( + [len(s) >= floor(members_count / args.number) for s in attendees_per_slot] + ) + and all( + [ + len(s) < (floor(members_count / args.number) + 2) + for s in attendees_per_slot + ] + ) + ): + print(f"Option {counter}") + for i, slot in enumerate(combination): + print(f" Meeting {i+1}: ", slot2name(slot)) + print(" ", ", ".join(attendees_per_slot[i])) + counter += 1 + +if counter == 1: + print("Oh no, no slots found")