Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the code structure #150

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions geospaas_harvesting/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""This module provides means to gather metadata about various datasets
into the GeoSPaaS catalog
"""

import importlib
import logging.config
import os
import os.path
import pkgutil
import sys
import yaml

Expand All @@ -20,3 +23,11 @@
if logging_configuration:
logging.config.dictConfig(logging_configuration)
logging.captureWarnings(True)

# import plugins
discovered_plugins = {
name: importlib.import_module(name)
for finder, name, ispkg
in pkgutil.iter_modules()
if name.startswith('geospaas_harvesting_')
}
76 changes: 41 additions & 35 deletions geospaas_harvesting/config.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
"""Configuration management"""
import importlib
import logging
import pkgutil

import geospaas_harvesting.providers.aviso as providers_aviso
import geospaas_harvesting.providers.base as providers_base
import geospaas_harvesting.providers.ceda as providers_ceda
import geospaas_harvesting.providers.cmems as providers_cmems
import geospaas_harvesting.providers.copernicus_scihub as providers_copernicus_scihub
import geospaas_harvesting.providers.earthdata_cmr as providers_earthdata_cmr
import geospaas_harvesting.providers.erddap as providers_erddap
import geospaas_harvesting.providers.ftp as providers_ftp
import geospaas_harvesting.providers.http as providers_http
import geospaas_harvesting.providers.jaxa as providers_jaxa
import geospaas_harvesting.providers.local as providers_local
import geospaas_harvesting.providers.metno as providers_metno
import geospaas_harvesting.providers.noaa as providers_noaa
import geospaas_harvesting.providers.podaac as providers_podaac
import geospaas_harvesting.providers.resto as providers_resto
import geospaas_harvesting
from .arguments import ArgumentParser, BooleanArgument, DictArgument, ListArgument
from .providers.base import Provider
from .utils import read_yaml_file


def import_provider_modules():
"""Import provider classes from core modules and plugins"""
imported = []
for base_module in [geospaas_harvesting, *geospaas_harvesting.discovered_plugins.values()]:
for _, name, ispkg in pkgutil.iter_modules(base_module.__path__):
if name == 'providers':
providers = importlib.import_module(f"{base_module.__name__}.{name}")
imported.append(providers)
if ispkg:
for _, provider_name, _ in pkgutil.iter_modules(providers.__path__):
imported.append(
importlib.import_module(f"{providers.__name__}.{provider_name}"))
return imported


import_provider_modules()
logger = logging.getLogger(__name__)


class NoProviderFoundError(Exception):
"""No provider class was found"""


class Configuration():
"""Base class for configuration objects"""

Expand Down Expand Up @@ -57,23 +66,19 @@ class ProvidersArgument(DictArgument):
'password': 'pass123'
}
"""
provider_types = {
'aviso': providers_aviso.AVISOProvider,
'ceda': providers_ceda.CEDAProvider,
'cmems': providers_cmems.CMEMSProvider,
'copernicus_scihub': providers_copernicus_scihub.CopernicusScihubProvider,
'earthdata_cmr': providers_earthdata_cmr.EarthDataCMRProvider,
'ftp': providers_ftp.FTPProvider,
'gportal_ftp': providers_jaxa.GPortalProvider,
'http': providers_http.HTTPProvider,
'metno': providers_metno.METNOProvider,
'nansat': providers_local.NansatProvider,
'netcdf': providers_local.NetCDFProvider,
'noaa': providers_noaa.NOAAProvider,
'podaac': providers_podaac.PODAACProvider,
'resto': providers_resto.RestoProvider,
'tabledap': providers_erddap.ERDDAPTableProvider,
}
provider_classes = Provider.__subclasses__()

def __init__(self, name, **kwargs):
super().__init__(name, **kwargs)

def _find_provider(self, provider_type):
"""Try to find a provider matching the `provider_type` in the
Provider subclasses
"""
for provider_class in self.provider_classes:
if provider_class.type == provider_type:
return provider_class
raise NoProviderFoundError(f"No provider found of type {provider_type}")

def parse(self, value):
"""Go through the list of provider settings and create the
Expand All @@ -84,15 +89,16 @@ def parse(self, value):
for provider_name, provider_settings in providers_dict.items():
try:
_providers[provider_name] = (
self.provider_types[provider_settings['type']](
self._find_provider(provider_settings['type'])(
name=provider_name,
**provider_settings,
))
except KeyError as error:
logger.error('Missing setting for provider: %s', error.args[0])
except NoProviderFoundError as error:
logger.error(error.args[0])
return _providers


class ProvidersConfiguration(Configuration):
"""Configuration manager for providers"""

Expand All @@ -110,7 +116,7 @@ class SearchConfiguration(Configuration):

def __init__(self):
self.providers = None
common_argument_parser = providers_base.Provider().search_parameters_parser
common_argument_parser = Provider().search_parameters_parser
self.config_arguments_parser = ArgumentParser([
DictArgument(
'common', argument_parser=common_argument_parser),
Expand Down
3 changes: 3 additions & 0 deletions geospaas_harvesting/providers/aviso.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

class AVISOProvider(TimeFilterMixin, Provider):
"""Provider for AVISO's Thredds"""

