blob: 8ee1b93b4ef2eaaeec976557757dbd7ba97346da [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
'''
Classes:
DatasetLoader - Generate OCW Dataset objects from a variety of sources.
'''
import ocw.data_source.local as local
import ocw.data_source.esgf as esgf
import ocw.data_source.rcmed as rcmed
import ocw.data_source.dap as dap
class DatasetLoader:
'''Generate OCW Dataset objects from a variety of sources.'''
def __init__(self, reference, targets):
'''Generate OCW Dataset objects from a variety of sources.
Each keyword argument can be information for a dataset in dictionary
form. For example:
``
>>> reference = {'data_source':'rcmed', 'name':'cru', 'dataset_id':10,
'parameter_id':34}
>>> targets = {'data_source':'local_multiple',
'path':'./data/CORDEX-Africa_data/AFRICA*pr.nc',
'variable':'pr'}
>>> loader = DatasetLoader(reference, targets)
``
Or more conveniently if the loader configuration is defined in a
yaml file named config_file (see RCMES examples):
``
>>> import yaml
>>> config = yaml.load(open(config_file))
>>> loader = DatasetLoader(**config['datasets'])
``
As shown in the first example, the dictionary for each keyword argument
should contain a data source and parameters specific to the loader for
that data source. Once the configuration is entered, the datasets may be
loaded using:
``
>>> loader.load_datasets()
>>> target_datasets = loader.target_datasets
``
If ``reference`` is entered as a keyword argument, then it may be
accesed from:
``
>>> reference_dataset = loader.reference_dataset
``
Additionally, each dataset must have a ``data_source`` keyword. This may
be one of the following:
* ``'local'`` - A single dataset file in a local directory
* ``'local_split'`` - A single dataset split accross multiple files in a
local directory
* ``'local_multiple'`` - Multiple datasets in a local directory
* ``'esgf'`` - Download the dataset from the Earth System Grid
Federation
* ``'rcmed'`` - Download the dataset from the Regional Climate Model
Evaluation System Database
* ``'dap'`` - Download the dataset from an OPeNDAP URL
Users who wish to download datasets from sources not described above
may define their own custom dataset loader function and incorporate it
as follows:
>>> loader.add_source_loader('my_source_name', my_loader_func)
:param reference: The reference dataset loader configuration.
:type reference: :mod:`dict`
:param targets: The target dataset loader configurations.
:type targets: :mod:`dict` or list of mod:`dict`
:raises KeyError: If an invalid argument is passed to a data source
loader function.
'''
# Reference dataset config
self.set_reference(**reference)
# Target dataset(s) config
self.set_targets(targets)
# Default loaders
self._source_loaders = {
'local': local.load_file,
'local_split': local.load_dataset_from_multiple_netcdf_files,
'local_multiple': local.load_multiple_files,
'esgf': esgf.load_dataset,
'rcmed': rcmed.parameter_dataset,
'dap': dap.load
}
def add_source_loader(self, source_name, loader_func):
'''
Add a custom source loader.
:param source_name: The name of the data source.
:type source_name: :mod:`string`
:param loader_func: Reference to a custom defined function. This should
return an OCW Dataset object.
:type loader_func: :class:`callable`
'''
self._source_loaders[source_name] = loader_func
def add_target(self, **kwargs):
'''
A convenient means of adding a target dataset to the loader.
:raises KeyError: If data_source is not specified.
'''
if 'data_source' not in kwargs:
raise KeyError('Dataset configuration must contain a data_source.')
self._target_config.append(kwargs)
def add_targets(self, targets):
'''
A convenient means of adding multiple target datasets to the loader.
:param targets: List of loader configurations for each target
:type targets: List of :mod:`dict`
:raises KeyError: If data_source is not specified.
'''
for target_config in targets:
self.add_target(**target_config)
def set_targets(self, targets):
'''
Reset the target dataset config.
:param targets: List of loader configurations for each target
:type targets: List of :mod:`dict`
:raises KeyError: If data_source is not specified.
'''
# This check allows for the user to enter targets as one block or
# as a list of separate blocks in their config files
if not isinstance(targets, list):
targets = [targets]
self._target_config = []
self.add_targets(targets)
def set_reference(self, **kwargs):
'''
Reset the reference dataset config.
:raises KeyError: If data_source is not specified.
'''
if 'data_source' not in kwargs:
raise KeyError('Dataset configuration must contain a data_source.')
self._reference_config = kwargs
def load_datasets(self):
'''
Loads the datasets from the given loader configurations.
'''
# Load the reference dataset
self.reference_dataset = self._load(**self._reference_config)
# Ensure output is clear if loading is performed more than once to
# prevent duplicates.
self.target_datasets = []
# Load the target datasets
for loader_params in self._target_config:
output = self._load(**loader_params)
# Need to account for the fact that some loaders return lists
# of OCW Dataset objects instead of just one
if isinstance(output, list):
self.target_datasets.extend(output)
else:
self.target_datasets.append(output)
def _load(self, **kwargs):
'''
Generic dataset loading method.
'''
# Extract the data source
data_source = kwargs.pop('data_source')
# Find the correct loader function for the given data source
loader_func = self._source_loaders[data_source]
# The remaining kwargs should be specific to the loader
output = loader_func(**kwargs)
# Preserve data_source info for later use
kwargs['data_source'] = data_source
return output