From 36b2200e4c9337d47f41694b575dcf9f9cec5a34 Mon Sep 17 00:00:00 2001 From: Pieter Verschaffelt Date: Thu, 18 Apr 2024 11:25:22 +0200 Subject: [PATCH] Add settings for specifying crap sequences --- src/components/pages/SettingsPage.vue | 92 +++++++++++++++++++ src/logic/configuration/Configuration.ts | 2 + .../configuration/ConfigurationManager.ts | 17 +++- src/logic/filesystem/fasta/FastaParser.ts | 41 +++++++++ .../study/StudyFileSystemDataWriter.ts | 2 +- 5 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 src/logic/filesystem/fasta/FastaParser.ts diff --git a/src/components/pages/SettingsPage.vue b/src/components/pages/SettingsPage.vue index e1857fe5..fc31af2c 100644 --- a/src/components/pages/SettingsPage.vue +++ b/src/components/pages/SettingsPage.vue @@ -112,6 +112,55 @@ +

Global analysis settings

+ + + + + +
Exclude (cRAP) sequences?
+ + Use this option if you would like to exclude specific peptides that are + known to be associated with cRAP-proteins. You can specify exactly which + protein sequences should be considered using the text area below. + +
+ + + +
+ + + + + + +
+
+ Or, import sequences from a FASTA file... +
+ + Import from file + +
+
+
+
+
+

Storage

@@ -264,6 +313,8 @@ import Rules from "./../validation/Rules"; import { NetworkConfiguration, NetworkUtils } from "unipept-web-components"; import DockerCommunicator from "@/logic/communication/docker/DockerCommunicator"; import Utils from "@/logic/Utils"; +import { promises as fs } from "fs"; +import FastaParser from "./../../logic/filesystem/fasta/FastaParser"; @Component export default class SettingsPage extends Vue { @@ -342,6 +393,25 @@ export default class SettingsPage extends Vue { return this.configuration.dockerConfigurationSettings; } + set excludeSequences(val: boolean) { + this.configuration.crapFilteringEnabled = val; + } + + get excludeSequences(): boolean { + return this.configuration.crapFilteringEnabled; + } + + set crapSequences(val: string) { + if (val == null) { + val = ""; + } + this.configuration.crapSequences = val.split("\r?\n"); + } + + get crapSequences(): string { + return this.configuration.crapSequences.join("\n"); + } + set customDbStorageLocation(value: string) { this.configuration.customDbStorageLocation = value; } @@ -439,6 +509,28 @@ export default class SettingsPage extends Vue { } } + public async importCrapFasta(): Promise { + const { dialog } = require("@electron/remote"); + + const chosenPath: Electron.OpenDialogReturnValue | undefined = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [ + { name: "FASTA files", extensions: ["fasta"] } + ] + }); + + if (chosenPath && chosenPath.filePaths.length > 0) { + const fastaPath = chosenPath.filePaths[0]; + const fastaContent = await fs.readFile(fastaPath, "utf8"); + + // Now, parse the fasta content and extract the protein sequences + const fastaParser = new FastaParser(); + const sequences = fastaParser.extractProteinSequences(fastaContent); + + this.crapSequences = sequences.join("\n"); + } + } + private showError(message: string): void { this.errorVisible = true; this.errorMessage = message; diff --git a/src/logic/configuration/Configuration.ts b/src/logic/configuration/Configuration.ts index cb750266..6721efe3 100644 --- a/src/logic/configuration/Configuration.ts +++ b/src/logic/configuration/Configuration.ts @@ -7,4 +7,6 @@ export default interface Configuration { configurationAppVersion: string; // List of custom Unipept endpoints that were provided to this app. endpoints: string[]; + crapFilteringEnabled: boolean; + crapSequences: string[]; } diff --git a/src/logic/configuration/ConfigurationManager.ts b/src/logic/configuration/ConfigurationManager.ts index 300fd425..a44dad85 100644 --- a/src/logic/configuration/ConfigurationManager.ts +++ b/src/logic/configuration/ConfigurationManager.ts @@ -36,8 +36,9 @@ export default class ConfigurationManager { } }, (config: Configuration) => config.customDbStorageLocation !== "", - (config: Configuration) => config.endpoints.every(e => this.isUrl(e)) - ] + (config: Configuration) => config.endpoints.every(e => this.isUrl(e)), + (config: Configuration) => Array.isArray(config.crapSequences), + ]; private app: App; @@ -69,6 +70,14 @@ export default class ConfigurationManager { data["endpoints"].push(ConfigurationManager.DEFAULT_ENDPOINT); } + if (!data["crapSequences"] || !Array.isArray(data["crapSequences"])) { + data["crapSequences"] = []; + } + + if (!data["crapFilteringEnabled"]) { + data["crapFilteringEnabled"] = false; + } + if (!this.isValidConfiguration(data)) { ConfigurationManager.currentConfiguration = await this.getDefaultConfiguration(); return ConfigurationManager.currentConfiguration; @@ -93,7 +102,9 @@ export default class ConfigurationManager { Utils.isWindows() ? DockerCommunicator.WINDOWS_DEFAULT_SETTINGS : DockerCommunicator.UNIX_DEFAULT_SETTINGS, customDbStorageLocation: customDbDir, configurationAppVersion: this.app.getVersion(), - endpoints: ["https://api.unipept.ugent.be/"] + endpoints: ["https://api.unipept.ugent.be/"], + crapFilteringEnabled: false, + crapSequences: [] } } diff --git a/src/logic/filesystem/fasta/FastaParser.ts b/src/logic/filesystem/fasta/FastaParser.ts new file mode 100644 index 00000000..55259525 --- /dev/null +++ b/src/logic/filesystem/fasta/FastaParser.ts @@ -0,0 +1,41 @@ +export default class FastaParser { + /** + * This function reads in a FASTA string and extracts all protein sequences from this string and returns them as an + * array of strings. + * + * @param fasta + */ + public extractProteinSequences(fasta: string): string[] { + // Split the input string into lines + const lines = fasta.trim().split("\n"); + + // Initialize an empty array to hold the sequences + const sequences: string[] = []; + + // Initialize an empty string to hold the current sequence + let currentSequence = ""; + + // Iterate over each line + for (const line of lines) { + // If the line starts with '>', it's a description line + if (line.startsWith(">")) { + // If there's a current sequence, add it to the sequences array + if (currentSequence) { + sequences.push(currentSequence); + } + // Start a new sequence + currentSequence = ""; + } else { + // If the line doesn't start with '>', it's a sequence line, so add it to the current sequence + currentSequence += line; + } + } + + // If there's a current sequence at the end, add it to the sequences array + if (currentSequence) { + sequences.push(currentSequence); + } + + return sequences; + } +} diff --git a/src/logic/filesystem/study/StudyFileSystemDataWriter.ts b/src/logic/filesystem/study/StudyFileSystemDataWriter.ts index 436f8a7d..1b4a91e2 100644 --- a/src/logic/filesystem/study/StudyFileSystemDataWriter.ts +++ b/src/logic/filesystem/study/StudyFileSystemDataWriter.ts @@ -11,7 +11,7 @@ export default class StudyFileSystemDataWriter extends FileSystemStudyVisitor { public async visitStudy(study: Study): Promise { try { // Make study directory if it does not exist yet... - await mkdirp(`${this.studyPath}`); + await mkdirp(this.studyPath); await this.dbManager.performQuery((db: Database) => { db.prepare("REPLACE INTO studies (id, name) VALUES (?, ?)").run(study.getId(), study.getName())