type = 'aviso'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = 'https://tds.aviso.altimetry.fr/thredds'
Expand Down
2 changes: 2 additions & 0 deletions geospaas_harvesting/providers/ceda.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class CEDAProvider(TimeFilterMixin, Provider):
"""Provider for CEDA FTP server"""

type = 'ceda'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = "ftp://anon-ftp.ceda.ac.uk"
Expand Down
2 changes: 2 additions & 0 deletions geospaas_harvesting/providers/cmems.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
class CMEMSProvider(Provider):
"""Provider for CMEMS using the copernicusmarine package"""

type = 'cmems'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.search_parameters_parser.add_arguments([
Expand Down
3 changes: 3 additions & 0 deletions geospaas_harvesting/providers/copernicus_scihub.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

class CopernicusScihubProvider(Provider):
"""Provider for the Copernicus Scihub APIs"""

type = 'copernicus_scihub'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.search_url = 'https://apihub.copernicus.eu/apihub/search'
Expand Down
3 changes: 3 additions & 0 deletions geospaas_harvesting/providers/earthdata_cmr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class EarthDataCMRProvider(Provider):
properly validated because of the massive amount of collections
available through this API. This needs to be refined.
"""

type = 'earthdata_cmr'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.search_url = 'https://cmr.earthdata.nasa.gov/search/granules.umm_json'
Expand Down
3 changes: 3 additions & 0 deletions geospaas_harvesting/providers/erddap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

class ERDDAPTableProvider(Provider):
"""Provider for tabledap APIs"""

type = 'tabledap'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = kwargs['url'].rstrip('/')
Expand Down
2 changes: 2 additions & 0 deletions geospaas_harvesting/providers/ftp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class FTPProvider(TimeFilterMixin, Provider):
"""Generic FTP provider"""

type = 'ftp'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.search_parameters_parser.add_arguments([
Expand Down
2 changes: 2 additions & 0 deletions geospaas_harvesting/providers/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class HTTPProvider(TimeFilterMixin, Provider):
"""Generic HTTP directory provider"""

type = 'http'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.search_parameters_parser.add_arguments([
Expand Down
2 changes: 2 additions & 0 deletions geospaas_harvesting/providers/jaxa.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class GPortalProvider(TimeFilterMixin, Provider):
"""Provider for JAXA GPortal FTP server"""

type = 'gportal_ftp'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = "ftp://ftp.gportal.jaxa.jp"
Expand Down
4 changes: 4 additions & 0 deletions geospaas_harvesting/providers/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class NansatProvider(TimeFilterMixin, Provider):
"""Provider for local files with metadata provided by Nansat
"""

type = 'nansat'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.search_parameters_parser.add_arguments([
Expand All @@ -45,6 +47,8 @@ class NetCDFProvider(TimeFilterMixin, Provider):
"""Provider for local files with metadata extracted directly using
"""

type = 'netcdf'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.longitude_attribute = kwargs.get('longitude_attribute', 'LONGITUDE')
Expand Down
3 changes: 3 additions & 0 deletions geospaas_harvesting/providers/metno.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

class METNOProvider(TimeFilterMixin, Provider):
"""Provider for MET NO's Thredds"""

type = 'metno'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = 'https://thredds.met.no/thredds'
Expand Down
2 changes: 2 additions & 0 deletions geospaas_harvesting/providers/noaa.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class NOAAProvider(TimeFilterMixin, Provider):
"""Provider for NOAA FTP servers"""

type = 'noaa'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = "ftp://{server}.ncep.noaa.gov"
Expand Down
3 changes: 3 additions & 0 deletions geospaas_harvesting/providers/podaac.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

class PODAACProvider(TimeFilterMixin, Provider):
"""Provider for PODAAC's OpenDAP"""

type = 'podaac'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = 'https://opendap.jpl.nasa.gov/opendap'
Expand Down
2 changes: 2 additions & 0 deletions geospaas_harvesting/providers/resto.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class RestoProvider(Provider):
parameters are fetched from the API.
"""

type = 'resto'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = kwargs['url'].rstrip('/')
Expand Down
14 changes: 9 additions & 5 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
import logging
import unittest
import unittest.mock as mock
from datetime import datetime, timezone as tz
from pathlib import Path

import geospaas_harvesting.config as config
import geospaas_harvesting.providers.base as providers_base
import geospaas_harvesting.providers.podaac as providers_podaac
import geospaas_harvesting.providers.cmems as providers_cmems
import geospaas_harvesting.providers.resto as providers_resto
Expand Down Expand Up @@ -58,10 +55,17 @@ def test_parse(self):
name='cmems', username='user', password='pass'),
})

def test_parse_error(self):
def test_parse_config_error(self):
"""Test error handling when parsing wrong configuration"""
with self.assertLogs(config.logger, level=logging.ERROR):
_ = config.ProvidersArgument('providers').parse({'foo': {}})
config.ProvidersArgument('providers').parse({'foo': {}})

def test_parse_no_provider_found(self):
"""Test error handling when no provider matches the requested
type
"""
with self.assertLogs(config.logger, level=logging.ERROR):
config.ProvidersArgument('providers').parse({'foo': {'type': 'foo'}})


class ProvidersConfigurationTestCase(unittest.TestCase):
Expand Down