@sjcrh/proteinpaint-server 2.125.0 → 2.126.1

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.
@@ -6,7 +6,8 @@ function termdb_test_default() {
6
6
  return {
7
7
  isMds3: true,
8
8
  isSupportedChartOverride: {
9
- runChart: () => true
9
+ runChart: () => true,
10
+ eventCount: () => true
10
11
  },
11
12
  cohort: {
12
13
  massNav: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.125.0",
3
+ "version": "2.126.1",
4
4
  "type": "module",
5
5
  "description": "a genomics visualization tool for exploring a cohort's genotype and phenotype data",
6
6
  "main": "src/app.js",
@@ -62,10 +62,10 @@
62
62
  "dependencies": {
63
63
  "@sjcrh/augen": "2.121.0",
64
64
  "@sjcrh/proteinpaint-python": "2.118.0",
65
- "@sjcrh/proteinpaint-r": "2.125.0",
66
- "@sjcrh/proteinpaint-rust": "2.125.0",
67
- "@sjcrh/proteinpaint-shared": "2.124.0",
68
- "@sjcrh/proteinpaint-types": "2.125.0",
65
+ "@sjcrh/proteinpaint-r": "2.126.0",
66
+ "@sjcrh/proteinpaint-rust": "2.126.0",
67
+ "@sjcrh/proteinpaint-shared": "2.126.0",
68
+ "@sjcrh/proteinpaint-types": "2.126.1",
69
69
  "@types/express": "^5.0.0",
70
70
  "@types/express-session": "^1.18.1",
71
71
  "better-sqlite3": "^9.4.1",
@@ -0,0 +1,121 @@
1
+ import { gdcMafPayload } from "#types/checkers";
2
+ import ky from "ky";
3
+ import { joinUrl } from "#shared/joinUrl.js";
4
+ import serverconfig from "#src/serverconfig.js";
5
+ const maxFileNumber = 1e3;
6
+ const allowedWorkflowType = "Aliquot Ensemble Somatic Variant Merging and Masking";
7
+ const maxTotalSizeCompressed = serverconfig.features.gdcMafMaxFileSize || 4e8;
8
+ const api = {
9
+ endpoint: "gdc/GRIN2list",
10
+ methods: {
11
+ get: {
12
+ ...gdcMafPayload,
13
+ init
14
+ },
15
+ post: {
16
+ ...gdcMafPayload,
17
+ init
18
+ }
19
+ }
20
+ };
21
+ function init({ genomes }) {
22
+ return async (req, res) => {
23
+ try {
24
+ const g = genomes.hg38;
25
+ if (!g)
26
+ throw "hg38 missing";
27
+ const ds = g.datasets.GDC;
28
+ if (!ds)
29
+ throw "hg38 GDC missing";
30
+ const payload = await listMafFiles(req.query, ds);
31
+ res.send(payload);
32
+ } catch (e) {
33
+ res.send({ status: "error", error: e.message || e });
34
+ }
35
+ };
36
+ }
37
+ async function listMafFiles(q, ds) {
38
+ const dataFormatFilter = {
39
+ op: "and",
40
+ content: [{ op: "=", content: { field: "data_format", value: "MAF" } }]
41
+ };
42
+ const filters = {
43
+ op: "and",
44
+ content: [
45
+ dataFormatFilter,
46
+ { op: "=", content: { field: "experimental_strategy", value: q.experimentalStrategy } },
47
+ { op: "=", content: { field: "analysis.workflow_type", value: allowedWorkflowType } },
48
+ { op: "=", content: { field: "access", value: "open" } }
49
+ ]
50
+ };
51
+ const case_filters = { op: "and", content: [] };
52
+ if (q.filter0) {
53
+ case_filters.content.push(q.filter0);
54
+ }
55
+ const { host } = ds.getHostHeaders(q);
56
+ const body = {
57
+ filters,
58
+ size: maxFileNumber,
59
+ fields: [
60
+ "id",
61
+ "file_size",
62
+ "cases.project.project_id",
63
+ // for display only
64
+ "cases.case_id",
65
+ // case uuid for making case url link to portal
66
+ "cases.submitter_id",
67
+ // used when listing all cases & files
68
+ "cases.samples.tissue_type",
69
+ "cases.samples.tumor_descriptor"
70
+ ].join(",")
71
+ };
72
+ if (case_filters.content.length)
73
+ body.case_filters = case_filters;
74
+ const response = await ky.post(joinUrl(host.rest, "files"), { timeout: false, json: body });
75
+ if (!response.ok)
76
+ throw `HTTP Error: ${response.status} ${response.statusText}`;
77
+ const re = await response.json();
78
+ if (!Number.isInteger(re.data?.pagination?.total))
79
+ throw "re.data.pagination.total is not int";
80
+ if (!Array.isArray(re.data?.hits))
81
+ throw "re.data.hits[] not array";
82
+ const files = [];
83
+ for (const h of re.data.hits) {
84
+ const c = h.cases?.[0];
85
+ if (!c)
86
+ throw "h.cases[0] missing";
87
+ const file = {
88
+ id: h.id,
89
+ project_id: c.project.project_id,
90
+ file_size: h.file_size,
91
+ case_submitter_id: c.submitter_id,
92
+ case_uuid: c.case_id,
93
+ sample_types: []
94
+ };
95
+ if (c.samples) {
96
+ let normalTypeName;
97
+ for (const { tumor_descriptor, tissue_type } of c.samples) {
98
+ if (tissue_type == "Normal") {
99
+ normalTypeName = (tumor_descriptor == "Not Applicable" ? "" : tumor_descriptor + " ") + tissue_type;
100
+ continue;
101
+ }
102
+ file.sample_types.push(tumor_descriptor + " " + tissue_type);
103
+ }
104
+ if (normalTypeName)
105
+ file.sample_types.push(normalTypeName);
106
+ }
107
+ file.sample_types = [...new Set(file.sample_types)];
108
+ files.push(file);
109
+ }
110
+ files.sort((a, b) => b.file_size - a.file_size);
111
+ const result = {
112
+ files,
113
+ filesTotal: re.data.pagination.total,
114
+ maxTotalSizeCompressed
115
+ };
116
+ return result;
117
+ }
118
+ export {
119
+ api,
120
+ maxTotalSizeCompressed
121
+ };
@@ -0,0 +1,96 @@
1
+ import { runGRIN2Payload } from "#types/checkers";
2
+ import { run_rust } from "@sjcrh/proteinpaint-rust";
3
+ import { run_R } from "@sjcrh/proteinpaint-r";
4
+ import serverconfig from "#src/serverconfig.js";
5
+ import path from "path";
6
+ const api = {
7
+ endpoint: "gdc/runGRIN2",
8
+ methods: {
9
+ get: {
10
+ ...runGRIN2Payload,
11
+ init
12
+ },
13
+ post: {
14
+ ...runGRIN2Payload,
15
+ init
16
+ }
17
+ }
18
+ };
19
+ function init({ genomes }) {
20
+ return async (req, res) => {
21
+ try {
22
+ console.log("[GRIN2] Validating genome configuration");
23
+ const g = genomes.hg38;
24
+ if (!g)
25
+ throw "hg38 missing";
26
+ const ds = g.datasets.GDC;
27
+ if (!ds)
28
+ throw "hg38 GDC missing";
29
+ const caseFiles = req.query;
30
+ console.log(`[GRIN2] Request received: ${JSON.stringify(caseFiles)}`);
31
+ if (!caseFiles) {
32
+ throw "Missing or invalid cases data";
33
+ }
34
+ try {
35
+ console.log("[GRIN2] Calling Rust for file processing...");
36
+ const rustInput = JSON.stringify(caseFiles);
37
+ console.log("[GRIN2] Executing Rust function...");
38
+ const rustResult = await run_rust("gdcGRIN2", rustInput);
39
+ console.log("[GRIN2] Rust execution completed");
40
+ console.log(`[GRIN2] Rust result type: ${typeof rustResult}`);
41
+ if (!rustResult) {
42
+ throw new Error("Failed to process MAF files: No result from Rust");
43
+ }
44
+ let parsedRustResult;
45
+ try {
46
+ parsedRustResult = typeof rustResult === "string" ? JSON.parse(rustResult) : rustResult;
47
+ console.log(`[GRIN2] Parsed Rust result: ${JSON.stringify(parsedRustResult).substring(0, 200)}...`);
48
+ } catch (parseError) {
49
+ console.error("[GRIN2] Error parsing Rust result:", parseError);
50
+ }
51
+ const genedbfile = path.join(serverconfig.tpmasterdir, g.genedb.dbfile);
52
+ const imagefile = path.join(serverconfig.cachedir, `grin2_${Date.now()}_${Math.floor(Math.random() * 1e9)}.png`);
53
+ const rInput = JSON.stringify({
54
+ genedb: genedbfile,
55
+ chromosomelist: g.majorchr,
56
+ imagefile,
57
+ lesion: rustResult
58
+ // The mutation string from Rust
59
+ });
60
+ console.log(`R input: ${rInput}`);
61
+ const parsedInput = JSON.parse(rInput);
62
+ console.log("Parsed lesion data type:", typeof parsedInput.lesion);
63
+ console.log(
64
+ "Parsed lesion data length:",
65
+ typeof parsedInput.lesion === "string" ? parsedInput.lesion.length : "not a string"
66
+ );
67
+ console.log("[GRIN2] Executing R script...");
68
+ const rResult = await run_R("gdcGRIN2.R", rInput, []);
69
+ console.log(`[GRIN2] R execution completed, result: ${rResult}`);
70
+ let resultData;
71
+ try {
72
+ resultData = JSON.parse(rResult);
73
+ console.log("[GRIN2] Finished R analysis");
74
+ const pngImg = resultData.png[0];
75
+ return res.json({ pngImg });
76
+ } catch (parseError) {
77
+ console.error("[GRIN2] Error parsing R result:", parseError);
78
+ console.log("[GRIN2] Raw R result:", rResult);
79
+ }
80
+ } finally {
81
+ }
82
+ } catch (e) {
83
+ console.error("[GRIN2] Error running analysis:", e);
84
+ console.error("[GRIN2] Error stack:", e.stack);
85
+ const errorResponse = {
86
+ status: "error",
87
+ error: e.message || String(e)
88
+ };
89
+ console.log(`[GRIN2] Sending error response: ${JSON.stringify(errorResponse)}`);
90
+ res.status(500).send(errorResponse);
91
+ }
92
+ };
93
+ }
94
+ export {
95
+ api
96
+ };
@@ -36,7 +36,7 @@ function init({ genomes }) {
36
36
  const ds = g.datasets[q.dslabel];
37
37
  if (!ds)
38
38
  throw "invalid dataset name";
39
- if (ds.__gdc && !ds.__gdc.doneCaching)
39
+ if (ds.label === "GDC" && !ds.__gdc?.doneCaching)
40
40
  throw "The server has not finished caching the case IDs: try again in about 2 minutes.";
41
41
  if ([TermTypes.GENE_EXPRESSION, TermTypes.METABOLITE_INTENSITY, NUMERIC_DICTIONARY_TERM].includes(q.dataType)) {
42
42
  if (!ds.queries?.[q.dataType] && q.dataType !== NUMERIC_DICTIONARY_TERM)
@@ -179,7 +179,7 @@ function gdcValidateQuery(ds, genome) {
179
179
  );
180
180
  return serverconfig.features.gdcGenes;
181
181
  }
182
- if (!ds.__gdc?.doneCaching) {
182
+ if (ds.label === "GDC" && !ds.__gdc?.doneCaching) {
183
183
  throw "The server has not finished caching the case IDs: try again in about 2 minutes.";
184
184
  }
185
185
  const { host, headers } = ds.getHostHeaders(q);