def _on_database_select(self, _):
        """Load chosen database"""
        self.structure_drop.reset()

        if (self.database[1] is None
                or getattr(self.database[1], "base_url", None) is None):
            self.query_button.tooltip = "Search - No database chosen"
            self.freeze()
        else:
            self.offset = 0
            self.number = 1
            self.structure_page_chooser.silent_reset()
            try:
                self.freeze()

                self.query_button.description = "Updating ..."
                self.query_button.icon = "cog"
                self.query_button.tooltip = "Updating filters ..."

                self._set_intslider_ranges()
                self._set_version()
            except Exception as exc:  # pylint: disable=broad-except
                LOGGER.error(
                    "Exception raised during setting IntSliderRanges: %s",
                    exc.with_traceback(),
                )
            finally:
                self.query_button.description = "Search"
                self.query_button.icon = "search"
                self.query_button.tooltip = "Search"
                self.sort_selector.valid_fields = sorted(
                    get_sortable_fields(self.database[1].base_url))
                self.unfreeze()
Exemplo n.º 2
0
 def __init__(self, *args: Tuple[Any]):
     LOGGER.error(
         "%s raised.\nError message: %s\nAbout this exception: %s",
         args[0].__class__.__name__
         if args and isinstance(args[0], Exception)
         else self.__class__.__name__,
         str(args[0]) if args else "",
         args[0].__doc__
         if args and isinstance(args[0], Exception)
         else self.__doc__,
     )
     super().__init__(*args)
Exemplo n.º 3
0
def handle_errors(response: dict) -> Tuple[str, set]:
    """Handle any errors"""
    if "data" not in response and "errors" not in response:
        raise InputError(f"No data and no errors reported in response: {response}")

    if "errors" in response:
        LOGGER.error("Errored response:\n%s", json.dumps(response, indent=2))

        if "data" in response:
            msg = (
                '<font color="red">Error(s) during querying,</font> but '
                f"<strong>{len(response['data'])}</strong> structures found."
            )
        elif isinstance(response["errors"], dict) and "detail" in response["errors"]:
            msg = (
                '<font color="red">Error(s) during querying. '
                f"Message from server:<br>{response['errors']['detail']!r}.</font>"
            )
        elif isinstance(response["errors"], list) and any(
            ["detail" in _ for _ in response["errors"]]
        ):
            details = [_["detail"] for _ in response["errors"] if "detail" in _]
            msg = (
                '<font color="red">Error(s) during querying. Message(s) from server:<br> - '
                f"{'<br> - '.join(details)!r}</font>"
            )
        else:
            msg = (
                '<font color="red">Error during querying, '
                "please try again later.</font>"
            )

        http_errors = set()
        for raw_error in response.get("errors", []):
            try:
                error = OptimadeError(**raw_error)
                status = int(error.status)
            except (ValidationError, TypeError, ValueError):
                status = 400
            http_errors.add(status)

        return msg, http_errors

    return "", set()
Exemplo n.º 4
0
def get_entry_endpoint_schema(base_url: str, endpoint: str = None) -> dict:
    """Retrieve provider's entry endpoint schema (default: /structures)."""
    result = {}

    endpoint = endpoint if endpoint is not None else "structures"
    endpoint = f"/info/{endpoint.strip('/')}"

    response = perform_optimade_query(endpoint=endpoint, base_url=base_url)
    msg, _ = handle_errors(response)
    if msg:
        LOGGER.error(
            "Could not retrieve information about entry-endpoint %r.\n  Message: %r\n  Response:"
            "\n%s",
            endpoint[len("/info/") :],
            msg,
            response,
        )
        return result

    return response.get("data", {}).get("properties", {})
Exemplo n.º 5
0
def fetch_providers(providers_urls: Union[str, List[str]] = None) -> list:
    """Fetch OPTIMADE database providers (from Materials-Consortia)

    :param providers_urls: String or list of strings with versioned base URL(s)
        to Materials-Consortia providers database
    """
    if providers_urls and not isinstance(providers_urls, (list, str)):
        raise TypeError("providers_urls must be a string or list of strings")

    if not providers_urls:
        providers_urls = PROVIDERS_URLS
    elif not isinstance(providers_urls, list):
        providers_urls = [providers_urls]

    for providers_url in providers_urls:
        providers = perform_optimade_query(base_url=providers_url, endpoint="")
        msg, _ = handle_errors(providers)
        if msg:
            LOGGER.warning("%r returned error(s).", providers_url)
        else:
            break
    else:
        if CACHED_PROVIDERS.exists():
            # Load local cached providers file
            LOGGER.warning(
                "Loading local, possibly outdated, list of providers (%r).",
                CACHED_PROVIDERS.name,
            )
            with open(CACHED_PROVIDERS, "r") as handle:
                providers = json.load(handle)
        else:
            LOGGER.error(
                "Neither any of the provider URLs: %r returned a valid response, "
                "and the local cached file of the latest valid response does not exist.",
                providers_urls,
            )
            providers = {}

    update_local_providers_json(providers)
    return providers.get("data", [])
Exemplo n.º 6
0
def check_entry_properties(
    base_url: str,
    entry_endpoint: str,
    properties: Union[str, Iterable[str]],
    checks: Union[str, Iterable[str]],
) -> List[str]:
    """Check an entry-endpoint's properties

    :param checks: An iterable, which only recognizes the following str entries:
    "sort", "sortable", "present", "queryable"
    The first two and latter two represent the same thing, i.e., whether a property is sortable
    and whether a property is present in the entry-endpoint's resource's attributes, respectively.
    :param properties: Can be either a list or not of properties to check.
    :param entry_endpoint: A valid entry-endpoint for the OPTIMADE implementation,
    e.g., "structures", "_exmpl_calculations", or "/extensions/structures".
    """
    if isinstance(properties, str):
        properties = [properties]
    properties = list(properties)

    if not checks:
        # Don't make any queries if called improperly (with empty iterable for `checks`)
        return properties

    if isinstance(checks, str):
        checks = [checks]
    checks = set(checks)
    if "queryable" in checks:
        checks.update({"present"})
        checks.remove("queryable")
    if "sortable" in checks:
        checks.update({"sort"})
        checks.remove("sortable")

    query_params = {
        "endpoint": f"/info/{entry_endpoint.strip('/')}",
        "base_url": base_url,
    }

    response = perform_optimade_query(**query_params)
    msg, _ = handle_errors(response)
    if msg:
        LOGGER.error(
            "Could not retrieve information about entry-endpoint %r.\n  Message: %r\n  Response:"
            "\n%s",
            entry_endpoint,
            msg,
            response,
        )
        if "present" in checks:
            return []
        return properties

    res = list(properties)  # Copy of input list of properties

    found_properties = response.get("data", {}).get("properties", {})
    for field in properties:
        field_property = found_properties.get(field, None)
        if field_property is None:
            LOGGER.debug(
                "Could not find %r in %r for provider with base URL %r. Found properties:\n%s",
                field,
                query_params["endpoint"],
                base_url,
                json.dumps(found_properties),
            )
            if "present" in checks:
                res.remove(field)
        elif "sort" in checks:
            sortable = field_property.get("sortable", False)
            if not sortable:
                res.remove(field)

    LOGGER.debug(
        "sortable fields found for %s (looking for %r): %r", base_url, properties, res
    )
    return res