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()
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)
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()
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", {})
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", [])
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