Source code for ee_extra.JavaScript.install

"""JavaScript install module provide functions to install/uninstall
a JavaScript Earth Enginemodule.
"""

import pathlib
import re
import urllib.request

import pkg_resources


def _convert_path_to_ee_sources(path: str) -> str:
    """Get the remote module path from the 'ee-sources' GCS bucket.

    Args:
        path: str

    Returns:
        An ee-sources module url.
    """
    if path.startswith("http"):
        eempath = path
    else:
        bpath = path.replace(":", "/")
        eempath = f"https://storage.googleapis.com/ee-sources/{bpath}"
    return eempath


def _get_ee_sources_path() -> str:
    """Gets the ee-sources folder path.

    Returns:
        The ee-sources folder path.
    """
    ee_extra_py_file = pkg_resources.resource_filename("ee_extra", "ee_extra.py")
    pkgdir = pathlib.Path(ee_extra_py_file).parent
    return pkgdir.joinpath("ee-sources").as_posix()


def _convert_path_to_ee_extra(path: str) -> str:
    """Convert an Earth Engine module path into an ee_extra module path.

    Args:
        path: str

    Returns:
        An ee_extra modules path.
    """
    if path.startswith("http"):
        ee_extra_path = pathlib.Path(_get_ee_sources_path()).joinpath(
            "EXTERNAL_" + pathlib.Path(path).stem + "/" + pathlib.Path(path).name
        )
    else:
        if path.endswith(".js"):
            ee_extra_path = pathlib.Path(_get_ee_sources_path()).joinpath(
                path.replace(":", "/")
            )
        else:
            ee_extra_path = pathlib.Path(_get_ee_sources_path()).joinpath(
                path.replace(":", "/") + ".js"
            )
    return ee_extra_path


def _check_if_module_exists(path: str) -> bool:
    """Check if an Earth Engine module has been installed in ee_extra.

    Args:
        path: str

    Returns:
        Whether the module has been installed.
    """
    return pathlib.Path(_convert_path_to_ee_extra(path)).exists()


def _open_module_as_str(path: str) -> str:
    """Open a module as a string.

    Args:
        path: str

    Returns:
        Specified module as a string.
    """
    if _check_if_module_exists(path):
        with open(_convert_path_to_ee_extra(path), "r") as file:
            module = file.read()

        return module
    else:
        raise Exception(f"The module '{path}' is not installed!")


def _get_dependencies(path: str) -> list:
    """Get the dependencies of an Earth Engine module.

    Args:
        path: str

    Returns:
        List of dependencies.
    """
    if _check_if_module_exists(path):

        module = _open_module_as_str(path)

        dependencies = re.findall(r"require\((.*?)\)", module)
        dependenciesClean = []

        for dep in dependencies:
            dependenciesClean.append(dep.replace('"', "").replace("'", ""))

        return dependenciesClean

    else:

        raise Exception(f"The module '{path}' is not installed!")


def _install(x: str, update: bool, quiet: bool = False):
    """Install an Earth Engine JavaScript module.

    The specified module will be installed in the ee_extra module path.

    Args:
        x: str
        update: bool
    """
    if _check_if_module_exists(x) and not update:
        if not quiet:
            print(f"The module '{x}' is already installed!")

    else:
        if not quiet:
            print(f"Downloading '{x}'...")

        ee_sources = _get_ee_sources_path()

        # Local path
        if x.startswith("http"):
            module_folder = pathlib.Path(ee_sources).joinpath(
                "EXTERNAL/" + pathlib.Path(x).stem
            )
        else:
            module_folder = pathlib.Path(ee_sources).joinpath(
                "/".join(x.replace(":", "/").split("/")[:-1])
            )

        if not module_folder.exists():
            module_folder.mkdir(parents=True, exist_ok=True)

        # With requests:
        # r = requests.get(_convert_path_to_ee_sources(x))
        # open(_convert_path_to_ee_extra(x), "wb").write(r.content)

        # With urllib:
        with urllib.request.urlopen(_convert_path_to_ee_sources(x)) as url:
            r = url.read()  # .decode()
        open(_convert_path_to_ee_extra(x), "wb").write(r)

        if not quiet:
            print(f"The module '{x}' was successfully installed!")


[docs]def install(x: str, update: bool = False, quiet: bool = False) -> list: """Install an Earth Engine modue and its dependencies. The specified dependencies will be installed in the ee_extra module path. Args: x: str update: bool Examples: >>> import ee >>> from ee_extra import Extra >>> ee.Initialize() >>> Extra.JavaScript.eejs2py.install("users/dmlmont/spectral:spectral") """ deps = [x] def _install_dependencies(x: list, update: bool, installed: list): if len(x) > 0: for dep in x: if dep not in installed: _install(dep, update, quiet) if not quiet: print(f"Checking dependencies for {dep}...") x.extend(_get_dependencies(dep)) installed.append(dep) x = [item for item in x if item not in installed] _install_dependencies(x, update, installed) else: quiet or print(f"All dependencies were successfully installed!") return _install_dependencies(x=deps, update=update, installed=[])
def rmtree(path): """Iterative delete of files using pathlib""" for p in path.iterdir(): if p.is_dir(): rmtree(p) else: p.unlink() path.rmdir()
[docs]def uninstall(x: str, quiet: bool = False): """Uninstall an Earth Engine JavaScript module. The specified module will be uninstalled. Dependencies won't be uninstalled. Args: x: str Examples: >>> import ee >>> from ee_extra import Extra >>> ee.Initialize() >>> Extra.JavaScript.eejs2py.uninstall("users/dmlmont/spectral:spectral") """ if _check_if_module_exists(x): rmtree(_convert_path_to_ee_extra(x).parent) quiet or print(f"The module '{x}' was successfully uninstalled!") else: quiet or print(f"The module '{x}' is not installed!")