Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion lectern/recon_discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,25 @@ def discover_repos(roster_csv: Path, *, repo_prefix: str) -> list[RepoRef]:
refs: list[RepoRef] = []
with Path(roster_csv).open(newline="") as f:
for row in csv.DictReader(f):
gid = (row.get("github_username") or row.get("github_id") or "").strip()
# Identifier precedence. `username` must outrank `github_id` because
# a Classroom 50 roster.csv uses BOTH columns with different meanings
# than a legacy GitHub Classroom roster does: `username` is the login
# and `github_id` is the immutable NUMERIC id. Legacy rosters put the
# login in `github_id` (see examples/.../.cohort-spec.json), so that
# stays in the chain as the last resort and old behavior is unchanged
# whenever `username` is absent. Reading `github_id` first would build
# repo names like `<prefix>-1548364` instead of `<prefix>-agiacalone`.
gid = (row.get("github_username") or row.get("username")
or row.get("github_id") or "").strip()
if not gid:
continue
name = (row.get("student_name") or row.get("canonical_name")
or row.get("name") or "").strip()
if not name:
# Classroom 50 splits the name across two columns.
name = " ".join(p for p in (
(row.get("first_name") or "").strip(),
(row.get("last_name") or "").strip(),
) if p)
refs.append(RepoRef(github_id=gid, student=name, repo=f"{repo_prefix}{gid}"))
return refs
58 changes: 58 additions & 0 deletions tests/test_recon_discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,61 @@ def test_discover_uses_canonical_name(tmp_path):
refs = discover_repos(csv_path, repo_prefix="pre-")
assert refs[0].student == "Barbara Gordon"
assert refs[0].repo == "pre-lucfox"


# --- Classroom 50 roster shape -------------------------------------------
# A C50 roster.csv carries BOTH `username` (the login) and `github_id` (the
# immutable NUMERIC id), and splits the name across first_name/last_name.
# A legacy GitHub Classroom roster put the LOGIN in `github_id`, so the two
# formats collide on that column name and precedence is what separates them.

C50_HEADER = "username,first_name,last_name,email,section,github_id,role\n"


def test_discover_c50_roster_prefers_username_over_numeric_github_id(tmp_path):
"""Reading github_id first would name the repo `<prefix>-1548364`."""
csv_path = tmp_path / "roster.csv"
csv_path.write_text(C50_HEADER +
"agiacalone,Anthony,Giacalone,,section-1,1548364,student\n")
refs = discover_repos(csv_path, repo_prefix="c50-tst0-lab-01-")
assert len(refs) == 1
assert refs[0].github_id == "agiacalone"
assert refs[0].repo == "c50-tst0-lab-01-agiacalone"


def test_discover_c50_roster_composes_name_from_first_last(tmp_path):
csv_path = tmp_path / "roster.csv"
csv_path.write_text(C50_HEADER +
"bruce-wayne,Bruce,Wayne,,section-1,,student\n")
refs = discover_repos(csv_path, repo_prefix="pre-")
assert refs[0].student == "Bruce Wayne"


def test_discover_c50_roster_keeps_rows_with_unresolved_github_id(tmp_path):
"""A blank numeric github_id must not drop the student from the population."""
csv_path = tmp_path / "roster.csv"
csv_path.write_text(C50_HEADER +
"bruce-wayne,Bruce,Wayne,,section-1,,student\n"
"dick-grayson,Dick,Grayson,,section-1,,student\n")
refs = discover_repos(csv_path, repo_prefix="pre-")
assert [r.repo for r in refs] == ["pre-bruce-wayne", "pre-dick-grayson"]


def test_discover_legacy_github_id_login_still_wins_when_no_username(tmp_path):
"""Back-compat: legacy rosters store the LOGIN in github_id and have no
`username` column, so behavior there must be unchanged."""
csv_path = tmp_path / "roster.csv"
csv_path.write_text("github_id,name\nbruce-wayne,Bruce Wayne\n")
refs = discover_repos(csv_path, repo_prefix="cecs-378-su26-01-lab-03-")
assert refs[0].github_id == "bruce-wayne"
assert refs[0].repo == "cecs-378-su26-01-lab-03-bruce-wayne"
assert refs[0].student == "Bruce Wayne"


def test_discover_explicit_github_username_outranks_c50_username(tmp_path):
"""`github_username` stays top of the chain as the explicit override."""
csv_path = tmp_path / "roster.csv"
csv_path.write_text("username,github_username,first_name,last_name\n"
"campus-sso-id,reallogin,Barbara,Gordon\n")
refs = discover_repos(csv_path, repo_prefix="pre-")
assert refs[0].repo == "pre-reallogin"
Loading