Skip to content

Commit

Permalink
DEV list importable lpm samples
Browse files Browse the repository at this point in the history
  • Loading branch information
juliecoust committed Sep 17, 2024
1 parent ea17281 commit 6e9ca3d
Show file tree
Hide file tree
Showing 6 changed files with 411 additions and 1 deletion.
51 changes: 51 additions & 0 deletions src/domain/entities/sample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

export interface PublicSampleResponseModel {
sample_name: string,
raw_file_name: string,
station_id: string,
first_image: string,
last_image: string,
comment: string,
qc_lvl1: boolean,
qc_lvl1_comment: string,
}

export interface PublicHeaderSampleResponseModel {
sample_name: string,
raw_file_name: string,
station_id: string,
first_image: string,
last_image: string,
comment: string,
qc_lvl1: boolean,
qc_lvl1_comment: string,
}

export interface HeaderSampleModel {
cruise: string;
ship: string;
filename: string;
profileId: string;
bottomDepth: string;
ctdRosetteFilename: string;
latitude: string;
longitude: string;
firstImage: string;
volImage: string;
aa: string;
exp: string;
dn: string;
windDir: string;
windSpeed: string;
seaState: string;
nebulousness: string;
comment: string;
endImg: string;
yoyo: string;
stationId: string;
sampleType: string;
integrationTime: string;
argoId: string;
pixelSize: string;
sampleDateTime: string;
}
6 changes: 6 additions & 0 deletions src/domain/interfaces/repositories/sample-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { PublicSampleResponseModel } from "../../entities/sample";

