Repository navigation
Network-backend API refactor: Phases 0–4 #127
Description
Network-backend API refactor: Phases 0–4
This issue tracks the refactor of MSstatsBioNet toward a generic network-backend API. The goals are:
- Decouple the package from INDRA, so other databases (OmniPath next) can be added as backends.
- Support entity types other than proteins (PTM sites, metabolites, ...).
- Tell apart nodes that are in the user's input from nodes the database adds.
Phases 0–4 are merged to devel (#112–#126). Phases 5–9 are listed at the end.
Each phase PR passed devtools::check() with 0 errors, 0 warnings and 1 NOTE (the existing "MSstats in Depends is not imported" NOTE). Each was also tested against MSstatsShiny's test_network_visualization.R (52 pass).
Summary
| Phase | PR | What it did |
|---|---|---|
| 0 | #112 | Deprecated unused arguments: the paper-count filter and the correlation filter |
| 1a | #113 | Edge and node contract, plus validate_network() |
| 1b | #115 | Edge columns renamed and added to match the contract |
| 1c | #116 | Node columns renamed to match the contract, contract enforced, golden output pinned |
| — | #114 | roxygen 8.1.0 update (chore) |
| 2 | #117 | INDRA moved behind an internal IndraBackend object and get_network() |
3 (node logFC) |
#119 | Node fold-change column kept as logFC (not log2FC) |
| 3a | #118 | Entity table: prepare_entities(), select_entities() |
| 3b | #120 | convert_ids() and get_entity_properties() for INDRA |
| 3c | #121 | Node status: measured, included_in_query, node_role, latent nodes |
| 3d | #122 | New API exported, plus backend_capabilities(), min_confidence, ?network_queries |
| 4a | #123 | Cytoscape display: latent-node styling, shapes by entity type, directed edges |
| 4b | #124 | get_evidence(), with the backend chosen from backend_database |
| 4c | #125 | get_curations() and filter_by_curation() |
| 4d | #126 | One node per input row when rows share a grounding |
Paired MSstatsShiny change: Vitek-Lab/MSstatsShiny#234 (displayLabelType = "entity_name"). It is still open and can merge now, because BioNet 1c is merged.
Phase 0: Deprecate unused arguments (#112)
-
Merged 2026-10-02
-
paper_count_cutoffis ignored and warns; the paper filter was removed.correlation_cutoffandprotein_level_datawarn as deprecated. -
paper_count_cutoff >= 2no longer errors. -
The deprecated arguments follow the Bioconductor policy: defunct in release N+1, removed in N+2.
-
Internal analysis scripts that used the deprecated arguments were updated. No callers of them remain.
Phase 1: Edge and node contract, snake_case column names
Phase 1 was written as one large commit, then split into three PRs for review.
1a. Contract module (#113)
-
Merged 2026-10-05
-
New
R/contract.R: the vocabularies (INTERACTION_TYPES,ENTITY_TYPES,NODE_ROLES) and the exportedvalidate_network(). It checks required columns and types, vocabularies and value ranges,NAstatistics on latent nodes, and that every edge endpoint is a node. It reports every problem in one error. -
Changes made in review:
- Symmetric statement types (
Complex,Association) must havedirected = FALSE. evidence_countrejectsNAandInf.- Malformed columns are reported instead of erroring.
- Renames:
query_typevalue"induced"→"subnetwork";source_db→backend_database;provenance_url→evidence_url;selected→included_in_query. - Final
NODE_ROLES:passed_cutoffs,user_added,mediator,upstream_regulator,downstream_target,path_start,path_end,path_intermediate.
- Symmetric statement types (
1b. Edge columns (#115)
-
Merged 2026-10-05
-
New edge columns:
directed,confidence(from INDRA belief),backend_database,query_type. -
Renamed edge columns:
stmt_hash→statement_idevidenceLink→evidence_url, now a link to this statement instead of every statement between the two agentsevidenceCount→evidence_count(integer)sourceCounts→evidence_sources
-
Code that reads these columns was updated:
filterSubnetworkByContext(), the topic functions, the Cytoscape edge elements, the JS click handler and the_edge_clickedShiny input, the curation filter, and the Filter-By-Context vignette. -
MSstatsShiny:
highlightEdgeInTable()reads onlysource,target,interactionandedge_type, so the rename is safe there. Its edge table now shows the new column names.
1c. Node columns and enforcement (#116)
-
Merged 2026-10-05
-
Renamed node columns:
Site→site,entityName→entity_name,entityId→entity_id. Addednamespace. Node IDs are character. -
cytoscapeNetwork(displayLabelType = "entityName")is deprecated in favor of"entity_name". Values other than"id"and"entity_name"now error; they used to fall back to"id"silently. -
getSubnetworkFromIndra()callsvalidate_network()on its output. -
Golden output pinned in
tests/testthat/_fixtures/golden_subnetwork.rds(8 nodes, 20 edges)._fixtures/make_golden_subnetwork.Rregenerates it. -
Removed
inst/extdata/subnetwork.rds, which nothing referenced and which had the old columns. -
Follow-up: chore: Switch entityName to entity_name for displayLabelType cytoscapeNetwork MSstatsShiny#234 changes the "Gene Name" radio button to
"entity_name"(still open).
Fixtures for future INDRA query types stay on the refactor-api branch and will merge with the Phase 7 parsers.
Phase 2: INDRA behind a backend object (#117)
-
Merged 2026-10-05
-
New S4 classes:
NetworkBackend(virtual),IndraBackend,NetworkQuery(virtual),SubnetworkQuery. -
New generic
get_network(backend, entities, query = subnetwork_query(), ...)with shared arguments:interaction_types(renamed fromstatement_typesin review)min_evidenceevidence_sources(renamed fromsourcesin review)include_entities
-
CoGEx HTTP code moved to
backend-indra-cogex.R.getSubnetworkFromIndra()now callsget_network(), and the golden output is unchanged. -
Decisions:
- Curation filtering is not part of
get_network(). It uses a different service and runs after the network is built. It becameget_curations()andfilter_by_curation()in Phase 4. - INDRA calls stay on
httr.
- Curation filtering is not part of
-
User-visible changes:
- A non-character
sources_filtererrors with "evidence_sources must be a character vector". - When
protein_level_dataandsources_filterare both invalid, thesources_filtererror is shown first.
- A non-character
Phase 3: Entity model, and measured vs. latent nodes
Node fold-change column stays logFC (#119)
-
Merged 2026-10-06
-
MSstats writes
log2FCorlog10FCdepending onlogTrans.prepare_entities()therefore copies whichever column the input has tologFC, with the values unchanged. Compared with RELEASE_3_23, the node column keeps its name.
3a. Entity table (#118)
-
Merged 2026-10-06
-
prepare_entities()builds one row per analyte with these columns:id,entity_type,id_type,namespace,entity_id,entity_name,included_in_query,site,parent_id,organism,logFC,adj.pvalue.- It errors if the input has several comparisons in
Labelandlabel =doesn't pick one. This replaces silently keeping the first.
- It errors if the input has several comparisons in
-
select_entities()flags rows that pass the cutoffs and drops none, so nodes in the input are recognized even when they fail the cutoffs.force_includetakesidvalues or"NS:ID"groundings.- An equivalence test shows it selects the same rows as the old input filter over a 48-combination argument grid.
-
parse_ptm_sites()uses an anchored pattern on each;-separated member. Results that differ from the old parsing:CLH1_HUMAN_S148→ siteS148(wasHUMAN_S148)P1_S148;P2_T5→ parentP1;P2, siteS148;T5
-
build_grounding_table()returns one row per grounding (long form). Only this function splits the;-joined grounding strings.
3b. convert_ids() and get_entity_properties() (#120)
-
Merged 2026-10-06
-
convert_ids()grounds the entity table:- UniProt IDs and mnemonics through CoGEx
- HGNC gene symbols through Gilda, using the table's organism
- chemical names through Gilda
- Unsupported entity type / ID type pairs, and proteins from a non-human organism, error with the supported options.
-
get_entity_properties()addsis_transcription_factor,is_kinaseandis_phosphatase(also for PTM rows, from the parent gene). -
IndraBackendgained agrounding_urlslot. -
annotateProteinInfoFromIndra()now usesconvert_ids()andget_entity_properties(). Its output isidentical()to 1.5.3 in live calls for all fourproteinIdTypevalues. -
Renamed in review:
get_annotations()→get_entity_properties(), because MSstats uses "annotation" for the experimental design file.
3c. Node status (#121)
-
Merged 2026-10-06
-
get_network()takes the entity table and sends only theincluded_in_queryrows. Each returned node is matched back to all rows by namespace and ID. -
New node columns:
entity_type,measured,included_in_query,node_role,has_measured_sites. The required node columns invalidate_network()now follow the contract. -
Nodes not in the input (latent nodes) get
measured = FALSE,NAstatistics, and anentity_typefrom their namespace (e.g. FPLX →family). This removes the made-uplogFC = 0,adj.pvalue = 1. -
The edge dedup key now includes the namespace, which fixes the collision between IDs like HGNC:2 and CHEBI:2.
-
PTM rule: site rows are
measured = TRUEwith their own statistics. Every row of a protein that has site rows getshas_measured_sites = TRUE. -
getSubnetworkFromIndra()builds its entities withprepare_entities()andselect_entities(). The golden output was re-pinned; this was an intended change.
3d. Export the new API (#122)
-
Merged 2026-10-06
-
Exported:
prepare_entities(),select_entities(),indra_backend(),convert_ids(),get_entity_properties(),subnetwork_query(),get_network(),backend_capabilities(), and the S4 classes, so other packages can add backends. -
backend_capabilities()returnsquery_types,id_conversions,entity_properties,interaction_typesandmax_nodes. It is not calledcapabilities(), which would maskbase::capabilities(). -
min_confidencedrops edges below the cutoff, plus edges with no score, and a message gives the count. -
get_network()prints the question it asks, e.g. "INDRA subnetwork: how are 42 selected proteins connected to each other, with no other nodes added?". -
New help pages:
?network_queries(with a glossary),?NetworkBackend-class,?NetworkQuery-class.
Phase 4: Downstream functions use the contract
4a. Display (#123)
-
Merged 2026-10-06
-
Node status styling:
- Latent nodes have no fill and a dashed grey border; the legend says "not in input data".
- Nodes in the input but not in the query are faded.
NAlogFC gives no fill. It used to get the zero-fold-change grey.
-
A PTM protein node takes its color from its protein-level row. If there is none, it gets a neutral container style ("no protein-level row in input").
-
Node shape by
entity_type: hexagon for metabolites and lipids, diamond for drugs, octagon for complexes, barrel for families. -
Edges use the
directedcolumn. Undirected edges have no arrow, and the reverse duplicate is dropped. -
.relProps()covers the whole interaction vocabulary, with a newmodificationcategory. -
The legend is built from what is drawn.
-
node_typevalues are unchanged, so MSstatsShiny's click handlers still work.
4b. get_evidence() (#124)
-
Merged 2026-10-06
-
Exported generic
get_evidence(backend, edges)with an INDRA method. Output isidentical()to the old internal function on live data. -
filterSubnetworkByContext(),decomposeSubnetworkByTopic(),decomposeSubnetworkIntoHierarchicalTopics(),compareTopicModels()andbootstrapTopicModels()takebackend = NULL. By default each edge goes to the backend named in itsbackend_database; an explicitbackendtakes every edge. -
A missing or
NAbackend_databaseerrors. There is no silent fallback to INDRA.
4c. Curation (#125)
-
Merged 2026-10-07
-
get_curations(backend, edges)makes one request per uniquestatement_id, where it used to make one per edge row.IndraBackendgained acuration_urlslot. -
filter_by_curation(network, min_evidence = 1, backend = NULL):- subtracts incorrect curations from
evidence_count - drops edges below
min_evidence, and always drops edges left below 1 - drops nodes that lost all their edges
- subtracts incorrect curations from
-
The output of
get_curations()is validated; for example, a numericstatement_idthat would lose precision errors. -
Fixed: curation
source_hashvalues were parsed as doubles, so two hashes could count once. They are now parsed withbigint_as_char. -
getSubnetworkFromIndra(filter_by_curation = TRUE)now callsfilter_by_curation().
4d. One node per input row (#126)
-
Merged 2026-10-07
-
When several input rows share a grounding (isoforms such as
P04637andP04637-2, or a protein group and its member), each row is its own node with its own statistics. The edge is copied once per (source node, target node) pair, and every copy keeps the statement's columns. -
sourceandtargetstay node IDs, so no code that reads edges had to change. -
A statement from a grounding to itself gives each matching node a self-loop, with no edges between isoforms.
-
statement_idis not unique per edge row. The statement-level key isbackend_database+statement_id. -
Replaces the 3c fallback for these cases, which kept INDRA's name as the node ID with
NAstatistics. Compared with 1.5.3, this is a breaking change: 1.5.3 merged these into one node withlogFC = 0,adj.pvalue = 1.
Remaining phases
- Phase 5: second backend plus
merge_networks(), as two PRs:- 5a:
merge_networks()and a shared backend contract test helper - 5b:
omnipath_backend()through OmnipathR (in Suggests) - Each backend's
convert_ids()will keep the groundings of other backends, so one entity table serves both.
- 5a:
- Phase 6: switch over. Deprecate
getSubnetworkFromIndra()andannotateProteinInfoFromIndra(); update MSstatsShiny and the vignettes in the same devel cycle. - Phase 7: more query types: upstream regulators (graph and enrichment), downstream targets, mediated, path search.
- Phase 8: cleanup and vignettes (incl. batching the curation lookup).
- Phase 9 (proposed, not scheduled): multi-condition input.
- Release N+1 / N+2: the deprecated items become defunct, then are removed.
Activity