github-actions[bot] commited on
Commit
01b8103
·
1 Parent(s): 04cce69

Deploy d0d1a1b

Browse files

AlphaFold failures now name their cause; drop the superseded mockups

Source: https://github.com/WINTER4000/turingDNA/commit/d0d1a1bee4bd36d3a803c847ba9065349794b20e

dee/static/app.js CHANGED
@@ -4010,13 +4010,33 @@ function _afResize(viewer) {
4010
  // Retry" affordance (the model DOES exist, EBI just blipped); a genuine
4011
  // absence says so plainly. Never again claim "model doesn't exist" when the
4012
  // truth is "EBI was momentarily down."
4013
- function _afShowError(host, transient, retry) {
4014
  if (!host) return;
 
 
 
 
 
 
 
 
 
 
 
 
4015
  const wrap = document.createElement('div');
4016
  wrap.className = 'alphafold-loading';
4017
- wrap.textContent = transient
4018
- ? 'AlphaFold-DB is temporarily unreachable — this usually clears in a moment.'
 
4019
  : 'No AlphaFold model is available for this protein entry.';
 
 
 
 
 
 
 
4020
  if (transient && typeof retry === 'function') {
4021
  const btn = document.createElement('button');
4022
  btn.type = 'button';
@@ -4046,7 +4066,9 @@ async function mountAlphaFoldViewer(hit, host, extraOpts) {
4046
 
4047
  const molstar = await _afWaitMolstar();
4048
  if (!molstar) {
4049
- _afShowError(host, true, () => mountAlphaFoldViewer(hit, host, extraOpts)); // CDN blip retry, don't tell them to refresh
 
 
4050
  return;
4051
  }
4052
  try {
@@ -4063,14 +4085,21 @@ async function mountAlphaFoldViewer(hit, host, extraOpts) {
4063
  if (!await _afTryLoad(viewer, candidates)) {
4064
  // exists === false → genuinely no model; otherwise treat as a
4065
  // transient EBI blip and offer a retry.
4066
- _afShowError(host, exists !== false, () => mountAlphaFoldViewer(hit, host, extraOpts));
 
 
 
 
4067
  return;
4068
  }
4069
  const overlay = host.querySelector('.alphafold-loading');
4070
  if (overlay) overlay.remove();
4071
  return viewer; // hand the instance back so the caller can spin/pin/resize it
4072
  } catch (err) {
4073
- _afShowError(host, true, () => mountAlphaFoldViewer(hit, host, extraOpts));
 
 
 
4074
  }
4075
  }
4076
 
@@ -6583,13 +6612,17 @@ if (_quitBtn) {
6583
  _afApplyBg(viewer);
6584
  const { candidates, exists } = await _afResolve(pdbUrl);
6585
  if (!await _afTryLoad(viewer, candidates)) {
6586
- _afShowError(host, exists !== false, () => _mountCrisprStructure(host, pdbUrl, residue));
 
 
 
6587
  return;
6588
  }
6589
  const ov = host.querySelector('.alphafold-loading'); if (ov) ov.remove();
6590
  _highlightResidue(molstar, viewer, residue);
6591
  } catch (err) {
6592
- _afShowError(host, true, () => _mountCrisprStructure(host, pdbUrl, residue));
 
6593
  }
6594
  }
6595
 
 
4010
  // Retry" affordance (the model DOES exist, EBI just blipped); a genuine
4011
  // absence says so plainly. Never again claim "model doesn't exist" when the
4012
  // truth is "EBI was momentarily down."
4013
+ function _afShowError(host, transient, retry, reason) {
4014
  if (!host) return;
4015
+ // The headline used to be the WHOLE message, which meant every possible
4016
+ // cause — a CORS block, a 404 on a stale model version, an aborted
4017
+ // timeout, a Mol* parse failure — was reported to the user as "EBI is
4018
+ // down". An audit found AlphaFold-DB up and serving 200s while this text
4019
+ // was on screen, so the headline was simply wrong and there was no way to
4020
+ // tell from the UI.
4021
+ //
4022
+ // Keep the friendly headline, but always carry the actual reason: one
4023
+ // line under it for the user, the full error in the console for whoever
4024
+ // is debugging. A retry button on a cause that retrying cannot fix is a
4025
+ // loop, and the reason is what tells them which they are looking at.
4026
+ if (reason) console.warn('[alphafold] ' + (transient ? 'transient' : 'absent') + ':', reason);
4027
  const wrap = document.createElement('div');
4028
  wrap.className = 'alphafold-loading';
4029
+ const head = document.createElement('div');
4030
+ head.textContent = transient
4031
+ ? 'AlphaFold-DB did not return the model — this usually clears in a moment.'
4032
  : 'No AlphaFold model is available for this protein entry.';
4033
+ wrap.appendChild(head);
4034
+ if (reason) {
4035
+ const why = document.createElement('div');
4036
+ why.textContent = String(reason).slice(0, 140);
4037
+ why.style.cssText = 'margin-top:6px;font-size:12px;opacity:.7';
4038
+ wrap.appendChild(why);
4039
+ }
4040
  if (transient && typeof retry === 'function') {
4041
  const btn = document.createElement('button');
4042
  btn.type = 'button';
 
4066
 
4067
  const molstar = await _afWaitMolstar();
4068
  if (!molstar) {
4069
+ // The Mol* bundle itself never arrived nothing to do with EBI.
4070
+ _afShowError(host, true, () => mountAlphaFoldViewer(hit, host, extraOpts),
4071
+ 'the 3-D viewer library did not load (CDN or network)');
4072
  return;
4073
  }
4074
  try {
 
4085
  if (!await _afTryLoad(viewer, candidates)) {
4086
  // exists === false → genuinely no model; otherwise treat as a
4087
  // transient EBI blip and offer a retry.
4088
+ _afShowError(host, exists !== false, () => mountAlphaFoldViewer(hit, host, extraOpts),
4089
+ exists === false
4090
+ ? 'the AlphaFold API has no entry for this accession'
4091
+ : 'every candidate model URL failed to load — tried '
4092
+ + (candidates || []).length + ' (newest version first)');
4093
  return;
4094
  }
4095
  const overlay = host.querySelector('.alphafold-loading');
4096
  if (overlay) overlay.remove();
4097
  return viewer; // hand the instance back so the caller can spin/pin/resize it
4098
  } catch (err) {
4099
+ // Usually a Mol* parse/render failure, not a network one. Saying "EBI
4100
+ // is unreachable" here sends people to check a service that is fine.
4101
+ _afShowError(host, true, () => mountAlphaFoldViewer(hit, host, extraOpts),
4102
+ (err && err.message) || String(err));
4103
  }
4104
  }
4105
 
 
6612
  _afApplyBg(viewer);
6613
  const { candidates, exists } = await _afResolve(pdbUrl);
6614
  if (!await _afTryLoad(viewer, candidates)) {
6615
+ _afShowError(host, exists !== false, () => _mountCrisprStructure(host, pdbUrl, residue),
6616
+ exists === false
6617
+ ? 'the AlphaFold API has no entry for this accession'
6618
+ : 'every candidate model URL failed to load');
6619
  return;
6620
  }
6621
  const ov = host.querySelector('.alphafold-loading'); if (ov) ov.remove();
6622
  _highlightResidue(molstar, viewer, residue);
6623
  } catch (err) {
6624
+ _afShowError(host, true, () => _mountCrisprStructure(host, pdbUrl, residue),
6625
+ (err && err.message) || String(err));
6626
  }
6627
  }
6628
 
dee/static/index.html CHANGED
@@ -112,7 +112,7 @@
112
  <!-- ?v= query bumps invalidate browser + iframe asset caches when app.css /
113
  app.js change. Bump these numbers whenever you ship a frontend update —
114
  without them, users keep getting the stale file for up to a week. -->
115
- <link rel="stylesheet" href="/static/app.css?v=20260819-lineage" />
116
  <!-- The work catalog + the draggable rail. Kept out of app.css so two new
117
  self-contained surfaces stay reviewable; every colour is an app.css
118
  token, so both themes work with nothing added. -->
@@ -2907,9 +2907,9 @@
2907
  <!-- Cloning reference data must load before app.js so the Designer
2908
  can read VECTORS / ENZYMES / CLONING_METHODS / TAGS / LINKERS. -->
2909
  <script src="/static/cloning_db.js?v=20260530-ui-polish" defer></script>
2910
- <script src="/static/context.js?v=20260819-lineage" defer></script>
2911
- <script src="/static/lineage.js?v=20260819-lineage" defer></script>
2912
- <script src="/static/app.js?v=20260819-lineage" defer></script>
2913
  <!-- The decision trace, BEFORE cockpit.js: applyEvent calls TDTrace.push
2914
  on the very first event, and both are `defer`, so document order is
2915
  load order. Loading it after would drop the opening events of a
 
112
  <!-- ?v= query bumps invalidate browser + iframe asset caches when app.css /
113
  app.js change. Bump these numbers whenever you ship a frontend update —
114
  without them, users keep getting the stale file for up to a week. -->
115
+ <link rel="stylesheet" href="/static/app.css?v=20260819-lineage2" />
116
  <!-- The work catalog + the draggable rail. Kept out of app.css so two new
117
  self-contained surfaces stay reviewable; every colour is an app.css
118
  token, so both themes work with nothing added. -->
 
2907
  <!-- Cloning reference data must load before app.js so the Designer
2908
  can read VECTORS / ENZYMES / CLONING_METHODS / TAGS / LINKERS. -->
2909
  <script src="/static/cloning_db.js?v=20260530-ui-polish" defer></script>
2910
+ <script src="/static/context.js?v=20260819-lineage2" defer></script>
2911
+ <script src="/static/lineage.js?v=20260819-lineage2" defer></script>
2912
+ <script src="/static/app.js?v=20260819-lineage2" defer></script>
2913
  <!-- The decision trace, BEFORE cockpit.js: applyEvent calls TDTrace.push
2914
  on the very first event, and both are `defer`, so document order is
2915
  load order. Loading it after would drop the opening events of a
tests/test_fold_structure_tool.py CHANGED
@@ -79,3 +79,44 @@ def test_fold_structure_surfaces_lookup_failure_honestly(monkeypatch):
79
  out = t._tool_fold_structure({"gene_symbol": "ZZZ", "organism": "human"})
80
  assert out["ok"] is False
81
  assert "ZZZ" in out["error"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  out = t._tool_fold_structure({"gene_symbol": "ZZZ", "organism": "human"})
80
  assert out["ok"] is False
81
  assert "ZZZ" in out["error"]
82
+
83
+
84
+ # --------------------------------------------------------------------------- #
85
+ # The failure UI must name the actual cause
86
+ # --------------------------------------------------------------------------- #
87
+ # An audit found AlphaFold-DB up and serving 200s (v6, ~0.3s) while the viewer
88
+ # was telling a user "AlphaFold-DB is temporarily unreachable". The headline
89
+ # was the WHOLE message, so every possible cause — a Mol* bundle that never
90
+ # loaded, a stale model version 404, an aborted timeout, a parse failure —
91
+ # was reported as an EBI outage, and nothing on screen could distinguish them.
92
+ def _app_js():
93
+ with open("dee/static/app.js", encoding="utf-8") as fh:
94
+ return fh.read()
95
+
96
+
97
+ def test_the_error_ui_accepts_and_shows_a_reason():
98
+ src = _app_js()
99
+ assert "function _afShowError(host, transient, retry, reason)" in src
100
+ # Shown to the user, and logged in full for whoever is debugging.
101
+ assert "console.warn('[alphafold] '" in src
102
+
103
+
104
+ def test_the_headline_no_longer_asserts_that_ebi_is_down():
105
+ """'did not return the model' describes what we observed. 'is temporarily
106
+ unreachable' is a claim about a third party's uptime that we were making
107
+ without evidence — and that the audit showed was often false."""
108
+ src = _app_js()
109
+ assert "AlphaFold-DB is temporarily unreachable" not in src
110
+ assert "AlphaFold-DB did not return the model" in src
111
+
112
+
113
+ def test_every_failure_path_passes_a_reason():
114
+ """A reason parameter nothing populates is decoration. Each call site must
115
+ say which failure it is reporting."""
116
+ src = _app_js()
117
+ for reason in (
118
+ "the 3-D viewer library did not load (CDN or network)",
119
+ "the AlphaFold API has no entry for this accession",
120
+ "every candidate model URL failed to load",
121
+ ):
122
+ assert reason in src, f"missing reason: {reason}"