StrangeIB commited on
Commit
67c1693
·
1 Parent(s): 84b9142

Support experience.usc.edu STARS format: detect name via 'STARS Report for', ID via 'ID#:', + per-report link token; old format still works

Browse files
Files changed (1) hide show
  1. redactors/pii.py +51 -23
redactors/pii.py CHANGED
@@ -1,14 +1,17 @@
1
  """PII redaction pass.
2
 
3
  Detects the student-identifying fields by their template anchors (constant
4
- labels / formats), never by hard-coded student data:
5
-
6
- - Student ID (10-digit number in the page header)
7
- - Roster name ("Last, First, Middle" line under the ID header)
8
- - Diploma name (line after "Name as it will appear on your USC Diploma:")
9
- - Mailing street (after "Diploma will be mailed to:")
10
- - Mailing city/state/zip (the line following the street)
 
 
11
  - Sport / team (between "Student Athlete:" and "Clock Date:")
 
12
  """
13
  import re
14
 
@@ -20,8 +23,9 @@ ROSTER_RE = re.compile(r"^[A-Z][A-Za-z.'\-]+(?:,\s+[A-Za-z.'\-]+){1,3}$")
20
 
21
  TAG_MAP = {
22
  "Student ID": "ID", "Roster name": "NAME", "Diploma name": "NAME",
23
- "Mailing street": "ADDRESS", "Mailing city/state/zip": "ADDRESS",
24
- "Sport/team": "SPORT",
 
25
  }
26
 
27
 
@@ -40,24 +44,48 @@ def _layout_lines(doc):
40
 
41
 
42
  def detect(doc):
43
- """Return {label: value} of detected identifiers. Missing fields omitted."""
 
 
 
 
 
 
 
 
44
  lines = _layout_lines(doc)
 
45
  found = {}
46
 
47
- id_line_idx = None
48
- for i, ln in enumerate(lines[:8]):
49
- m = ID_RE.search(ln)
50
- if m:
51
- found["Student ID"] = m.group(0)
52
- id_line_idx = i
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  break
54
 
55
- if id_line_idx is not None:
56
- for ln in lines[id_line_idx + 1: id_line_idx + 4]:
57
- cand = _first_chunk(ln)
58
- if cand and ROSTER_RE.match(cand):
59
- found["Roster name"] = cand
60
- break
61
 
62
  for i, ln in enumerate(lines):
63
  low = ln.lower()
@@ -134,7 +162,7 @@ def verify(doc, meta):
134
 
135
  soft = []
136
  tokens = set()
137
- for label in ("Roster name", "Diploma name"):
138
  if label in values:
139
  for tok in re.split(r"[,\s]+", values[label]):
140
  if len(tok) >= 4 and tok.isalpha():
 
1
  """PII redaction pass.
2
 
3
  Detects the student-identifying fields by their template anchors (constant
4
+ labels / formats), never by hard-coded student data. Handles both STARS
5
+ layouts: the older "Microsoft Print To PDF" report and the newer
6
+ experience.usc.edu ("Skia/PDF") report. Fields include:
7
+
8
+ - Student ID ("ID#:" label, or a 10-digit number in the header)
9
+ - Header name (after "STARS Report for ", experience.usc.edu)
10
+ - Roster name ("Last, First, Middle" header line, Microsoft-print)
11
+ - Diploma name (after "Name as it will appear on your USC Diploma:")
12
+ - Mailing street/zip (the diploma "mailed to" block, Microsoft-print)
13
  - Sport / team (between "Student Athlete:" and "Clock Date:")
14
+ - Report link (per-report stars.usc.edu token in the print footer)
15
  """
16
  import re
17
 
 
23
 
24
  TAG_MAP = {
25
  "Student ID": "ID", "Roster name": "NAME", "Diploma name": "NAME",
26
+ "Header name": "NAME", "Mailing street": "ADDRESS",
27
+ "Mailing city/state/zip": "ADDRESS", "Sport/team": "SPORT",
28
+ "Report link": "LINK",
29
  }
30
 
31
 
 
44
 
45
 
46
  def detect(doc):
47
+ """Return {label: value} of detected identifiers. Missing fields omitted.
48
+
49
+ Supports two STARS layouts:
50
+ * "Microsoft Print To PDF" — header has the ID then a "Last, First,
51
+ Middle" line; a diploma section with mailing address; sport line.
52
+ * experience.usc.edu ("Skia/PDF") — name follows "STARS Report for ",
53
+ ID follows "ID#:", plus a per-report link token in the footer; no
54
+ diploma/mailing-address section.
55
+ """
56
  lines = _layout_lines(doc)
57
+ text = "\n".join(lines)
58
  found = {}
59
 
60
+ # --- Student ID -------------------------------------------------------
61
+ m = re.search(r"ID#:\s*(\d{9,10})", text) # experience.usc.edu
62
+ if m:
63
+ found["Student ID"] = m.group(1)
64
+ else:
65
+ for ln in lines[:8]: # Microsoft-print header
66
+ mm = ID_RE.search(ln)
67
+ if mm:
68
+ found["Student ID"] = mm.group(0)
69
+ break
70
+
71
+ # --- Name (experience.usc.edu): "STARS Report for <First Middle Last>" -
72
+ m = re.search(r"STARS Report for\s+(.+)", text)
73
+ if m:
74
+ nm = re.split(r"\s{2,}|\|", m.group(1).strip())[0].strip()
75
+ if nm:
76
+ found["Header name"] = nm
77
+
78
+ # --- Roster name (Microsoft-print): "Last, First[, Middle]" in header --
79
+ for ln in lines[:8]:
80
+ cand = _first_chunk(ln)
81
+ if cand and ROSTER_RE.match(cand):
82
+ found["Roster name"] = cand
83
  break
84
 
85
+ # --- Per-report link token (experience.usc.edu print footer) ----------
86
+ m = re.search(r"https?://stars\.usc\.edu/\S+", text)
87
+ if m:
88
+ found["Report link"] = m.group(0)
 
 
89
 
90
  for i, ln in enumerate(lines):
91
  low = ln.lower()
 
162
 
163
  soft = []
164
  tokens = set()
165
+ for label in ("Roster name", "Diploma name", "Header name"):
166
  if label in values:
167
  for tok in re.split(r"[,\s]+", values[label]):
168
  if len(tok) >= 4 and tok.isalpha():