export interface SampleRepository {
ensureFolderExists(root_folder_path: string): Promise<void>;
listImportableSamples(root_folder_path: string): Promise<PublicSampleResponseModel[]>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { PublicSampleResponseModel } from "../../../entities/sample";
import { UserUpdateModel } from "../../../entities/user";

export interface ListImportableSamplesUseCase {
execute(current_user: UserUpdateModel, project_id: number): Promise<PublicSampleResponseModel[]>;
}
244 changes: 244 additions & 0 deletions src/domain/repositories/sample-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@

// import { SampleDataSource } from "../../data/interfaces/data-sources/sample-data-source";
// import { InstrumentModelResponseModel } from "../entities/instrument_model";
// import { PublicPrivilege } from "../entities/privilege";
// import { SampleRequestCreationModel, SampleRequestModel, SampleUpdateModel, SampleResponseModel, PublicSampleResponseModel, PublicSampleRequestCreationModel } from "../entities/sample";
// import { PreparedSearchOptions, SearchResult } from "../entities/search";
// import { SampleRepository } from "../interfaces/repositories/sample-repository";

import { head } from "shelljs";
import { HeaderSampleModel, PublicHeaderSampleResponseModel } from "../entities/sample";
import { SampleRepository } from "../interfaces/repositories/sample-repository";


import { promises as fs } from 'fs';
import path from 'path';

export class SampleRepositoryImpl implements SampleRepository {

//sampleDataSource: SampleDataSource

// // TODO move to a search repository
// order_by_allow_params: string[] = ["asc", "desc"]
// filter_operator_allow_params: string[] = ["=", ">", "<", ">=", "<=", "<>", "IN", "LIKE"]

// constructor(sampleDataSource: SampleDataSource) {
// this.sampleDataSource = sampleDataSource
// }

async ensureFolderExists(root_folder_path: string): Promise<void> {
const folderPath = path.join(root_folder_path);

try {
await fs.access(folderPath);
console.log('Folder exists');
} catch (error) {
throw new Error(`Folder does not exist at path: ${folderPath}`);
}
}

async listImportableSamples(root_folder_path: string): Promise<PublicHeaderSampleResponseModel[]> {
const folderPath = path.join(root_folder_path);
// read from folderPath/meta/*header*.txt and return the list of samples
const meta_header_samples = await this.getSamplesFromHeaders(folderPath);

// read from folderPath/ecodata and return the list of samples
const ecodata_samples = await this.getSamplesFromEcodata(folderPath);

// flag qc samples to flase if not in both lists, and add qc message
const samples: PublicHeaderSampleResponseModel[] = [];
for (const sample of meta_header_samples) {
samples.push({
sample_name: sample.filename,
raw_file_name: sample.filename,
station_id: sample.stationId,
first_image: sample.firstImage,
last_image: sample.endImg,
comment: sample.comment,
qc_lvl1: ecodata_samples.includes(sample.filename) ? true : false,
qc_lvl1_comment: ecodata_samples.includes(sample.filename) ? '' : 'Sample not found in ecodata folder'
});
}
return samples;
}

// Function to read and return samples from header.txt files
async getSamplesFromHeaders(folderPath: string): Promise<HeaderSampleModel[]> {
const samples: HeaderSampleModel[] = [];
try {
const header_path = path.join(folderPath, 'meta');
const files = await fs.readdir(header_path);
console.log('header files', files);
for (const file of files) {
if (file.includes('header') && file.endsWith('.txt')) {
const filePath = path.join(header_path, file);
const content = await fs.readFile(filePath, 'utf8');

const lines = content.trim().split('\n');
for (let i = 1; i < lines.length; i++) {
samples.push(this.getSampleFromHeaderLine(lines[i]));
}
}
}
} catch (err) {
throw new Error(`Error reading files: ${err.message}`);
}

return samples;
}

getSampleFromHeaderLine(line: string): HeaderSampleModel {
console.log('line', line);
const fields = line.split(';');

const sample: HeaderSampleModel = {
cruise: fields[0],
ship: fields[1],
filename: fields[2],
profileId: fields[3],
bottomDepth: fields[4],
ctdRosetteFilename: fields[5],
latitude: fields[6],
longitude: fields[7],
firstImage: fields[8],
volImage: fields[9],
aa: fields[10],
exp: fields[11],
dn: fields[12],
windDir: fields[13],
windSpeed: fields[14],
seaState: fields[15],
nebulousness: fields[16],
comment: fields[17],
endImg: fields[18],
yoyo: fields[19],
stationId: fields[20],
sampleType: fields[21],
integrationTime: fields[22],
argoId: fields[23],
pixelSize: fields[24],
sampleDateTime: fields[25]
};
return sample;
}

// Function to read and return samples from ecodata folder names
async getSamplesFromEcodata(folderPath: string): Promise<string[]> {
const samples: string[] = [];
try {
const files = await fs.readdir(path.join(folderPath, 'ecodata'));

for (const file of files) {
samples.push(file);
}
} catch (err) {
throw new Error(`Error reading files: ${err.message}`);
}

return samples;
}



// async createSample(sample: SampleRequestCreationModel): Promise<number> {
// const result = await this.sampleDataSource.create(sample)
// return result;
// }

// async getSample(sample: SampleRequestModel): Promise<SampleResponseModel | null> {
// const result = await this.sampleDataSource.getOne(sample)
// return result;
// }

// async deleteSample(sample: SampleRequestModel): Promise<number> {
// const result = await this.sampleDataSource.deleteOne(sample)
// return result;
// }

// private async updateSample(sample: SampleUpdateModel, params: string[]): Promise<number> {
// const filteredSample: Partial<SampleUpdateModel> = {};
// const unauthorizedParams: string[] = [];

// // Filter the sample object based on authorized parameters
// Object.keys(sample).forEach(key => {
// if (key === 'sample_id') {
// filteredSample[key] = sample[key];
// } else if (params.includes(key)) {
// filteredSample[key] = sample[key];
// } else {
// unauthorizedParams.push(key);
// }
// });

// // If unauthorized params are found, throw an error
// if (unauthorizedParams.length > 0) {
// throw new Error(`Unauthorized or unexisting parameters : ${unauthorizedParams.join(', ')}`);
// }
// // If there are valid parameters, update the sample
// if (Object.keys(filteredSample).length <= 1) {
// throw new Error('Please provide at least one valid parameter to update');
// }
// const updatedSampleCount = await this.sampleDataSource.updateOne(filteredSample as SampleUpdateModel);
// return updatedSampleCount;
// }

// async standardUpdateSample(sample: SampleUpdateModel): Promise<number> {
// const params_restricted = ["sample_id", "root_folder_path", "sample_title", "sample_acronym", "sample_description", "sample_information", "cruise", "ship", "data_owner_name", "data_owner_email", "operator_name", "operator_email", "chief_scientist_name", "chief_scientist_email", "override_depth_offset", "enable_descent_filter", "privacy_duration", "visible_duration", "public_duration", "instrument_model", "serial_number"]
// const updated_sample_nb = await this.updateSample(sample, params_restricted)
// return updated_sample_nb
// }

// async standardGetSamples(options: PreparedSearchOptions): Promise<SearchResult<SampleResponseModel>> {
// // Can be filtered by
// const filter_params_restricted = ["sample_id", "root_folder_path", "sample_title", "sample_acronym", "sample_description", "sample_information", "cruise", "ship", "data_owner_name", "data_owner_email", "operator_name", "operator_email", "chief_scientist_name", "chief_scientist_email", "override_depth_offset", "enable_descent_filter", "privacy_duration", "visible_duration", "public_duration", "instrument_model", "serial_number", "sample_creation_date"]

// // Can be sort_by
// const sort_param_restricted = ["sample_id", "root_folder_path", "sample_title", "sample_acronym", "sample_description", "sample_information", "cruise", "ship", "data_owner_name", "data_owner_email", "operator_name", "operator_email", "chief_scientist_name", "chief_scientist_email", "override_depth_offset", "enable_descent_filter", "privacy_duration", "visible_duration", "public_duration", "instrument_model", "serial_number", "sample_creation_date"]

// return await this.getSamples(options, filter_params_restricted, sort_param_restricted, this.order_by_allow_params, this.filter_operator_allow_params)
// }

// //TODO MOVE TO SEARCH REPOSITORY
// private async getSamples(options: PreparedSearchOptions, filtering_params: string[], sort_by_params: string[], order_by_params: string[], filter_operator_params: string[]): Promise<SearchResult<SampleResponseModel>> {
// const unauthorizedParams: string[] = [];
// //TODO move to a search repository
// // Filter options.sort_by by sorting params
// options.sort_by = options.sort_by.filter(sort_by => {
// let is_valid = true;
// if (!sort_by_params.includes(sort_by.sort_by)) {
// unauthorizedParams.push(`Unauthorized sort_by: ${sort_by.sort_by}`);
// is_valid = false;
// }
// if (!order_by_params.includes(sort_by.order_by)) {
// unauthorizedParams.push(`Unauthorized order_by: ${sort_by.order_by}`);
// is_valid = false;
// }
// return is_valid;
// });

// //TODO move to a search repository
// // Filter options.filters by filtering params
// options.filter = options.filter.filter(filter => {
// let is_valid = true;
// if (!filtering_params.includes(filter.field)) {
// unauthorizedParams.push(`Filter field: ${filter.field}`);
// is_valid = false;
// }
// if (!filter_operator_params.includes(filter.operator)) {
// unauthorizedParams.push(`Filter operator: ${filter.operator}`);
// is_valid = false;
// }
// return is_valid;
// });

// //TODO move to a search repository
// if (unauthorizedParams.length > 0) {
// throw new Error(`Unauthorized or unexisting parameters : ${unauthorizedParams.join(', ')}`);
// }

// return await this.sampleDataSource.getAll(options);
// }



}
70 changes: 70 additions & 0 deletions src/domain/use-cases/sample/list-importable-samples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { PublicSampleResponseModel } from "../../entities/sample";
import { UserUpdateModel } from "../../entities/user";
import { PrivilegeRepository } from "../../interfaces/repositories/privilege-repository";
import { SampleRepository } from "../../interfaces/repositories/sample-repository";
import { ProjectRepository } from "../../interfaces/repositories/project-repository";
import { UserRepository } from "../../interfaces/repositories/user-repository";

import { ListImportableSamplesUseCase } from "../../interfaces/use-cases/sample/list-importable-samples";
import { ProjectResponseModel } from "../../entities/project";

export class ListImportableSamples implements ListImportableSamplesUseCase {
sampleRepository: SampleRepository
userRepository: UserRepository
privilegeRepository: PrivilegeRepository
projectRepository: ProjectRepository

constructor(sampleRepository: SampleRepository, userRepository: UserRepository, privilegeRepository: PrivilegeRepository, projectRepository: ProjectRepository) {
this.sampleRepository = sampleRepository
this.userRepository = userRepository
this.privilegeRepository = privilegeRepository
this.projectRepository = projectRepository
}

async execute(current_user: UserUpdateModel, project_id: number): Promise<PublicSampleResponseModel[]> {
// Ensure the user is valid and can be used
await this.userRepository.ensureUserCanBeUsed(current_user.user_id);

// Ensure the current user has permission to get the project importable samples
await this.ensureUserCanGet(current_user, project_id);

const project: ProjectResponseModel = await this.getProjectIfExist(project_id);

const samples = await this.listImportableSamples(project);

// Ensure the task to get exists
if (!samples) { throw new Error("Cannot find samples"); }

return samples;
}

private async listImportableSamples(project: ProjectResponseModel): Promise<PublicSampleResponseModel[]> {
await this.sampleRepository.ensureFolderExists(project.root_folder_path);
const samples = await this.sampleRepository.listImportableSamples(project.root_folder_path);
return samples;
}

private async getProjectIfExist(project_id: number): Promise<ProjectResponseModel> {
const project = await this.projectRepository.getProject({ project_id: project_id });
if (!project) {
throw new Error("Cannot find project");
}
return project;
}

private async ensureUserCanGet(current_user: UserUpdateModel, project_id: number): Promise<void> {
console.log("project_id : ", project_id);
console.log("current_user.user_id : ", current_user.user_id);
const userIsAdmin = await this.userRepository.isAdmin(current_user.user_id);
console.log("userIsAdmin : ", userIsAdmin);
const userHasPrivilege = await this.privilegeRepository.isGranted({
user_id: current_user.user_id,
project_id: project_id
});
console.log("userHasPrivilege : ", userHasPrivilege);

if (!userIsAdmin && !userHasPrivilege) {
throw new Error("Logged user cannot list importable samples in this project");
}
}
}
Loading

0 comments on commit 6e9ca3d

Please sign in to comment.