DatasetService.java

/*
 * Copyright 2018 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.genesys.server.service;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.validation.Valid;

import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.NoSuchRepositoryFileException;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.server.api.FilteredPage;
import org.genesys.server.exception.NotFoundElement;
import org.genesys.server.model.dataset.Dataset;
import org.genesys.server.model.dataset.DatasetAccessionRef;
import org.genesys.server.model.dataset.DatasetCreator;
import org.genesys.server.model.dataset.DatasetLang;
import org.genesys.server.model.dataset.DatasetLocation;
import org.genesys.server.model.filters.DatasetFilter;
import org.genesys.server.model.genesys.Accession;
import org.genesys.server.model.impl.FaoInstitute;
import org.genesys.server.model.traits.Descriptor;
import org.genesys.server.service.filter.AccessionFilter;
import org.genesys.server.exception.SearchException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.annotation.JsonUnwrapped;

/**
 * The Interface DatasetService.
 */
public interface DatasetService extends FilteredTranslatedCRUDService
	<Dataset, DatasetLang, DatasetTranslationService.TranslatedDataset, DatasetFilter> { 

	String DATASET_METADATA_FILE_NAME = "Metadata.xlsx";

	/**
	 * Allow admins to update existing records
	 * @param updated dataset with updates
	 * @return updated dataset
	 */
	Dataset forceUpdate(Dataset updated);

	/**
	 * Method updating accessionRefs in Dataset.
	 *
	 * @param dataset the dataset
	 * @param accessionRefs new accessionRefs
	 * @return updated Dataset in db.
	 */
	Dataset setAccessionRefs(Dataset dataset, @Valid Collection<DatasetAccessionRef> accessionRefs);

	/**
	 * Method adding new descriptor to Dataset.
	 *
	 * @param dataset the dataset
	 * @param descriptors the descriptors
	 * @return updated Dataset in db.
	 */
	Dataset addDescriptors(Dataset dataset, Descriptor... descriptors);

	/**
	 * Method removing preserved descriptor from Dataset.
	 *
	 * @param dataset the dataset
	 * @param descriptors the descriptors
	 * @return updated Dataset in db.
	 */
	Dataset removeDescriptors(Dataset dataset, Descriptor... descriptors);

	/**
	 * Update all descriptors.
	 *
	 * @param dataset the dataset
	 * @param descriptors ordered descriptor list
	 * @return updated dataset
	 */
	Dataset updateDescriptors(Dataset dataset, List<Descriptor> descriptors);

	/**
	 * Load dataset based on identifiers and version provided in the input.
	 *
	 * @param input query by example
	 * @return dataset loaded from the database
	 */
	Dataset loadDataset(Dataset input);

	/**
	 * Load dataset.
	 *
	 * @param uuid the uuid
	 * @param version the version
	 * @return the dataset
	 */
	Dataset loadDataset(UUID uuid, int version);

	/**
	 * Load AccessionRef list by Dataset.
	 *
	 * @param dataset the dataset
	 * @param page Pageable
	 * @return PageImpl of AccessionRef
	 */
	Page<DatasetAccessionRef> listAccessions(Dataset dataset, Pageable page);

	/**
	 * List current and published datasets by accession.
	 *
	 * @param accession the accession
	 * @return list of Dataset
	 */
	List<Dataset> listByAccession(Accession accession);

	@Transactional(readOnly = true)
	Page<DatasetTranslationService.TranslatedDataset> listTranslated(DatasetFilter filter, Pageable page) throws SearchException;

	/**
	 * Get terms by filter.
	 * @param filter the dataset filter
	 *
	 * @return the term result
	 * @throws SearchException the search exception
	 * @throws IOException the IO exception
	 */
	Map<String, ElasticsearchService.TermResult> getSuggestions(DatasetFilter filter) throws SearchException, IOException;

	/**
	 * List datasets matching the filter for current user.
	 *
	 * @param filter filter data
	 * @param page Pageable
	 * @return list of Dataset
	 */
	Page<Dataset> listDatasetsForCurrentUser(DatasetFilter filter, Pageable page) throws SearchException;

	/**
	 * Load list of Dataset.
	 *
	 * @param page page
	 * @return list of Detaset
	 */
	Page<Dataset> loadCurrentVersionList(Pageable page);

	/**
	 * Load Dataset by UUID.
	 *
	 * @param uuid uuid of dataset
	 * @return loaded dataset
	 * @throws NotFoundElement when dataset not found by uuid
	 */
	Dataset loadDataset(UUID uuid) throws NotFoundElement;

	/**
	 * Load translated Dataset by UUID.
	 *
	 * @param uuid uuid of dataset
	 * @return loaded dataset
	 * @throws NotFoundElement when dataset not found by uuid
	 */
	DatasetTranslationService.TranslatedDataset loadTranslatedDataset(UUID uuid) throws NotFoundElement;

	/**
	 * Adds the file to dataset.
	 *
	 * @param dataset the dataset
	 * @param file file that be added to Db and dataset
	 * @return updated dataset
	 * @throws NotFoundElement NotFoundElement
	 * @throws IOException IOException
	 * @throws InvalidRepositoryPathException InvalidRepositoryPathException
	 * @throws InvalidRepositoryFileDataException InvalidRepositoryFileDataException
	 */
	Dataset addDatasetFile(Dataset dataset, MultipartFile file) throws NotFoundElement, IOException, InvalidRepositoryPathException, InvalidRepositoryFileDataException;

	/**
	 * Removes the file of dataset.
	 *
	 * @param dataset the dataset
	 * @param fileUuid UUID of repositoryFile
	 * @return updated dataset
	 * @throws NotFoundElement NotFoundElement
	 * @throws NoSuchRepositoryFileException NoSuchRepositoryFileException
	 * @throws IOException IOException
	 */
	Dataset removeDatasetFile(Dataset dataset, UUID fileUuid) throws NotFoundElement, NoSuchRepositoryFileException, IOException;

	/**
	 * Load list of RepositoryFile by uuid of dataset.
	 *
	 * @param dataset the dataset
	 * @return loaded list of RepositoryFile
	 * @throws NotFoundElement the not found element
	 */
	List<RepositoryFile> listDatasetFiles(Dataset dataset) throws NotFoundElement;

	/**
	 * Register Accession identifiers with the Dataset.
	 *
	 * @param dataset the dataset
	 * @param accessionRefs the accessionRefs
	 * @return updated Dataset
	 * @throws NotFoundElement the not found element
	 */
	Dataset addAccessionRefs(Dataset dataset, @Valid Collection<DatasetAccessionRef> accessionRefs) throws NotFoundElement;

	/**
	 * Puts the dataset into the Review state to be reviewed by admin.
	 * Admin and the user with MANAGE permission - only.
	 *
	 * @param dataset the dataset
	 * @return dataset in REVIEWING state
	 */
	Dataset reviewDataset(Dataset dataset);

	/**
	 * Unpublishes published dataset.
	 * Admin and the user with MANAGE permission - only.
	 *
	 * @param dataset the dataset
	 * @return unpublished dataset
	 */
	Dataset rejectDataset(Dataset dataset);

	/**
	 * Validates and publishes an unpublished dataset. Admin - only
	 *
	 * @param dataset the dataset
	 * @return published dataset
	 */
	Dataset approveDataset(Dataset dataset);

	/**
	 * Update dataset file information.
	 *
	 * @param dataset the dataset
	 * @param metadata the updated file metadata
	 * @return updated dataset
	 * @throws NoSuchRepositoryFileException the no such repository file exception
	 */
	Dataset updateDatasetFile(Dataset dataset, @Valid RepositoryFile metadata) throws NoSuchRepositoryFileException;

	/**
	 * Count the number of published datasets.
	 *
	 * @param filter the filter
	 * @return the number of published datasets
	 */
	long countDatasets(DatasetFilter filter) throws SearchException;

	/**
	 * Load data about last published datasets.
	 *
	 * @return the data about last published datasets
	 */
	List<Object[]> lastPublished();

	/**
	 * Rematch dataset accessions for all datasets.
	 */
	void rematchDatasetAccessions();

	/**
	 * Rematch dataset accessions.
	 *
	 * @param dataset the dataset
	 * @return the dataset
	 */
	Dataset rematchDatasetAccessions(Dataset dataset);

	/**
	 * Create DatasetCreator for Dataset.
	 *
	 * @param dataset the dataset
	 * @param input DatasetCreator
	 * @return created DatasetCreator
	 * @throws NotFoundElement throws if don't match the version
	 */
	DatasetCreator createDatasetCreator(Dataset dataset, @Valid DatasetCreator input) throws NotFoundElement;

	/**
	 * Remove DatasetCreator of Dataset.
	 *
	 * @param dataset the dataset
	 * @param input DatasetCreator
	 * @return removed DatasetCreator
	 * @throws NotFoundElement throws if don't match the version
	 */
	DatasetCreator removeDatasetCreator(Dataset dataset, DatasetCreator input) throws NotFoundElement;

	/**
	 * Load list DatasetCreators by Dataset UUID.
	 *
	 * @param datasetUuid Dataset UUID
	 * @param page Page
	 * @return Page with DatasetCreator list
	 */
	Page<DatasetCreator> listDatasetCreators(UUID datasetUuid, Pageable page);

	/**
	 * Load dataset by input DatasetCreator.
	 *
	 * @param datasetCreator input DatasetCreator
	 * @return loaded DatasetCreator
	 * @throws NotFoundElement throws if don't match the version
	 */
	DatasetCreator loadDatasetCreator(DatasetCreator datasetCreator) throws NotFoundElement;

	/**
	 * Load dataset by input DatasetCreator.
	 *
	 * @param datasetCreatorUuid input DatasetCreator UUID
	 * @return loaded DatasetCreator
	 * @throws NotFoundElement throws if don't match the version
	 */
	DatasetCreator loadDatasetCreator(UUID datasetCreatorUuid) throws NotFoundElement;

	/**
	 * Load list DatasetCreators by Dataset UUID.
	 *
	 * @param dataset the dataset
	 * @return List of DatasetCreator
	 * @throws NotFoundElement throws if don't match the version
	 */
	List<DatasetCreator> loadDatasetCreators(Dataset dataset) throws NotFoundElement;

	/**
	 * Update dataset creator.
	 *
	 * @param dataset the dataset
	 * @param datasetCreator the dataset creator
	 * @return the dataset creator
	 * @throws NotFoundElement the not found element
	 */
	DatasetCreator updateDatasetCreator(Dataset dataset, @Valid DatasetCreator datasetCreator) throws NotFoundElement;

	/**
	 * Autocomplete creators.
	 *
	 * @param text the text
	 * @return list of matched creators
	 */
	List<DatasetCreator> autocompleteCreators(String text);


	/**
	 * Method adding DatasetLocation to Dataset.
	 *
	 * @param dataset the dataset
	 * @param input DatasetLocation
	 * @return added DatasetLocation
	 * @throws NotFoundElement the not found element
	 */
	DatasetLocation createLocation(Dataset dataset, @Valid DatasetLocation input) throws NotFoundElement;

	/**
	 * Method for remove DatasetLocation of Dataset.
	 *
	 * @param dataset the dataset
	 * @param input DatasetLocation
	 * @return removed DatasetLocation
	 * @throws NotFoundElement the not found element
	 */
	DatasetLocation removeLocation(Dataset dataset, DatasetLocation input) throws NotFoundElement;

	/**
	 * Method for getting DatasetLocation list of Dataset.
	 *
	 * @param dataset the dataset
	 * @return loaded List
	 * @throws NotFoundElement the not found element
	 */
	List<DatasetLocation> listLocation(Dataset dataset) throws NotFoundElement;

	/**
	 * Method for getting DatasetLocation list of Dataset.
	 *
	 * @param datasetUuid Dataset UUID
	 * @param page page
	 * @return page with loaded list DatasetLocation
	 */
	Page<DatasetLocation> listLocation(UUID datasetUuid, Pageable page);

	/**
	 * Method for getting DatasetLocation by UUID.
	 *
	 * @param input DatasetLocation input
	 * @return loaded DatasetLocation
	 * @throws NotFoundElement the not found element
	 */
	DatasetLocation loadLocation(DatasetLocation input) throws NotFoundElement;

	/**
	 * Method for getting DatasetLocation by UUID.
	 *
	 * @param locationUuid DatasetLocation UUID
	 * @return loaded DatasetLocation
	 * @throws NotFoundElement the not found element
	 */
	DatasetLocation loadLocation(UUID locationUuid) throws NotFoundElement;

	/**
	 * Method for update location.
	 *
	 * @param dataset the dataset
	 * @param input data of location
	 * @return updated location
	 * @throws NotFoundElement the not found element
	 */
	DatasetLocation updateLocation(Dataset dataset, @Valid DatasetLocation input) throws NotFoundElement;

	/**
	 * Clear accession references across all Datasets for specified accessions. 
	 *
	 * @param accessions the accessions
	 * @return the int
	 */
	int clearAccessionRefs(Collection<Accession> accessions);

	/**
	 * Get the repository path where dataset files are stored
	 * 
	 * @param dataset
	 * @return the path to the repository folder
	 */
	Path getDatasetRepositoryFolder(Dataset dataset);

	/**
	 * Gets the dataset, nothing lazyloaded.
	 *
	 * @param uuid the uuid
	 */
	Dataset getDataset(UUID uuid);

	/**
	 * Gets the dataset, nothing lazyloaded.
	 *
	 * @param uuid the uuid
	 * @param version the version
	 * @return the dataset
	 */
	Dataset getDataset(UUID uuid, Integer version);

	/**
	 * Generate Excel file with MCPD passport data for provided dataset.
	 *
	 * @param dataset the dataset
	 * @param outputStream the stream
	 */
	void writeXlsxMCPD(Dataset dataset, OutputStream outputStream) throws IOException;

	/**
	 * Method creating a new version of Dataset based on an existing published Dataset.
	 *
	 * @param source the source
	 * @return saved Dataset in db.
	 */
	Dataset createNewVersion(@Valid Dataset source);

	public static class DatasetOverview {
		public String filterCode;
		public DatasetFilter filter;
		public long datasetCount;
		public Map<String, ElasticsearchService.TermResult> overview;
		public Map<String, ElasticsearchService.TermResult> suggestions;

		public static DatasetOverview from(String filterCode, DatasetFilter filter, Map<String, ElasticsearchService.TermResult> overview, long datasetCount, Map<String, ElasticsearchService.TermResult> suggestions) {
			DatasetOverview res = new DatasetOverview();
			res.filterCode = filterCode;
			res.filter = filter;
			res.overview = overview;
			res.datasetCount = datasetCount;
			res.suggestions = suggestions;

			return res;
		}
	}

	public static class DatasetSuggestionPage {
		@JsonUnwrapped
		public FilteredPage<Dataset, DatasetFilter> page;
		public Map<String, ElasticsearchService.TermResult> suggestions;

		public static DatasetSuggestionPage from(FilteredPage<Dataset, DatasetFilter> page, Map<String, ElasticsearchService.TermResult> suggestions) {
			DatasetSuggestionPage res = new DatasetSuggestionPage();
			res.page = page;
			res.suggestions = suggestions;

			return res;
		}
	}

	/**
	 * Update existing Dataset and accession refs from old FAO WIEWS Institute to the new Institute.
	 * This updates accession.instCode and accession.institute in the database.
	 * 
	 * @param currentInstitute current {@link FaoInstitute}
	 * @param newInstitute new institute
	 * @throws RuntimeException if the new institute already has Subsets
	 * @return number of updated {@link DatasetAccessionRef} records
	 */
	long changeInstitute(FaoInstitute currentInstitute, FaoInstitute newInstitute);

	/**
	 * Get lazy-loaded {@code Descriptors} in the {@code dataset}.
	 * 
	 * @param dataset the dataset
	 * @return All descriptors in this dataset
	 */
	List<Descriptor> getDatasetDescriptors(Dataset dataset);

	/**
	 * Remove descriptors from the Dataset that are not in Amphibian.
	 * Add missing descriptors that are in Amphibian to the Dataset
	 *
	 * @param dataset the dataset
	 * @return All synchronized descriptors in this dataset
	 */
	List<Descriptor> synchronizeDescriptors(Dataset dataset);
	
	/**
	 * Schedule re-matching of AccessionRefs in batches
	 * @param accessionRefs
	 */
	void batchRematchAccessionRefs(List<DatasetAccessionRef> accessionRefs);

	/**
	 * Find published Datasets by AccessionFilter.
	 *
	 * @param accessionFilter AccessionFilter
	 * @return Page with Datasets
	 */
	Set<UUID> findAccessionsAmphibianDatasets(AccessionFilter accessionFilter);

	/**
	 * Get all localized descriptors for list by UUID
	 * @param descriptorList Descriptor list
	 */
	List<DescriptorTranslationService.TranslatedDescriptor> loadTranslatedDescriptors(Dataset dataset);

}