-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add shared stac-populator CLI for all populator impl + add directory …
…crawler populator
- Loading branch information
1 parent
d22214a
commit cf75217
Showing
7 changed files
with
256 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import argparse | ||
import glob | ||
import importlib | ||
import os | ||
import sys | ||
from typing import Callable, Optional | ||
|
||
from STACpopulator import __version__ | ||
|
||
POPULATORS = {} | ||
|
||
|
||
def make_main_parser() -> argparse.ArgumentParser: | ||
parser = argparse.ArgumentParser(prog="stac-populator", description="STACpopulator operations.") | ||
parser.add_argument("--version", "-V", action="version", version=f"%(prog)s {__version__}", | ||
help="prints the version of the library and exits") | ||
commands = parser.add_subparsers(title="command", dest="command", description="STAC populator command to execute.") | ||
|
||
run_cmd_parser = make_run_command_parser() | ||
commands.add_parser( | ||
"run", | ||
prog=f"{parser.prog} {run_cmd_parser.prog}", parents=[run_cmd_parser], | ||
formatter_class=run_cmd_parser.formatter_class, usage=run_cmd_parser.usage, | ||
add_help=False, help=run_cmd_parser.description, description=run_cmd_parser.description | ||
) | ||
|
||
# add more commands as needed... | ||
|
||
return parser | ||
|
||
|
||
def make_run_command_parser() -> argparse.ArgumentParser: | ||
""" | ||
Groups all sub-populator CLI listed in :py:mod:`STACpopulator.implementations` as a common ``stac-populator`` CLI. | ||
Dispatches the provided arguments to the appropriate sub-populator CLI as requested. Each sub-populator CLI must | ||
implement functions ``make_parser`` and ``main`` to generate the arguments and dispatch them to the corresponding | ||
caller. The ``main`` function should accept a sequence of string arguments, which can be passed to the parser | ||
obtained from ``make_parser``. | ||
An optional ``runner`` can also be defined in each populator module. If provided, the namespace arguments that have | ||
already been parsed to resolve the populator to run will be used directly, avoiding parsing arguments twice. | ||
""" | ||
parser = argparse.ArgumentParser(prog="command", description="STACpopulator implementation runner.") | ||
subparsers = parser.add_subparsers(title="populator", dest="populator", description="Implementation to run.") | ||
populators_impl = "implementations" | ||
populators_dir = os.path.join(os.path.dirname(__file__), populators_impl) | ||
populator_mods = glob.glob(f"{populators_dir}/**/[!__init__]*.py", recursive=True) # potential candidate scripts | ||
for populator_path in sorted(populator_mods): | ||
populator_script = populator_path.split(populators_dir, 1)[1][1:] | ||
populator_py_mod = os.path.splitext(populator_script)[0].replace(os.sep, ".") | ||
populator_name, pop_mod_file = populator_py_mod.rsplit(".", 1) | ||
populator_root = f"STACpopulator.{populators_impl}.{populator_name}" | ||
pop_mod_file_loc = f"{populator_root}.{pop_mod_file}" | ||
populator_module = importlib.import_module(pop_mod_file_loc, populator_root) | ||
parser_maker: Callable[[], argparse.ArgumentParser] = getattr(populator_module, "make_parser", None) | ||
populator_runner = getattr(populator_module, "runner", None) # optional, call main directly if not available | ||
populator_caller = getattr(populator_module, "main", None) | ||
if callable(parser_maker) and callable(populator_caller): | ||
populator_parser = parser_maker() | ||
populator_prog = f"{parser.prog} {populator_name}" | ||
subparsers.add_parser( | ||
populator_name, | ||
prog=populator_prog, parents=[populator_parser], formatter_class=populator_parser.formatter_class, | ||
add_help=False, # add help disabled otherwise conflicts with this main populator help | ||
help=populator_parser.description, description=populator_parser.description, | ||
usage=populator_parser.usage, | ||
) | ||
POPULATORS[populator_name] = { | ||
"name": populator_name, | ||
"caller": populator_caller, | ||
"parser": populator_parser, | ||
"runner": populator_runner, | ||
} | ||
return parser | ||
|
||
|
||
def main(*args: str) -> Optional[int]: | ||
parser = make_main_parser() | ||
args = args or sys.argv[1:] # same as was parse args does, but we must provide them to subparser | ||
ns = parser.parse_args(args=args) # if 'command' or 'populator' unknown, auto prints the help message with exit(2) | ||
params = vars(ns) | ||
populator_cmd = params.pop("command") | ||
if not populator_cmd: | ||
parser.print_help() | ||
return 0 | ||
result = None | ||
if populator_cmd == "run": | ||
populator_name = params.pop("populator") | ||
if not populator_name: | ||
parser.print_help() | ||
return 0 | ||
populator_args = args[2:] # skip [command] [populator] | ||
populator_caller = POPULATORS[populator_name]["caller"] | ||
populator_runner = POPULATORS[populator_name]["runner"] | ||
if populator_runner: | ||
result = populator_runner(ns) | ||
else: | ||
result = populator_caller(*populator_args) | ||
return 0 if result is None else result | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
62 changes: 62 additions & 0 deletions
62
STACpopulator/implementations/DirectoryLoader/crawl_directory.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import argparse | ||
from typing import NoReturn, Optional, MutableMapping, Any | ||
|
||
from STACpopulator.input import STACDirectoryLoader | ||
from STACpopulator.models import GeoJSONPolygon, STACItemProperties | ||
from STACpopulator.populator_base import STACpopulatorBase | ||
from STACpopulator.stac_utils import LOGGER | ||
|
||
|
||
class DirectoryPopulator(STACpopulatorBase): | ||
item_properties_model = STACItemProperties | ||
item_geometry_model = GeoJSONPolygon | ||
|
||
def __init__( | ||
self, | ||
stac_host: str, | ||
loader: STACDirectoryLoader, | ||
update: bool, | ||
collection: MutableMapping[str, Any], | ||
) -> None: | ||
self._collection_info = collection | ||
super().__init__(stac_host, loader, update) | ||
|
||
def load_config(self): | ||
pass # ignore | ||
|
||
def create_stac_collection(self) -> MutableMapping[str, Any]: | ||
return self._collection_info | ||
|
||
def create_stac_item(self, item_name: str, item_data: MutableMapping[str, Any]) -> MutableMapping[str, Any]: | ||
return item_data | ||
|
||
|
||
def make_parser() -> argparse.ArgumentParser: | ||
parser = argparse.ArgumentParser(description="Directory STAC populator") | ||
parser.add_argument("stac_host", type=str, help="STAC API URL.") | ||
parser.add_argument("directory", type=str, help="Path to a directory structure with STAC Collections and Items.") | ||
parser.add_argument("--update", action="store_true", help="Update collection and its items.") | ||
parser.add_argument( | ||
"--prune", action="store_true", | ||
help="Limit search of STAC Collections only to first top-most matches in the crawled directory structure." | ||
) | ||
return parser | ||
|
||
|
||
def runner(ns: argparse.Namespace) -> Optional[int] | NoReturn: | ||
LOGGER.info(f"Arguments to call: {vars(ns)}") | ||
|
||
for collection_path, collection_json in STACDirectoryLoader(ns.directory, "collection", ns.prune): | ||
loader = STACDirectoryLoader(collection_path, "item", False) | ||
populator = DirectoryPopulator(ns.stac_host, loader, ns.update, collection_json) | ||
populator.ingest() | ||
|
||
|
||
def main(*args: str) -> Optional[int]: | ||
parser = make_parser() | ||
ns = parser.parse_args(args) | ||
return runner(ns) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters