-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ea17281
commit 6e9ca3d
Showing
6 changed files
with
411 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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[]>; | ||
} |
6 changes: 6 additions & 0 deletions
6
src/domain/interfaces/use-cases/sample/list-importable-samples.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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[]>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
// } | ||
|
||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} | ||
} |
Oops, something went wrong.