Skip to content

Instantly share code, notes, and snippets.

@agoose77
Created July 15, 2026 13:31
Show Gist options
  • Select an option

  • Save agoose77/4bc5ffbec05cdb33802982c322bbdb63 to your computer and use it in GitHub Desktop.

Select an option

Save agoose77/4bc5ffbec05cdb33802982c322bbdb63 to your computer and use it in GitHub Desktop.
migrate-users.py
import argparse
import collections
import csv
import escapism
import string
import sys
def escape_email(email):
return escapism.escape(email, safe=safe_chars, escape_char="-").lower()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("names", type=argparse.FileType("r"))
parser.add_argument(
"-r", "--rate", type=int, default=500, help="Links to create/remove per second"
)
args = parser.parse_args()
reader = csv.DictReader(args.names)
safe_chars = set(string.ascii_lowercase + string.digits)
entries = []
seen = []
for row in reader:
src = escape_email(row["jupyter_email"])
dst = row["sis_user_id"]
seen.append(src)
seen.append(dst)
entries.append((src, dst))
seen_counts = collections.Counter(seen)
print(len(entries))
with open("create-links.sh", "w") as cf, open("remove-links.sh", "w") as rf, open("report-links.sh", "w") as rpf:
for f in rf, cf, rpf:
f.write("#!/usr/bin/env bash\n")
f.write("set -eux\n")
rpf.write("set +x\n")
n_written = 0
for src, dst in entries:
if seen_counts[src] > 1 or seen_counts[dst] > 1 or not dst:
print("skipping", src, dst, file=sys.stderr, flush=True)
continue
cf.write(f"test -L '{dst}' || ln -s '{src}' '{dst}'\n")
rf.write(f"test -L '{dst}' && rm '{dst}'\n")
rpf.write(f"test -L '{dst}' && echo '{dst} exists!'\n")
n_written += 1
if not n_written % args.rate:
sleep_command = "sleep 1"
cf.write(f"{sleep_command}\n")
rf.write(f"{sleep_command}\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment