Skip to content

Commit d54570a

Browse files
committed
Amend unified analysis report
1 parent 8d9575c commit d54570a

File tree

5 files changed

+134
-379
lines changed

5 files changed

+134
-379
lines changed

Graph Analysis/unified_analysis.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,58 @@ def _truncate_label(text: str, max_len: int = 80) -> str:
3939
return safe if len(safe) <= max_len else (safe[: max_len - 1] + "…")
4040

4141

42+
# ---------------- Participant-only Degree (Co-attendance) ----------------
43+
44+
def extract_participants(record: Dict[str, Any]) -> List[str]:
45+
"""Extract likely participants from a meeting record.
46+
- peoplePresent: comma-separated string under meetingInfo
47+
- host, documenter: added if present (deduped)
48+
"""
49+
participants: List[str] = []
50+
meeting_info = {}
51+
if isinstance(record, dict):
52+
meeting_info = record.get("meetingInfo", {}) or {}
53+
# peoplePresent as comma-separated string
54+
pp = meeting_info.get("peoplePresent", "")
55+
if isinstance(pp, str) and pp.strip():
56+
participants.extend([p.strip() for p in pp.split(",") if p.strip()])
57+
# host/documenter as single names
58+
for key in ("host", "documenter"):
59+
val = meeting_info.get(key)
60+
if isinstance(val, str) and val.strip():
61+
participants.append(val.strip())
62+
# dedupe while preserving order
63+
seen = set()
64+
deduped: List[str] = []
65+
for p in participants:
66+
if p not in seen:
67+
seen.add(p)
68+
deduped.append(p)
69+
return deduped
70+
71+
72+
def build_coattendance_graph(records: Iterable[Any]) -> nx.Graph:
73+
G = nx.Graph()
74+
for rec in records:
75+
participants = extract_participants(rec)
76+
if len(participants) < 2:
77+
continue
78+
for p in participants:
79+
G.add_node(p)
80+
for u, v in combinations(participants, 2):
81+
if G.has_edge(u, v):
82+
G[u][v]["weight"] += 1
83+
else:
84+
G.add_edge(u, v, weight=1)
85+
return G
86+
87+
88+
def degree_analysis(G: nx.Graph) -> Tuple[Dict[str, int], Counter]:
89+
degree_dict = dict(G.degree())
90+
degree_counts = Counter(degree_dict.values())
91+
return degree_dict, degree_counts
92+
93+
4294
# ---------------- JSON Path Structure ----------------
4395

4496
def extract_json_paths(obj: Any, prefix: str = "") -> List[str]:
@@ -161,6 +213,9 @@ def connected_components_info(G: nx.Graph, top: int) -> Dict[str, Any]:
161213
def write_report(
162214
output_file: str,
163215
summary: Dict[str, Any],
216+
attend_deg: Tuple[Dict[str, int], Counter],
217+
attend_top: List[Tuple[str, int]],
218+
attend_dist: List[Tuple[int, int]],
164219
field_deg: Tuple[Dict[str, int], Counter],
165220
field_top: List[Tuple[str, int]],
166221
field_dist: List[Tuple[int, int]],
@@ -182,6 +237,20 @@ def write_report(
182237
f.write(f"- {k}: {v}\n")
183238
f.write("\n")
184239

240+
# Participant-only Degree (Co-attendance)
241+
f.write("## Degree (Co-attendance) Analysis\n")
242+
f.write("### Top Nodes by Degree\n")
243+
f.write("| Rank | Node | Degree |\n|------|------|--------|\n")
244+
for i, (node, deg) in enumerate(attend_top, 1):
245+
label = _truncate_label(node, 80)
246+
f.write(f"| {i} | {label} | {deg} |\n")
247+
f.write("\n")
248+
f.write("### Degree Distribution\n")
249+
f.write("| Degree | Count of Nodes |\n|--------|-----------------|\n")
250+
for d, c in attend_dist:
251+
f.write(f"| {d} | {c} |\n")
252+
f.write("\n")
253+
185254
# JSON Field Degree Analysis
186255
f.write("## JSON Field Degree Analysis\n")
187256
f.write("### Top Fields by Degree\n")
@@ -276,6 +345,13 @@ def main() -> None:
276345
args = parser.parse_args()
277346

278347
data = load_json(args.input)
348+
records = ensure_iterable_records(data)
349+
350+
# Participant-only co-attendance
351+
G_attend = build_coattendance_graph(records)
352+
attend_deg_dict, attend_deg_counts = degree_analysis(G_attend)
353+
attend_top = sorted(attend_deg_dict.items(), key=lambda x: x[1], reverse=True)[: args.limit_top]
354+
attend_dist = sorted(attend_deg_counts.items(), key=lambda x: x[0])
279355

280356
# Path analysis
281357
all_paths = extract_json_paths(data)
@@ -299,6 +375,8 @@ def main() -> None:
299375
components = connected_components_info(G_fields, args.limit_top)
300376

301377
summary = {
378+
"Co-attendance graph (nodes)": len(G_attend.nodes),
379+
"Co-attendance graph (edges)": len(G_attend.edges),
302380
"Path graph (nodes)": len(G_paths.nodes),
303381
"Path graph (edges)": len(G_paths.edges),
304382
"Field graph (nodes)": len(G_fields.nodes),
@@ -308,6 +386,9 @@ def main() -> None:
308386
write_report(
309387
output_file=args.output,
310388
summary=summary,
389+
attend_deg=(attend_deg_dict, attend_deg_counts),
390+
attend_top=attend_top,
391+
attend_dist=attend_dist,
311392
field_deg=(fdeg_dict, fdeg_counts),
312393
field_top=field_top,
313394
field_dist=field_dist,

reports/unified_analysis_report_2.md

Lines changed: 0 additions & 120 deletions
This file was deleted.

reports/unified_analysis_report_3.md

Lines changed: 0 additions & 120 deletions
This file was deleted.

0 commit comments

Comments
 (0)