Skip to content

Instantly share code, notes, and snippets.

@Andrew-William-Smith
Last active September 4, 2020 02:41
Show Gist options
  • Save Andrew-William-Smith/4dd4e120a7f10f034851cb725236e675 to your computer and use it in GitHub Desktop.
Save Andrew-William-Smith/4dd4e120a7f10f034851cb725236e675 to your computer and use it in GitHub Desktop.
Simple script to create breakout rooms and assign scribes for discussion sections.

Discussion Section Breakout Room Assignments

This script generates evenly-distributed breakout rooms for course discussion sections, in addition to appointing scribes for each section in round-robin style. It was intended for the discussion section model we use in CS 314, although it can be applied to any course with little or no modification.

Setup instructions

Download the script breakout.py to a file on your machine, or clone this gist to a local directory. In the same directory, create a file named students.csv containing each student's information in the following format:

Student 1 Name,student1eid,0
Student 2 Name,student2eid,0
...

You are now ready to run the script! Use the following command to run it:

python3 breakout.py

Once it is finished, you should see a file named zoom.csv containing breakout room definitions that you can upload to Zoom using the "Import from CSV" feature after checking "Breakout Room pre-assign" in your meeting's options. Be sure to only edit single occurrences of meetings at a time, lest you accidentally appoint a scribe for the entire semester. Furthermore, once the script has finished, you should see that the scribeship counts (the final column) in students.csv have been updated to reflect the new scribe assignments: as such, you should retain one students.csv file updated by the script for the entire semester. Note that Canvas does not currently provide a way to automatically apportion student groups, so this script does not provide Canvas integration at this time.

from typing import List
import csv
import math
import random
# Step 1: Create a file "students.csv" in the same directory as this script,
# containing each student's information in the following format:
# >> Student 1 Name,student1eid,0
# >> Student 2 Name,student2eid,0
# If you do not create this file, you'll receive a FileNotFoundError
# when you run the script. You should only need to create this file
# once; it will be updated for subsequent runs of the script.
# Step 2: Customise to the maximum group size that you would prefer. Groups of
# at most 3 or 4 students are recommended.
GROUP_SIZE = 4
class Student:
"""
A simple class representing a student's name, EID, and the number of times
they have served as a group's scribe.
"""
def __init__(self, name: str, eid: str, scribeships: int):
self.name = name
self.eid = eid
self.scribeships = scribeships
def __repr__(self):
return f'<Student name=\"{self.name}\" scribeships={self.scribeships}>'
def __lt__(self, other):
return self.scribeships < other.scribeships
def add_scribeship(self):
"""Add a scribeship to this student's record."""
self.scribeships += 1
def fair_partition(lst: List[Student],
part_size: int,
distribute: List[Student] = []) -> List[List[Student]]:
"""
Partition the specified list "fairly" into groups with the specified size,
ensuring that all groups have a roughly even number of constituents.
Furthermore, ensures that one of the people in the distribute list is
allocated to each group as far as is possible. Returns a list of the
generated partitions.
"""
# Precondition: ensure that set difference works properly
if len(distribute) > len(lst):
raise ValueError(
"Cannot have more mandatory constituents than constituents.")
# Shuffle the list to ensure random allocation
shuffled = lst.copy()
random.shuffle(shuffled)
# Determine how many partitions into which we can divide the constituents
num_groups = math.ceil(len(lst) / part_size)
partitions = []
# Handle mandatory distribution first
for i in range(num_groups):
if i < len(distribute):
new_member = distribute[i]
else:
new_member = shuffled[i - len(distribute)]
# Record that this constituent has been distributed
partitions.append([new_member])
shuffled.remove(new_member)
new_member.add_scribeship()
# Distribute remaining constituents
for i in range(len(shuffled)):
partitions[i % num_groups].append(shuffled[i])
return partitions
def write_zoom_csv(groups: List[List[Student]]) -> None:
"""
Create a file named "zoom.csv" containing a description of the specified
groups that is compatible with Zoom's breakout room format.
"""
with open('zoom.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Pre-assign Room Name', 'Email Address'])
for i in range(len(groups)):
scribe_name = groups[i][0].name
writer.writerows(
[[f'Group {i + 1} (Scribe: {scribe_name})',
f'{s.eid}@eid.utexas.edu'] for s in groups[i]])
def write_students_csv(students: List[Student]) -> None:
"""
Re-write the file "students.csv" with updated scribeship counts.
"""
with open('students.csv', 'w', newline='') as f:
csv.writer(f).writerows(
[[s.name, s.eid, str(s.scribeships)] for s in students])
if __name__ == '__main__':
# Read in all students
students = []
with open('students.csv', 'r') as f:
reader = csv.reader(f)
for name, eid, scribeships in reader:
students.append(Student(name, eid, int(scribeships)))
# Determine the first student who can serve as a scribe
students.sort(reverse=True)
max_scribeships = students[0].scribeships
distribute_start = 0
while (distribute_start < len(students) and
students[distribute_start].scribeships == max_scribeships):
distribute_start += 1
if distribute_start == len(students):
distribute_start = 0
# Create groups each having a scribe
candidate_scribes = students[distribute_start:]
candidate_scribes.reverse()
groups = fair_partition(students, GROUP_SIZE, candidate_scribes)
# Write out files
write_zoom_csv(groups)
write_students_csv(students)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment