Skip to content

Commit

Permalink
Implemented most of the file system reading / writing on Electron's side
Browse files Browse the repository at this point in the history
  • Loading branch information
pverscha committed Feb 8, 2024
1 parent 4c8b350 commit 5de30cf
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 89 deletions.
30 changes: 28 additions & 2 deletions src/common/project/Project.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
import Study from "@common/study/Study";

export default class Project {
private studies: Study[];

/**
* A project is the global entity that keeps track of all the studies and assays that belong together.
*
* @param name Name of the project. This can be used by the user to distinguish between different projects and their
* unique properties
* @param location Value used to identify where this project can be retrieved from. In most cases this is a
* directory somewhere on the local system, but it does not need to be.
*/
constructor(
public readonly name: string,
public readonly location: string,
public readonly studies: Study[],
) {}
) {
this.studies = [];
}

public addStudy(study: Study) {
this.studies.push(study);
}

public getStudies(): Study[] {
return this.studies;
}

public removeStudy(study: Study) {
const idx = this.studies.findIndex((val: Study) => val.getId() === study.getId());
if (idx !== -1) {
this.studies.splice(idx, 1);
}
}
}
25 changes: 25 additions & 0 deletions src/common/project/ProjectManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Project from "@common/project/Project";

export default interface ProjectManager {
/**
* Load an existing project. If the project files do not exist, this function will throw an error.
*
* @param projectLocation The main directory of the project on disk.
* @param addToRecents Should this project be added to the list of recent projects? Set to false for no.
*/
loadProject(
projectLocation: string,
addToRecents: boolean
): Promise<Project>;

/**
* Create a new project and correctly initialize all project files in the provided directory.
*
* @param projectLocation The main directory of the project on disk.
* @param addToRecents Should this project be added to the list of recent projects? Set to false for no.
*/
createProject(
projectLocation: string,
addToRecents: boolean
): Promise<Project>;
}
26 changes: 16 additions & 10 deletions src/common/study/StudyManager.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import Study from "@common/study/Study";
import DatabaseManager from "@main/database/DatabaseManager";

export default interface StudyManager {
/**
* Load a study from the given directory. The study's name is assumed to be the name of the directory. If the given
* directory is empty, a new study will be created.
* Load all studies that are associated to this study manager. Typically something to identify these studies (such
* as a project) is passed along the constructor of this class.
*/
loadStudies(): Promise<Study[]>;

/**
* Write the given study object (that is associated with the given project) to disk.
*
* @param study
*/
writeStudy(study: Study): Promise<void>;

/**
* Remove the given study object from the underlying storage system.
*
* @param directory The directory that contains all assays and required metadata for this study.
* @param dbManager A database manager connected to the project that this study belongs to and that can be used for
* retrieving / updating this study's metadata.
* @param study
*/
loadStudy(
directory: string,
dbManager: DatabaseManager
): Promise<Study>;
removeStudy(study: Study): Promise<void>;
}
12 changes: 8 additions & 4 deletions src/main/assay/FileSystemAssayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@ import createWorker from "./PeptideReaderWorker?nodeWorker";
import { Worker } from "worker_threads";
import { AssayTableRow } from "@main/database/schemas/Schema";
import { Database as DbType } from "better-sqlite3";
import Project from "@common/project/Project";
import path from "path";
import Study from "@common/study/Study";

export default class FileSystemAssayManager implements AssayManager {
private static inProgress: Promise<string[]> | undefined
private static worker: Worker;

public constructor(
private readonly directoryPath: string,
private readonly dbManager: DatabaseManager
private readonly dbManager: DatabaseManager,
private readonly project: Project,
private readonly study: Study
) {}

public async loadAssay(
assayName: string,
assayId: string
): Promise<Assay> {
const path = `${this.directoryPath}${assayName}.pep`;
const assayPath = `${path.join(this.project.location, this.study.getName(), assayName)}.pep`;

// First, try to read in all the peptides for this assay.
const peptidesString: string = await fs.readFile(path, {
const peptidesString: string = await fs.readFile(assayPath, {
encoding: "utf-8"
});

Expand Down
4 changes: 4 additions & 0 deletions src/main/file-system/FileSystemManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export default class FileSystemManager {
return fs.writeFile(path, contents, { encoding: "utf-8" });
}

public removeFile(path: string): Promise<void> {
return fs.rm(path, { recursive: true });
}

public async mkdir(path: string): Promise<void> {
await fs.mkdir(path, { recursive: true });
}
Expand Down
104 changes: 104 additions & 0 deletions src/main/project/FileSystemProjectManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { v4 as uuidv4 } from "uuid";
import path from "path";
import DatabaseManager from "@main/database/DatabaseManager";
import VersionUtils from "@main/utils/VersionUtils";
import Project from "@common/project/Project";
import Study from "@common/study/Study";
import ProjectManager from "@common/project/ProjectManager";
import FileSystemManager from "@main/file-system/FileSystemManager";
import AppManager from "@main/app/AppManager";
import FileSystemStudyManager from "@main/study/FileSystemStudyManager";
import FileSystemRecentProjectManager from "@main/project/FileSystemRecentProjectManager";

export default class FileSystemProjectManager implements ProjectManager {
private static readonly DB_FILE_NAME: string = "metadata.sqlite";

public async loadProject(
projectLocation: string,
addToRecents: boolean = true
): Promise<Project> {
if (!projectLocation.endsWith("/")) {
projectLocation += "/";
}

const fsManager = new FileSystemManager();

// Check if a project is actually present in this directory. If there isn't, we cannot load the project.
if (!await fsManager.fileExists(projectLocation + FileSystemProjectManager.DB_FILE_NAME)) {
throw new Error("InvalidProjectException: Project metadata file was not found!");
}

const appManager = new AppManager();

const dbManager = new DatabaseManager(
projectLocation + FileSystemProjectManager.DB_FILE_NAME
);
await dbManager.initializeDatabase(appManager.getAppVersion());
const dbAppVersion = dbManager.getApplicationVersion();

if (VersionUtils.isVersionLargerThan(dbAppVersion, appManager.getAppVersion())) {
throw new Error("ProjectVersionMismatchException: Project was made with a more recent version of Unipept Desktop!");
} else {
await dbManager.setApplicationVersion(appManager.getAppVersion());
}

const project = new Project(
path.basename(projectLocation),
projectLocation
);

const studyManager = new FileSystemStudyManager(
dbManager,
project
);

for (const study of await studyManager.loadStudies()) {
project.addStudy(study);
}

if (addToRecents) {
const recentProjectsMng = new FileSystemRecentProjectManager();
await recentProjectsMng.addRecentProject(projectLocation);
}

return project;
}

public async createProject(
projectLocation: string,
addToRecents: boolean = true
): Promise<Project> {
if (!projectLocation.endsWith("/")) {
projectLocation += "/";
}

const fsManager = new FileSystemManager();

// Create the project directory
await fsManager.mkdir(projectLocation);

const dbManager = new DatabaseManager(
path.join(projectLocation, FileSystemProjectManager.DB_FILE_NAME)
);

const appManager = new AppManager();
await dbManager.initializeDatabase(appManager.getAppVersion());

// Create one dummy study for this project
const study = new Study(uuidv4());
study.setName("Study name");

const project = new Project(path.basename(projectLocation), projectLocation);
project.addStudy(study);

const studyManager = new FileSystemStudyManager(dbManager, project);
studyManager.writeStudy(study);

if (addToRecents) {
const recentProjectsMng = new FileSystemRecentProjectManager();
await recentProjectsMng.addRecentProject(projectLocation);
}

return project;
}
}
69 changes: 0 additions & 69 deletions src/main/project/ProjectManager.ts

This file was deleted.

Loading

0 comments on commit 5de30cf

Please sign in to comment.