blob: f84bdc4030e52d88323b218d7e5929208d987e0b [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.rcmed as rcmed
import ocw.data_source.podaac_datasource as podaac
import warnings
class DatasetLoader:
'''Generate a list of OCW Dataset objects from a variety of sources.'''
def __init__(self, *loader_opts):
'''Generate a list of OCW Dataset objects from a variety of sources.
Each keyword argument can be information for a dataset in dictionary
form. For example:
>>> loader_opt1 = {'loader_name': 'rcmed', 'name': 'cru',
'dataset_id': 10, 'parameter_id': 34}
>>> loader_opt2 = {'path': './data/TRMM_v7_3B43_1980-2010.nc,
'variable': 'pcp'}
>>> loader = DatasetLoader(loader_opt1, loader_opt2)
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))
>>> obs_loader_config = config['datasets']['reference']
>>> loader = DatasetLoader(*obs_loader_config)
As shown in the first example, the dictionary for each argument should
contain a loader name and parameters specific to the particular loader.
Once the configuration is entered, the datasets may be loaded using:
>>> loader.load_datasets()
>>> obs_datasets = loader.datasets
Additionally, each dataset must have a ``loader_name`` keyword. This may
be one of the following:
* ``'local'`` - One or multiple dataset files in a local directory
* ``'local_split'`` - A single dataset split accross multiple files 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
* ``'podaac'`` - Download the dataset from Physical Oceanography
Distributed Active Archive Center
Users who wish to load datasets from loaders not described above may
define their own custom dataset loader function and incorporate it as
follows:
>>> loader.add_source_loader('my_loader_name', my_loader_func)
:param loader_opts: Dictionaries containing the each dataset loader
configuration, representing the keyword arguments of
the loader function specified by an additional key
called 'loader_name'. If not specified by the user,
this defaults to local.
:type loader_opts: :class:`dict`
:raises KeyError: If an invalid argument is passed to a data source
loader function.
'''
# dataset loader config
self.set_loader_opts(*loader_opts)
# Default loaders
self._source_loaders = {
'local': local.load_multiple_files,
'local_split': local.load_dataset_from_multiple_netcdf_files,
'rcmed': rcmed.parameter_dataset,
'podaac': podaac.extract_l4_granule
}
# Exclude esgf and dap for python 3 until they are compatible
try:
import ocw.data_source.esgf as esgf
import ocw.data_source.dap as dap
self._source_loaders['dap'] = dap.load
self._source_loaders['esgf'] = esgf.load_dataset
except ImportError:
warnings.warn('dap and esgf loaders missing. If these are needed, '
'fallback to python 2.7.x.')
def add_source_loader(self, loader_name, loader_func):
'''
Add a custom source loader.
:param loader_name: The name of the data source.
:type loader_name: :mod:`string`
:param loader_func: Reference to a custom defined function. This should
return an OCW Dataset object, and have an origin which satisfies
origin['source'] == loader_name.
:type loader_func: :class:`callable`
'''
self._source_loaders[loader_name] = loader_func
def add_loader_opts(self, *loader_opts):
'''
A convenient means of adding loader options for each dataset to the
loader.
:param loader_opts: Dictionaries containing the each dataset loader
configuration, representing the keyword arguments of
the loader function specified by an additional key
called 'loader_name'. If not specified by the user,
this defaults to local.
:type loader_opts: :mod:`dict`
'''
for opt in loader_opts:
if 'loader_name' not in opt:
opt['loader_name'] = 'local'
self._config.extend(loader_opts)
def set_loader_opts(self, *loader_opts):
'''
Reset the dataset loader config.
:param loader_opts: Dictionaries containing the each dataset loader
configuration, representing the keyword arguments of
the loader function specified by an additional key
called 'loader_name'. If not specified by the user,
this defaults to local.
:type loader_opts: :mod:`dict`
'''
self._config = []
self.add_loader_opts(*loader_opts)
def load_datasets(self):
'''
Loads the datasets from the given loader configurations.
'''
# Ensure output is clear if loading is performed more than once to
# prevent duplicates.
self.datasets = []
# Load the datasets
for loader_opt in self._config:
output = self._load(**loader_opt)
# Need to account for the fact that some loaders return lists
# of OCW Dataset objects instead of just one
if isinstance(output, list):
self.datasets.extend(output)
else:
self.datasets.append(output)
def _load(self, **kwargs):
'''
Generic dataset loading method.
'''
# Extract the loader name
loader_name = kwargs.pop('loader_name')
# Find the correct loader function for the given data source
loader_func = self._source_loaders[loader_name]
# The remaining kwargs should be specific to the loader
output = loader_func(**kwargs)
# Preserve loader_name info for later use
kwargs['loader_name'] = loader_name
return output