Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
cc wis2box/wmo
  • Loading branch information
webb-ben committed Jan 17, 2025
1 parent 3026033 commit 780aaaa
Show file tree
Hide file tree
Showing 21 changed files with 2,517 additions and 0 deletions.
41 changes: 41 additions & 0 deletions pygeobox/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
###############################################################################
#
# 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.
#
###############################################################################

__version__ = '0.6.dev1'

import click

from pygeobox.api import api
from pygeobox.env import environment
from pygeobox.auth import auth


@click.group()
@click.version_option(version=__version__)
def cli():
"""pygeobox"""
pass


cli.add_command(api)
cli.add_command(environment)
cli.add_command(auth)

183 changes: 183 additions & 0 deletions pygeobox/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
###############################################################################
#
# 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.
#
###############################################################################

import click
import logging

from pygeobox.api.backend import load_backend
from pygeobox.api.config import load_config
from pygeobox import cli_helpers

LOGGER = logging.getLogger(__name__)


def setup_collection(meta: dict = {}) -> bool:
"""
Add collection to api backend and configuration
:param meta: `dict` of collection metadata
:returns: `bool` of API collection setup result
"""

try:
name = meta['id'].lower()
except KeyError:
LOGGER.error(f'Invalid configuration: {meta}')
return False

if 'topic_hierarchy' in meta:
data_name = meta['topic_hierarchy']
else:
data_name = meta['id']

backend = load_backend()
if not backend.has_collection(data_name):

if not backend.add_collection(data_name):
msg = f'Unable to setup backend for collection {data_name}'
LOGGER.error(msg)
return False

api_config = load_config()
if not api_config.has_collection(name):

collection = api_config.prepare_collection(meta)
if not api_config.add_collection(name, collection):
msg = f'Unable to setup configuration for collection {name}'
LOGGER.error(msg)
return False

return True


def remove_collection(name: str) -> bool:
"""
Add collection to api backend and mcf or collection configuration
:param name: `str` of collection name
:returns: `bool` of API collection removal result
"""

backend = load_backend()
if backend.has_collection(name):
backend.delete_collection(name)

api_config = load_config()
if api_config.has_collection(name):
api_config.delete_collection(name)

if backend.has_collection(name) or api_config.has_collection(name):
LOGGER.error(f'Unable to remove collection for {name}')
return False
else:
return True


def upsert_collection_item(collection_id: str, item: dict,
method: str = 'POST'):
"""
Add or update a collection item
:param collection_id: name of collection
:param item: `dict` of GeoJSON item data
:returns: `str` identifier of added item
"""
backend = load_backend()
if backend.upsert_collection_items(collection_id, [item], method):
return True


def delete_collection_item(collection_id: str, item_id: str) -> str:
"""
Delete an item from a collection
:param collection_id: name of collection
:param item_id: `str` of item identifier
:returns: `str` identifier of added item
"""
backend = load_backend()
backend.delete_collection_item(collection_id, item_id)


def delete_collections_by_retention(days: int) -> None:
"""
Delete collections by retention date
:param days: `int` of number of days
:returns: `None`
"""
backend = load_backend()
backend.delete_collections_by_retention(days)


@click.group()
def api():
"""API management"""
pass


@click.command()
@click.pass_context
@cli_helpers.OPTION_VERBOSITY
def setup(ctx, verbosity):
"""Add collection items to API backend"""

api_config = load_config()
if not api_config.has_collection(''):
click.echo('API not ready')
else:
click.echo('API ready')


@click.command()
@click.pass_context
@cli_helpers.ARGUMENT_FILEPATH
@cli_helpers.OPTION_VERBOSITY
def add_collection(ctx, filepath, verbosity):
"""Delete collection from api backend"""

if not setup_collection(meta=filepath.read()):
click.echo('Unable to add collection')
else:
click.echo('Collection added')


@click.command()
@click.pass_context
@click.argument('collection')
@cli_helpers.OPTION_VERBOSITY
def delete_collection(ctx, collection, verbosity):
"""Delete collection from api backend"""

if not remove_collection(collection):
click.echo('Unable to delete collection')
else:
click.echo('Collection deleted')


api.add_command(setup)
api.add_command(add_collection)
api.add_command(delete_collection)
46 changes: 46 additions & 0 deletions pygeobox/api/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
###############################################################################
#
# 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.
#
###############################################################################

import logging
from typing import Any

from pygeobox.env import API_BACKEND_TYPE, API_BACKEND_URL
from pygeobox.plugin import load_plugin, PLUGINS

LOGGER = logging.getLogger(__name__)


def load_backend() -> Any:
"""
Load pygeobox API backend
:returns: plugin object
"""

LOGGER.debug('Loading backend')

codepath = PLUGINS['api_backend'][API_BACKEND_TYPE]['plugin']
defs = {
'codepath': codepath,
'url': API_BACKEND_URL
}

return load_plugin('api_backend', defs)
111 changes: 111 additions & 0 deletions pygeobox/api/backend/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
###############################################################################
#
# 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.
#
###############################################################################

import logging

LOGGER = logging.getLogger(__name__)

class BaseBackend:
"""Abstract API backend"""
def __init__(self, defs: dict) -> None:
"""
initializer
:param defs: `dict` of connection parameters
(url, host, port, username, password)
"""

self.url = defs.get('url')
self.host = defs.get('host')
self.port = defs.get('port')
self.username = defs.get('username')
self.password = defs.get('password')

def add_collection(self, name: str) -> bool:
"""
Add a collection
:param name: name of collection
:returns: `bool` of result
"""

raise NotImplementedError()

def delete_collection(self, name: str) -> bool:
"""
Delete a collection
:param name: name of collection
:returns: `bool` of delete result
"""

raise NotImplementedError()

def has_collection(self, name: str) -> bool:
"""
Checks a collection
:param name: name of collection
:returns: `bool` of collection result
"""

raise NotImplementedError()

def upsert_collection_item(self, collection: str, item: dict) -> str:
"""
Add or update a collection item
:param collection: name of collection
:param item: `dict` of GeoJSON item data
:returns: `str` identifier of added item
"""

raise NotImplementedError()

def delete_collection_item(self, collection: str, item_id: str) -> str:
"""
Delete an item from a collection
:param collection: name of collection
:param item_id: `str` of item identifier
:returns: `bool` of delete result
"""

raise NotImplementedError()

def delete_collections_by_retention(self, days: int) -> None:
"""
Delete collections by retention date
:param days: `int` of number of days
:returns: `None`
"""

raise NotImplementedError()

def __repr__(self):
return f'<BaseBackend> (url={self.url})'
Loading

0 comments on commit 780aaaa

Please sign in to comment.