Source code for eodal.metadata.utils

"""
Helper functions to interact with the satellite meta data base

Copyright (C) 2022 Lukas Valentin Graf

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from __future__ import annotations

import os
import subprocess
import pandas as pd

from pathlib import Path
from typing import Optional
from typing import Union

from eodal.utils.exceptions import DataNotFoundError


def _check_linux_cifs(ip: Union[str, Path]) -> Path:
    """
    Searches for mount point of an external file system on a Linux
    operating system

    :ip:
        IP or network address of the NAS device for which
        to search the mount point.
    """

    res = Path("")

    exe = "cat /proc/mounts | grep cifs"
    response = subprocess.getoutput(exe)
    lines = response.split("\n")
    for line in lines:
        if str(ip) in line:
            # data is on mounted share -> get local file system mapper
            local_path = line.split(" ")[1]
            # check if current user has access to read local path, otherwise keep
            # searching (might happen if another user manually mounts the NAS)
            if os.access(local_path, os.R_OK):
                res = Path(local_path)
                break

    return res


[docs] def reconstruct_path( record: pd.Series, is_raw_data: Optional[bool] = True, path_to_nas: Optional[bool] = True, ) -> Path: """ auxiliary function to reconstruct the actual dataset location based on the entries in the metatdata base. Raises an error if the dataset was not found. :param record: single record from the metadata base denoting a single dataset :param is_raw_data: if True (default) assumes the queried data is Sentinel-2 ESA derived "raw" data in .SAFE archive format and not already processed by eodal to multi-band geoTiff files. :param path_to_nas: if True (default) tries to find the mount point of the NAS file system on the local machine's file system. :return in_dir: filepath to the directory for the local machine """ ip = Path(record.storage_device_ip) # check if ip points towards a network drive under Linux if path_to_nas: if os.name == "posix": # search for mount point of NAS file system mount_point = _check_linux_cifs(ip=ip) # if this attempt failed, we can check the alias if available if str(mount_point) == "." and record.storage_device_ip_alias != "": mount_point = _check_linux_cifs(ip=record.storage_device_ip_alias) # if no mount point is found raise an error if str(mount_point) == "": raise DataNotFoundError( "Could not find mount point for external file system" ) share = mount_point.joinpath(record.storage_share) # Windows does not know about mount points, it should be able to work # with network paths elif os.name == "nt": share = Path(record.storage_device_ip).joinpath(record.storage_share) # if share is not available test alias if available if not share.exists(): if record.storage_device_ip_alias == "": raise DataNotFoundError( "Could not find network path for external file system" ) share = Path(record.storage_device_ip.alias).joinpath( record.storage_share ) # path is to local filesystem or does not require mount points else: share = Path(record.storage_share) # the path should work 'as it is' on Windows machines by replacing the slashes if os.name == "nt": # TODO test on Windows tmp = str(share) tmp = tmp.replace(r"//", r"\\").replace(r"/", os.sep) share = Path(tmp) if not share.exists(): raise DataNotFoundError(f"Could not find {share}") if is_raw_data: in_dir = share.joinpath(record.product_uri) else: in_dir = share # handle products not ending with '.SAFE' (e.g., when data comes from Mundi) if not in_dir.exists(): in_dir = Path(str(in_dir).replace(".SAFE", "")) if not in_dir.exists(): raise NotADirectoryError(f"Could not find {str(in_dir)}") return in_dir