Collectables Assignment - OCS Mentor Dashboard
Collect mentor records from a database into AbstractCollectable classes, then use a Registry to find which capstones still need mentors. Created by CSA Admin - Shayan, Jim, Lucas, Jacob, Harrish, Arnav, Rudra, and Dhyan.
Collectables Assignment: OCS Mentor Dashboard
The OCS mentor dashboard (/mentor) shows which capstone projects each mentor is on. To recruit the right mentors, OCS needs one answer from the mentor database: which capstones still need a mentor, and in what area? You will collect raw database rows into AbstractCollectable classes (from the Abstract Collectables lesson) and let the Registry sort and group them.
Hacks
Three hacks, each in its own runner below. Every runner is a complete program, so it runs on its own.
Hack #1 (Weight: 30%)
Build the Expertise attribute class. Extend AbstractCollectable with private rank, area, and focus. getMap() returns Rank, Area, Focus in that order; compareTo() orders by rank, not alphabetically; catalog() holds Frontend (0), Backend (1), Data & AI (2), and DevOps (3), added out of order. Show insertion order next to natural sort, and explain why rank order fits the dashboard.
Hack #2 (Weight: 30%)
Build the Mentor entity that has-a Expertise. getOrg() returns the email domain, getOpenSlots() returns capacity minus assigned, getMap() exposes Name, UID, Org, Expertise, Assigned, and Open Slots, and compareTo() sorts names ignoring case. Show Ada Rivera’s row (techcorp.example, 1 open slot) and explain why derived fields belong in getMap().
Hack #3 (Weight: 40%)
Build the mentor dashboard with a Registry. loadMentors turns database rows into mentors and skips the bad Blockchain row without crashing. Print orderBy("Open Slots") and groupBy("Expertise"). mentorGaps groups unmentored capstones by Needs, subtracts open slots, and reports which areas OCS must recruit for (expected: 2 Backend). Explain that result in your own words.
Grading Plan (1 Point Total)
| Hack | Weight | What earns the points |
|---|---|---|
#1 Expertise |
30% | Rank/Area/Focus map, rank-based compareTo, all 4 areas out of order, insertion vs sorted shown and explained. |
#2 Mentor |
30% | Org and Open Slots derived and in getMap(), case-insensitive sort, Ada’s row shown and explained. |
| #3 Dashboard | 40% | Bad row skipped, orderBy and groupBy shown, mentorGaps finds 2 Backend, result explained. |
Each hack is scored on its own. Specific output lines, edge cases, and explained reasoning score highest.
Submit
- Copy this notebook into your portfolio at
_notebooks/homework, and keep the Hack #1, Hack #2, and Hack #3 headings. - Finish each runner until its Self-Check passes. Hacks #2 and #3 start with copies of earlier classes; paste your finished versions over them.
- Under each hack, add a markdown cell with the key output lines and 2-3 sentences on why it works. The grader reads your notebook’s code and markdown, not its outputs.
- Submit the GitHub link to your .ipynb file (it looks like
github.com/<you>/<repo>/blob/main/_notebooks/homework/<file>.ipynb). A GitHub Pages link cannot be graded.
Hack #1: Expertise
Problem: Build the Expertise attribute class. Extend AbstractCollectable with private rank, area, and focus. getMap() returns Rank, Area, Focus in that order; compareTo() orders by rank, not alphabetically; catalog() holds Frontend (0), Backend (1), Data & AI (2), and DevOps (3), added out of order. Show insertion order next to natural sort, and explain why rank order fits the dashboard.
| Rank | Area | Focus |
|---|---|---|
| 0 | Frontend | UI, SASS, accessibility |
| 1 | Backend | APIs, Spring, databases |
| 2 | Data & AI | analytics, ML, datasets |
| 3 | DevOps | deployment, AWS, Docker |
Code Runner Challenge
Hack #1 - finish the Expertise TODOs, then Run until 3/3
View IPYNB Source
// CODE_RUNNER: Hack #1 - finish the Expertise TODOs, then Run until 3/3
import java.util.*;
// ===== Lesson foundation (given, do not edit) =====
/** Contract for every dashboard record: project fields into a map, sort naturally. */
interface Collectable extends Comparable<Collectable> {
Map<String, Object> getMap();
List<String> getKeys();
Object getValue(String key);
}
/** Shared code: subclasses only write getMap() and compareTo(). */
abstract class AbstractCollectable implements Collectable {
public List<String> getKeys() { return new ArrayList<>(getMap().keySet()); }
public Object getValue(String key) { return getMap().get(key); }
public String toString() { return getMap().toString(); }
}
/** A typed list of collectables. */
class Catalog<T extends Collectable> {
private final List<T> items = new ArrayList<>();
public void add(T item) { items.add(item); }
public List<T> getItems() { return items; }
public void display() { items.forEach(System.out::println); }
}
/** Sorts, orders, and groups any catalog by its map keys. */
class Registry {
private final List<Collectable> items;
public Registry(Catalog<? extends Collectable> catalog) { items = new ArrayList<>(catalog.getItems()); }
/** Prints the sort key first. */
record ProjectedCollectable(Collectable delegate, String primaryKey) implements Collectable {
public Map<String, Object> getMap() {
Map<String, Object> original = delegate.getMap();
Map<String, Object> reordered = new LinkedHashMap<>();
if (original.containsKey(primaryKey)) reordered.put(primaryKey, original.get(primaryKey));
original.forEach((k, v) -> { if (!k.equals(primaryKey)) reordered.put(k, v); });
return reordered;
}
public List<String> getKeys() { return new ArrayList<>(getMap().keySet()); }
public Object getValue(String key) { return getMap().get(key); }
public int compareTo(Collectable o) { return delegate.compareTo(o); }
public String toString() { return getMap().toString(); }
}
public List<Collectable> sortNatural() {
List<Collectable> sorted = new ArrayList<>(items);
Collections.sort(sorted);
return sorted;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public List<Collectable> sortByField(String key) {
List<Collectable> sorted = new ArrayList<>(items);
sorted.sort((a, b) -> {
Object valA = a.getValue(key), valB = b.getValue(key);
return (valA instanceof Comparable && valB instanceof Comparable) ? ((Comparable) valA).compareTo(valB) : 0;
});
return sorted;
}
public List<ProjectedCollectable> orderBy(String key) {
return sortByField(key).stream().map(item -> new ProjectedCollectable(item, key)).toList();
}
public Map<Object, List<Collectable>> groupBy(String key) {
Map<Object, List<Collectable>> groups = new LinkedHashMap<>();
for (Collectable item : items) groups.computeIfAbsent(item.getValue(key), k -> new ArrayList<>()).add(item);
return groups;
}
}
// ===== Hack #1: Expertise =====
/** A mentoring area. Natural order is rank, not alphabetical. */
class Expertise extends AbstractCollectable {
private final int rank;
private final String area;
private final String focus;
Expertise(int rank, String area, String focus) {
this.rank = rank;
this.area = area;
this.focus = focus;
}
public String getArea() { return area; }
public Map<String, Object> getMap() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Area", area);
// TODO: put "Rank" first, then "Area", then "Focus"
return map;
}
public int compareTo(Collectable o) {
// TODO: compare by rank (hint: Integer.compare and o.getValue("Rank"))
return 0;
}
static Catalog<Expertise> catalog() {
Catalog<Expertise> catalog = new Catalog<>();
catalog.add(new Expertise(2, "Data & AI", "analytics, ML, datasets"));
// TODO: add Frontend (0), Backend (1), DevOps (3) -- out of order on purpose
return catalog;
}
/** Throws for an unknown area, so bad database rows get caught. (given) */
static Expertise find(Catalog<Expertise> catalog, String area) {
for (Expertise e : catalog.getItems()) if (e.getArea().equals(area)) return e;
throw new IllegalArgumentException("unknown expertise " + area);
}
}
// ===== Run Hack #1 =====
public class Main {
public static void main(String[] args) {
Catalog<Expertise> areas = Expertise.catalog();
System.out.println("=== Expertise, insertion order ===");
areas.display();
System.out.println("\n=== Expertise, natural sort (rank) ===");
new Registry(areas).sortNatural().forEach(System.out::println);
System.out.println("\n=== Self-Check: Hack #1 ===");
List<Object> sorted = new ArrayList<>();
new Registry(areas).sortNatural().forEach(e -> sorted.add(e.getValue("Area")));
check("4 areas in the catalog", areas.getItems().size() == 4);
check("getMap() keys are Rank, Area, Focus",
!areas.getItems().isEmpty() && areas.getItems().get(0).getKeys().equals(List.of("Rank", "Area", "Focus")));
check("natural sort is Frontend, Backend, Data & AI, DevOps",
sorted.equals(List.of("Frontend", "Backend", "Data & AI", "DevOps")));
System.out.println(passed + "/" + total + " Hack #1 checks passed");
}
static int passed = 0, total = 0;
static void check(String name, boolean ok) {
total++;
if (ok) passed++;
System.out.println((ok ? "PASS " : "FAIL ") + name);
}
}
Main.main(null);
Hack #2: Mentor
Problem: Build the Mentor entity that has-a Expertise. getOrg() returns the email domain, getOpenSlots() returns capacity minus assigned, getMap() exposes Name, UID, Org, Expertise, Assigned, and Open Slots, and compareTo() sorts names ignoring case. Show Ada Rivera’s row (techcorp.example, 1 open slot) and explain why derived fields belong in getMap().
Paste your finished Expertise from Hack #1 over the copy at the top of this runner.
Code Runner Challenge
Hack #2 - finish the Mentor TODOs, then Run until 4/4
View IPYNB Source
// CODE_RUNNER: Hack #2 - finish the Mentor TODOs, then Run until 4/4
import java.util.*;
// ===== Lesson foundation (given, do not edit) =====
/** Contract for every dashboard record: project fields into a map, sort naturally. */
interface Collectable extends Comparable<Collectable> {
Map<String, Object> getMap();
List<String> getKeys();
Object getValue(String key);
}
/** Shared code: subclasses only write getMap() and compareTo(). */
abstract class AbstractCollectable implements Collectable {
public List<String> getKeys() { return new ArrayList<>(getMap().keySet()); }
public Object getValue(String key) { return getMap().get(key); }
public String toString() { return getMap().toString(); }
}
/** A typed list of collectables. */
class Catalog<T extends Collectable> {
private final List<T> items = new ArrayList<>();
public void add(T item) { items.add(item); }
public List<T> getItems() { return items; }
public void display() { items.forEach(System.out::println); }
}
/** Sorts, orders, and groups any catalog by its map keys. */
class Registry {
private final List<Collectable> items;
public Registry(Catalog<? extends Collectable> catalog) { items = new ArrayList<>(catalog.getItems()); }
/** Prints the sort key first. */
record ProjectedCollectable(Collectable delegate, String primaryKey) implements Collectable {
public Map<String, Object> getMap() {
Map<String, Object> original = delegate.getMap();
Map<String, Object> reordered = new LinkedHashMap<>();
if (original.containsKey(primaryKey)) reordered.put(primaryKey, original.get(primaryKey));
original.forEach((k, v) -> { if (!k.equals(primaryKey)) reordered.put(k, v); });
return reordered;
}
public List<String> getKeys() { return new ArrayList<>(getMap().keySet()); }
public Object getValue(String key) { return getMap().get(key); }
public int compareTo(Collectable o) { return delegate.compareTo(o); }
public String toString() { return getMap().toString(); }
}
public List<Collectable> sortNatural() {
List<Collectable> sorted = new ArrayList<>(items);
Collections.sort(sorted);
return sorted;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public List<Collectable> sortByField(String key) {
List<Collectable> sorted = new ArrayList<>(items);
sorted.sort((a, b) -> {
Object valA = a.getValue(key), valB = b.getValue(key);
return (valA instanceof Comparable && valB instanceof Comparable) ? ((Comparable) valA).compareTo(valB) : 0;
});
return sorted;
}
public List<ProjectedCollectable> orderBy(String key) {
return sortByField(key).stream().map(item -> new ProjectedCollectable(item, key)).toList();
}
public Map<Object, List<Collectable>> groupBy(String key) {
Map<Object, List<Collectable>> groups = new LinkedHashMap<>();
for (Collectable item : items) groups.computeIfAbsent(item.getValue(key), k -> new ArrayList<>()).add(item);
return groups;
}
}
// ===== From Hack #1: paste your finished Expertise over this class =====
/** A mentoring area. Natural order is rank, not alphabetical. */
class Expertise extends AbstractCollectable {
private final int rank;
private final String area;
private final String focus;
Expertise(int rank, String area, String focus) {
this.rank = rank;
this.area = area;
this.focus = focus;
}
public String getArea() { return area; }
public Map<String, Object> getMap() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Area", area);
// TODO: put "Rank" first, then "Area", then "Focus"
return map;
}
public int compareTo(Collectable o) {
// TODO: compare by rank (hint: Integer.compare and o.getValue("Rank"))
return 0;
}
static Catalog<Expertise> catalog() {
Catalog<Expertise> catalog = new Catalog<>();
catalog.add(new Expertise(2, "Data & AI", "analytics, ML, datasets"));
// TODO: add Frontend (0), Backend (1), DevOps (3) -- out of order on purpose
return catalog;
}
/** Throws for an unknown area, so bad database rows get caught. (given) */
static Expertise find(Catalog<Expertise> catalog, String area) {
for (Expertise e : catalog.getItems()) if (e.getArea().equals(area)) return e;
throw new IllegalArgumentException("unknown expertise " + area);
}
}
// ===== Hack #2: Mentor =====
/** A mentor record. HAS-A Expertise. */
class Mentor extends AbstractCollectable {
private final String uid, name, email;
private final Expertise expertise;
private final int capacity, assigned;
Mentor(String uid, String name, String email, Expertise expertise, int capacity, int assigned) {
this.uid = uid;
this.name = name;
this.email = email;
this.expertise = expertise;
this.capacity = capacity;
this.assigned = assigned;
}
public Expertise getExpertise() { return expertise; }
public String getOrg() {
// TODO: return the part of the email after "@"
return "";
}
public int getOpenSlots() {
// TODO: capacity minus assigned
return 0;
}
public Map<String, Object> getMap() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Name", name);
map.put("UID", uid);
// TODO: put "Org", "Expertise" (the area name), "Assigned", and "Open Slots"
return map;
}
public int compareTo(Collectable o) {
// TODO: compare names ignoring case, so "frank kim" sorts with the F's
return 0;
}
}
// ===== Run Hack #2 =====
public class Main {
public static void main(String[] args) {
Expertise backend = new Expertise(1, "Backend", "APIs, Spring, databases");
Mentor ada = new Mentor("arivera", "Ada Rivera", "ada.rivera@techcorp.example", backend, 2, 1);
Mentor frank = new Mentor("fkim", "frank kim", "frank@designlab.example", backend, 2, 0);
Mentor grace = new Mentor("gnguyen", "Grace Nguyen", "grace@chainco.example", backend, 1, 0);
System.out.println("=== Ada's dashboard row ===");
System.out.println(ada);
System.out.println("Org: " + ada.getValue("Org") + ", Open Slots: " + ada.getValue("Open Slots"));
Catalog<Mentor> trio = new Catalog<>();
trio.add(grace);
trio.add(ada);
trio.add(frank);
System.out.println("\n=== Natural sort (name, ignoring case) ===");
new Registry(trio).sortNatural().forEach(m -> System.out.println(m.getValue("Name")));
System.out.println("\n=== Self-Check: Hack #2 ===");
check("getMap() has Name, UID, Org, Expertise, Assigned, Open Slots",
ada.getKeys().containsAll(List.of("Name", "UID", "Org", "Expertise", "Assigned", "Open Slots")));
check("Org is the email domain", "techcorp.example".equals(ada.getValue("Org")));
check("Open Slots = capacity - assigned", Integer.valueOf(1).equals(ada.getValue("Open Slots")));
check("natural sort ignores case (frank kim before Grace)", frank.compareTo(grace) < 0);
System.out.println(passed + "/" + total + " Hack #2 checks passed");
}
static int passed = 0, total = 0;
static void check(String name, boolean ok) {
total++;
if (ok) passed++;
System.out.println((ok ? "PASS " : "FAIL ") + name);
}
}
Main.main(null);
Hack #3: Mentor Dashboard
Problem: Build the mentor dashboard with a Registry. loadMentors turns database rows into mentors and skips the bad Blockchain row without crashing. Print orderBy("Open Slots") and groupBy("Expertise"). mentorGaps groups unmentored capstones by Needs, subtracts open slots, and reports which areas OCS must recruit for (expected: 2 Backend). Explain that result in your own words.
The database rows and Capstone class are given. Paste your finished Expertise and Mentor over the copies in this runner first.
Code Runner Challenge
Hack #3 - finish loadMentors, the demos, and mentorGaps, then Run until 3/3
View IPYNB Source
// CODE_RUNNER: Hack #3 - finish loadMentors, the demos, and mentorGaps, then Run until 3/3
import java.util.*;
// ===== Lesson foundation (given, do not edit) =====
/** Contract for every dashboard record: project fields into a map, sort naturally. */
interface Collectable extends Comparable<Collectable> {
Map<String, Object> getMap();
List<String> getKeys();
Object getValue(String key);
}
/** Shared code: subclasses only write getMap() and compareTo(). */
abstract class AbstractCollectable implements Collectable {
public List<String> getKeys() { return new ArrayList<>(getMap().keySet()); }
public Object getValue(String key) { return getMap().get(key); }
public String toString() { return getMap().toString(); }
}
/** A typed list of collectables. */
class Catalog<T extends Collectable> {
private final List<T> items = new ArrayList<>();
public void add(T item) { items.add(item); }
public List<T> getItems() { return items; }
public void display() { items.forEach(System.out::println); }
}
/** Sorts, orders, and groups any catalog by its map keys. */
class Registry {
private final List<Collectable> items;
public Registry(Catalog<? extends Collectable> catalog) { items = new ArrayList<>(catalog.getItems()); }
/** Prints the sort key first. */
record ProjectedCollectable(Collectable delegate, String primaryKey) implements Collectable {
public Map<String, Object> getMap() {
Map<String, Object> original = delegate.getMap();
Map<String, Object> reordered = new LinkedHashMap<>();
if (original.containsKey(primaryKey)) reordered.put(primaryKey, original.get(primaryKey));
original.forEach((k, v) -> { if (!k.equals(primaryKey)) reordered.put(k, v); });
return reordered;
}
public List<String> getKeys() { return new ArrayList<>(getMap().keySet()); }
public Object getValue(String key) { return getMap().get(key); }
public int compareTo(Collectable o) { return delegate.compareTo(o); }
public String toString() { return getMap().toString(); }
}
public List<Collectable> sortNatural() {
List<Collectable> sorted = new ArrayList<>(items);
Collections.sort(sorted);
return sorted;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public List<Collectable> sortByField(String key) {
List<Collectable> sorted = new ArrayList<>(items);
sorted.sort((a, b) -> {
Object valA = a.getValue(key), valB = b.getValue(key);
return (valA instanceof Comparable && valB instanceof Comparable) ? ((Comparable) valA).compareTo(valB) : 0;
});
return sorted;
}
public List<ProjectedCollectable> orderBy(String key) {
return sortByField(key).stream().map(item -> new ProjectedCollectable(item, key)).toList();
}
public Map<Object, List<Collectable>> groupBy(String key) {
Map<Object, List<Collectable>> groups = new LinkedHashMap<>();
for (Collectable item : items) groups.computeIfAbsent(item.getValue(key), k -> new ArrayList<>()).add(item);
return groups;
}
}
// ===== OCS mentor database (given) =====
/** Rows as the database returns them. Row 7 is bad data on purpose. */
class MentorDatabase {
// id, uid, name, email, expertise, capacity (max capstones), assigned (current capstones)
static final Object[][] MENTOR_ROWS = {
{1, "arivera", "Ada Rivera", "ada.rivera@techcorp.example", "Backend", 2, 1},
{2, "bchen", "Ben Chen", "ben@designlab.example", "Frontend", 3, 3},
{3, "cokafor", "Chioma Okafor", "chioma@datahub.example", "Data & AI", 2, 0},
{4, "dpatel", "Dev Patel", "dev@techcorp.example", "Backend", 1, 1},
{5, "elopez", "Elena Lopez", "elena@cloudops.example", "DevOps", 2, 1},
{6, "fkim", "frank kim", "frank@designlab.example", "Frontend", 2, 0},
{7, "gnguyen", "Grace Nguyen", "grace@chainco.example", "Blockchain", 1, 0},
};
// project, expertise it needs, mentors assigned
static final Object[][] CAPSTONE_ROWS = {
{"Food Bank Tracker", "Backend", 0},
{"Robotics Scheduler", "DevOps", 1},
{"Library Check-In", "Frontend", 1},
{"Volunteer Hours", "Data & AI", 0},
{"Tutor Match", "Backend", 0},
{"Club Fair Map", "Frontend", 0},
{"Senior Center Portal", "Backend", 0},
};
}
/** A capstone project and the mentor expertise it needs. */
class Capstone extends AbstractCollectable {
private final String project;
private final String needs;
private final int mentors;
Capstone(String project, String needs, int mentors) {
this.project = project;
this.needs = needs;
this.mentors = mentors;
}
public Map<String, Object> getMap() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Project", project);
map.put("Needs", needs);
map.put("Mentors", mentors);
return map;
}
public int compareTo(Collectable o) { return project.compareTo((String) o.getValue("Project")); }
static Catalog<Capstone> catalog() {
Catalog<Capstone> catalog = new Catalog<>();
for (Object[] row : MentorDatabase.CAPSTONE_ROWS) {
catalog.add(new Capstone((String) row[0], (String) row[1], (Integer) row[2]));
}
return catalog;
}
}
// ===== From Hack #1: paste your finished Expertise over this class =====
/** A mentoring area. Natural order is rank, not alphabetical. */
class Expertise extends AbstractCollectable {
private final int rank;
private final String area;
private final String focus;
Expertise(int rank, String area, String focus) {
this.rank = rank;
this.area = area;
this.focus = focus;
}
public String getArea() { return area; }
public Map<String, Object> getMap() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Area", area);
// TODO: put "Rank" first, then "Area", then "Focus"
return map;
}
public int compareTo(Collectable o) {
// TODO: compare by rank (hint: Integer.compare and o.getValue("Rank"))
return 0;
}
static Catalog<Expertise> catalog() {
Catalog<Expertise> catalog = new Catalog<>();
catalog.add(new Expertise(2, "Data & AI", "analytics, ML, datasets"));
// TODO: add Frontend (0), Backend (1), DevOps (3) -- out of order on purpose
return catalog;
}
/** Throws for an unknown area, so bad database rows get caught. (given) */
static Expertise find(Catalog<Expertise> catalog, String area) {
for (Expertise e : catalog.getItems()) if (e.getArea().equals(area)) return e;
throw new IllegalArgumentException("unknown expertise " + area);
}
}
// ===== From Hack #2: paste your finished Mentor over this class =====
/** A mentor record. HAS-A Expertise. */
class Mentor extends AbstractCollectable {
private final String uid, name, email;
private final Expertise expertise;
private final int capacity, assigned;
Mentor(String uid, String name, String email, Expertise expertise, int capacity, int assigned) {
this.uid = uid;
this.name = name;
this.email = email;
this.expertise = expertise;
this.capacity = capacity;
this.assigned = assigned;
}
public Expertise getExpertise() { return expertise; }
public String getOrg() {
// TODO: return the part of the email after "@"
return "";
}
public int getOpenSlots() {
// TODO: capacity minus assigned
return 0;
}
public Map<String, Object> getMap() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Name", name);
map.put("UID", uid);
// TODO: put "Org", "Expertise" (the area name), "Assigned", and "Open Slots"
return map;
}
public int compareTo(Collectable o) {
// TODO: compare names ignoring case, so "frank kim" sorts with the F's
return 0;
}
}
// ===== Hack #3: Mentor Dashboard =====
public class Main {
/** Turns database rows into Mentors. Skips rows with bad data instead of crashing. */
static Catalog<Mentor> loadMentors(Object[][] rows, Catalog<Expertise> areas) {
Catalog<Mentor> mentors = new Catalog<>();
for (Object[] row : rows) {
try {
Expertise expertise = Expertise.find(areas, (String) row[4]);
// TODO: build a Mentor from row[1], row[2], row[3], expertise, row[5], row[6] and add it
} catch (IllegalArgumentException e) {
System.out.println("Skipped row " + row[0] + ": " + e.getMessage());
}
}
return mentors;
}
/** Area -> how many more mentors OCS needs (unmentored capstones minus open slots). */
static Map<String, Integer> mentorGaps(Catalog<Capstone> capstones, Catalog<Mentor> mentors) {
Map<String, Integer> gaps = new LinkedHashMap<>();
// TODO 1: add every capstone with 0 Mentors to a new Catalog, then groupBy("Needs") with a Registry
// TODO 2: total the open slots per Expertise area (hint: Map.merge)
// TODO 3: for each area, gap = unmentored projects - open slots; put it in gaps when it is > 0
return gaps;
}
public static void main(String[] args) {
Catalog<Mentor> mentors = loadMentors(MentorDatabase.MENTOR_ROWS, Expertise.catalog());
Registry registry = new Registry(mentors);
System.out.println("\n=== 1. Mentors, natural sort (by Name) ===");
registry.sortNatural().forEach(System.out::println);
System.out.println("\n=== 2. Mentors, orderBy Open Slots ===");
// TODO: registry.orderBy("Open Slots")
System.out.println("\n=== 3. Mentors, groupBy Expertise ===");
// TODO: registry.groupBy("Expertise"), print each area and its mentor names
System.out.println("\n=== 4. Where we need mentors ===");
Map<String, Integer> gaps = mentorGaps(Capstone.catalog(), mentors);
gaps.forEach((area, count) -> System.out.println("Recruit " + count + " " + area + " mentor(s)"));
System.out.println("\n=== Self-Check: Hack #3 ===");
check("6 mentors loaded, bad Blockchain row skipped (needs Hack #1 and #2 finished)", mentors.getItems().size() == 6);
check("groupBy(\"Expertise\") gives 4 groups", registry.groupBy("Expertise").size() == 4);
check("Backend needs 2 more mentors", Integer.valueOf(2).equals(gaps.get("Backend")));
System.out.println(passed + "/" + total + " Hack #3 checks passed");
}
static int passed = 0, total = 0;
static void check(String name, boolean ok) {
total++;
if (ok) passed++;
System.out.println((ok ? "PASS " : "FAIL ") + name);
}
}
Main.main(null);
Quick Validation Checklist
- Each Self-Check passes: 3/3, 4/4, 3/3.
- Every hack has a markdown cell with its output lines and your explanation.
- The Blockchain row is skipped, not crashed on.
- You submitted the GitHub
.ipynblink, not a Pages link.
Above and beyond: add an Availability class (weekday/evening) and group mentors by it.
Submit Assignment
Need to update a submission later? Open the submissions dashboard.