@sjcrh/proteinpaint-server 2.172.0 → 2.174.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -69,7 +69,7 @@ async function getResult(q, ds) {
69
69
  ({ term2sample2value, byTermId, bySampleId } = await getNumericDictTermAnnotation(q, ds));
70
70
  } else {
71
71
  ;
72
- ({ term2sample2value, byTermId, bySampleId, skippedSexChrGenes } = await ds.queries[q.dataType].get(_q));
72
+ ({ term2sample2value, byTermId, bySampleId, skippedSexChrGenes } = await ds.queries[q.dataType].get(_q, ds));
73
73
  }
74
74
  const noValueTerms = [];
75
75
  for (const [term, obj] of term2sample2value) {
@@ -206,6 +206,7 @@ async function validate_query_geneExpression(ds, genome) {
206
206
  const q = ds.queries.geneExpression;
207
207
  if (!q) return;
208
208
  q.geneExpression2bins = {};
209
+ if (typeof q.get == "function") return;
209
210
  if (q.src == "gdcapi") {
210
211
  gdc_validate_query_geneExpression(ds, genome);
211
212
  return;
@@ -1,4 +1,5 @@
1
1
  import { runChartPayload } from "#types/checkers";
2
+ import { getNumberFromDate } from "#shared/terms.js";
2
3
  const api = {
3
4
  endpoint: "termdb/runChart",
4
5
  methods: {
@@ -13,23 +14,9 @@ const api = {
13
14
  }
14
15
  };
15
16
  async function getRunChart(q, ds) {
16
- const terms = [];
17
- let xTermId;
18
- let yTermId;
19
- if (q.term && q.term2) {
20
- const tws = [
21
- { term: q.term, q: { mode: "continuous" }, $id: q.term.id },
22
- { term: q.term2, q: { mode: "continuous" }, $id: q.term2.id }
23
- ];
24
- terms.push(...tws);
25
- xTermId = q.term.id;
26
- yTermId = q.term2.id;
27
- } else {
28
- throw new Error("term and term2 must be provided");
29
- }
30
- if (!xTermId || !yTermId) {
31
- throw new Error("Unable to determine term IDs for x and y axes");
32
- }
17
+ const terms = [q.term, q.term2];
18
+ const xTermId = q.term["$id"];
19
+ const yTermId = q.term2["$id"];
33
20
  const { getData } = await import("../src/termdb.matrix.js");
34
21
  const data = await getData(
35
22
  {
@@ -81,16 +68,20 @@ function buildRunChartFromData(aggregation, xTermId, yTermId, data) {
81
68
  month = Math.floor(frac * 12) + 1;
82
69
  }
83
70
  } else {
84
- month = 1;
71
+ year = null;
85
72
  }
86
73
  if (year == null || month == null || Number.isNaN(year) || Number.isNaN(month)) {
87
- throw new Error(
88
- `Invalid date value for sample ${sampleId}: xTermId=${xTermId}, xRaw=${xRaw}, parsed year=${year}, month=${month}`
74
+ skippedSamples++;
75
+ console.log(
76
+ `Skipping sample ${sampleId}: Invalid date value - xTermId=${xTermId}, xRaw=${xRaw}, parsed year=${year}, month=${month}`
89
77
  );
78
+ continue;
90
79
  }
91
- const bucketKey = `${year}-${String(month).padStart(2, "0")}`;
92
- const x = Number(`${year}.${String(month).padStart(2, "0")}`);
93
- const date = new Date(year, month - 1, 1);
80
+ const yearNum = year;
81
+ const monthNum = month;
82
+ const bucketKey = `${yearNum}-${String(monthNum).padStart(2, "0")}`;
83
+ const x = Number(`${yearNum}.${String(monthNum).padStart(2, "0")}`);
84
+ const date = new Date(yearNum, monthNum - 1, 1);
94
85
  const xName = date.toLocaleString("en-US", { month: "long", year: "numeric" });
95
86
  if (!buckets[bucketKey]) {
96
87
  buckets[bucketKey] = {
@@ -101,7 +92,7 @@ function buildRunChartFromData(aggregation, xTermId, yTermId, data) {
101
92
  success: 0,
102
93
  total: 0,
103
94
  countSum: 0,
104
- sortKey: year * 100 + month,
95
+ sortKey: yearNum * 100 + monthNum,
105
96
  yValues: []
106
97
  };
107
98
  }
@@ -162,18 +153,33 @@ function buildRunChartFromData(aggregation, xTermId, yTermId, data) {
162
153
  if (skippedSamples > 0) {
163
154
  console.log(`buildRunChartFromData: Skipped ${skippedSamples} sample(s) due to missing x or y values`);
164
155
  }
156
+ function xFromBucket(b) {
157
+ const yearNum = Math.floor(b.sortKey / 100);
158
+ const monthNum = b.sortKey % 100;
159
+ const x = getNumberFromDate(new Date(yearNum, monthNum - 1, 15));
160
+ return Math.round(x * 100) / 100;
161
+ }
165
162
  const points = Object.values(buckets).sort((a, b) => a.sortKey - b.sortKey).map((b) => {
163
+ const x = xFromBucket(b);
166
164
  if (aggregation === "proportion") {
167
165
  const total = b.total || 0;
168
166
  const succ = b.success || 0;
169
167
  const y = total ? Math.round(succ / total * 1e3) / 1e3 : 0;
170
- return { x: b.x, xName: b.xName, y, sampleCount: total };
168
+ return { x, xName: b.xName, y, sampleCount: total };
171
169
  } else if (aggregation === "count") {
172
170
  const y = Math.round((b.countSum || 0) * 100) / 100;
173
- return { x: b.x, xName: b.xName, y, sampleCount: b.count };
171
+ return { x, xName: b.xName, y, sampleCount: b.count };
174
172
  } else {
175
- const avg = b.count ? Math.round(b.ySum / b.count * 100) / 100 : 0;
176
- return { x: b.x, xName: b.xName, y: avg, sampleCount: b.count };
173
+ let y;
174
+ if (aggregation === "median" && (b.yValues?.length ?? 0) > 0) {
175
+ const sorted = [...b.yValues].sort((a, b2) => a - b2);
176
+ const mid = Math.floor(sorted.length / 2);
177
+ y = sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
178
+ } else {
179
+ y = b.count ? b.ySum / b.count : 0;
180
+ }
181
+ y = Math.round(y * 100) / 100;
182
+ return { x, xName: b.xName, y, sampleCount: b.count };
177
183
  }
178
184
  });
179
185
  const yValues = points.map((p) => p.y).filter((v) => typeof v === "number" && !Number.isNaN(v));