mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 15:40:05 +00:00
Reuse the shared real node filter in the community listing loop
The per community Nodes count and listing loop only excluded file
nodes, so a rationale docstring fragment node or a concept node
leaked into Nodes (N) as if it were a real code declaration, and
inflated the reported count by including it. The Knowledge Gaps
section a few lines below already excluded file, concept and
rationale nodes together, the correct set an earlier issue
established for this same function, the community listing loop was
never given the same treatment.
Consolidates every real node check this function makes, the summary
thin and shown counts, the community listing, and the gaps section
isolated and thin community counts, onto one shared predicate instead
of four separate inline filters that had drifted apart. Fixing only
the display line without also fixing the count computations that
decide which communities are thin or shown would have reintroduced
the exact header versus render disagreement an earlier issue fixed,
since the header counts and the rendered content need to agree on
what counts as a real node.
Fixes issue 3794.
(cherry picked from commit 57fab42e81)
This commit is contained in:
1 parent
e27d51d7c0
commit
d6ff6e0984
1 file changed
+24
-15
+24
-15
@@ -153,13 +153,29 @@ def generate(
|
||||
f"the graph (top: {top})"
|
||||
)
|
||||
|
||||
from .analyze import _is_file_node as _ifn
|
||||
from .analyze import _is_file_node, _is_concept_node
|
||||
|
||||
def _real_node(n) -> bool:
|
||||
# The one definition of "real" (structural, not file/concept/
|
||||
# rationale) every figure this function prints about itself must
|
||||
# agree on (#3148) -- file nodes are structural scaffolding, concept
|
||||
# nodes are semantic-layer entities, and rationale nodes are
|
||||
# docstring fragments the LLM extracted for context, none of which
|
||||
# are a code declaration a reader would recognize as a "node" in
|
||||
# this list. A community-listing loop that filtered only file nodes
|
||||
# let rationale/concept content leak into "Nodes (N): ..." as if it
|
||||
# were a real declaration, and inflated N by counting it (#3794).
|
||||
return (
|
||||
not _is_file_node(G, n)
|
||||
and not _is_concept_node(G, n)
|
||||
and G.nodes[n].get("file_type") != "rationale"
|
||||
)
|
||||
|
||||
def _real_count(nodes) -> int:
|
||||
return sum(1 for n in nodes if not _ifn(G, n))
|
||||
return sum(1 for n in nodes if _real_node(n))
|
||||
|
||||
non_empty = {cid: nodes for cid, nodes in communities.items()
|
||||
if any(not _ifn(G, n) for n in nodes)}
|
||||
if any(_real_node(n) for n in nodes)}
|
||||
# One predicate for every figure the report prints about itself (#3148):
|
||||
# "thin" is real < min_community_size (ZERO real nodes included), and
|
||||
# "shown" is what the render loop below actually renders (real >=
|
||||
@@ -280,8 +296,9 @@ def generate(
|
||||
for cid, nodes in communities.items():
|
||||
label = community_labels.get(cid, f"Community {cid}")
|
||||
score = cohesion_scores.get(cid, 0.0)
|
||||
# Filter method/function stubs from display - they're structural noise
|
||||
real_nodes = [n for n in nodes if not _ifn(G, n)]
|
||||
# Filter file/concept/rationale nodes from display - they're
|
||||
# structural noise or docstring prose, not a code declaration (#3794).
|
||||
real_nodes = [n for n in nodes if _real_node(n)]
|
||||
if not real_nodes:
|
||||
continue
|
||||
if len(real_nodes) < min_community_size:
|
||||
@@ -307,15 +324,7 @@ def generate(
|
||||
]
|
||||
|
||||
# --- Gaps section ---
|
||||
from .analyze import _is_file_node, _is_concept_node
|
||||
|
||||
isolated = [
|
||||
n for n in G.nodes()
|
||||
if G.degree(n) <= 1
|
||||
and not _is_file_node(G, n)
|
||||
and not _is_concept_node(G, n)
|
||||
and G.nodes[n].get("file_type") != "rationale"
|
||||
]
|
||||
isolated = [n for n in G.nodes() if G.degree(n) <= 1 and _real_node(n)]
|
||||
# Same threshold the Summary and Communities headers used (#3148): this
|
||||
# was a hardcoded 3, so with --min-community-size anything else the count
|
||||
# here disagreed with the label text beside it, which already printed
|
||||
@@ -324,7 +333,7 @@ def generate(
|
||||
# as a genuinely thin one, so it belongs in this count too.
|
||||
thin_communities = {
|
||||
cid: nodes for cid, nodes in communities.items()
|
||||
if sum(1 for n in nodes if not _is_file_node(G, n)) < min_community_size
|
||||
if _real_count(nodes) < min_community_size
|
||||
}
|
||||
gap_count = len(isolated) + len(thin_communities)
|
||||
|
||||
|
||||
Reference in new issue
Block a user