def get_url_internal_wfs( self, ogc_server: main.OGCServer, errors: Set[str] ) -> Tuple[Optional[Url], Optional[Url], Optional[Url]]: # required to do every time to validate the url. if ogc_server.auth != main.OGCSERVER_AUTH_NOAUTH: url: Optional[Url] = Url( self.request.route_url("mapserverproxy", _query={"ogcserver": ogc_server.name}) ) url_wfs: Optional[Url] = url url_internal_wfs = get_url2( f"The OGC server (WFS) '{ogc_server.name}'", ogc_server.url_wfs or ogc_server.url, self.request, errors=errors, ) else: url = get_url2(f"The OGC server '{ogc_server.name}'", ogc_server.url, self.request, errors=errors) url_wfs = ( get_url2( f"The OGC server (WFS) '{ogc_server.name}'", ogc_server.url_wfs, self.request, errors=errors, ) if ogc_server.url_wfs is not None else url ) url_internal_wfs = url_wfs return url_internal_wfs, url, url_wfs
def url_wfs_description(self, request: pyramid.request.Request) -> Optional[str]: if not self.url_wfs: return self.url_description(request) errors: Set[str] = set() url = get_url2(self.name, self.url_wfs, request, errors) return url.url() if url else "\n".join(errors)
def _get_wms_url(self, errors: Set[str]) -> Optional[Url]: ogc_server = self.ogc_server url = get_url2(f"The OGC server '{ogc_server.name}'", ogc_server.url, self.request, errors) if errors: LOG.error("\n".join(errors)) return url
def _themes(self, interface: str = "desktop", filter_themes: bool = True, min_levels: int = 1) -> Tuple[List[Dict[str, Any]], Set[str]]: """ This function returns theme information for the role identified by ``role_id``. """ self._load_tree_items() errors = set() layers = self._layers(interface) themes = models.DBSession.query(main.Theme) themes = themes.filter(main.Theme.public.is_(True)) auth_themes = models.DBSession.query(main.Theme) auth_themes = auth_themes.filter(main.Theme.public.is_(False)) auth_themes = auth_themes.join(main.Theme.restricted_roles) auth_themes = auth_themes.filter( main.Role.id.in_(get_roles_id(self.request))) themes = themes.union(auth_themes) themes = themes.order_by(main.Theme.ordering.asc()) if filter_themes and interface is not None: themes = themes.join(main.Theme.interfaces) themes = themes.filter(main.Interface.name == interface) export_themes = [] for theme in themes.all(): if re.search("[/?#]", theme.name): errors.add("The theme has an unsupported name '{}'.".format( theme.name)) continue children, children_errors = self._get_children( theme, layers, min_levels) errors |= children_errors # Test if the theme is visible for the current user if children: icon = (get_url2("The Theme '{}'".format(theme.name), theme.icon, self.request, errors) if theme.icon is not None and theme.icon else self.request.static_url( "/etc/geomapfish/static/images/blank.png")) theme_theme = { "id": theme.id, "name": theme.name, "icon": icon, "children": children, "functionalities": self._get_functionalities(theme), "metadata": self._get_metadatas(theme, errors), } export_themes.append(theme_theme) return export_themes, errors
def _get_wfs_url(self, errors: Set[str]) -> Optional[Url]: ogc_server = self.ogc_server url = get_url2( "The OGC server (WFS) '{}'".format(ogc_server.name), ogc_server.url_wfs or ogc_server.url, self.request, errors, ) if errors: LOG.error("\n".join(errors)) return url
def _fill_wmts(self, layer_theme: Dict[str, Any], layer: main.Layer, errors: Set[str]) -> None: url = get_url2(f"The WMTS layer '{layer.name}'", layer.url, self.request, errors=errors) layer_theme["url"] = url.url() if url is not None else None if layer.style: layer_theme["style"] = layer.style if layer.matrix_set: layer_theme["matrixSet"] = layer.matrix_set layer_theme["layer"] = layer.layer layer_theme["imageType"] = layer.image_type
def get_ogc_server_wfs_url_ids(request: pyramid.request.Request) -> Dict[str, List[int]]: """Get the OGCServer ids mapped on the WFS URL.""" from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel errors: Set[str] = set() servers: Dict[str, List[int]] = {} for ogc_server in DBSession.query(OGCServer).all(): url = get_url2(ogc_server.name, ogc_server.url_wfs or ogc_server.url, request, errors) if url is not None: servers.setdefault(url.url(), []).append(ogc_server.id) return servers
def _fill_wmts(self, layer_theme: Dict[str, Any], layer: main.Layer, errors: Set[str]) -> None: layer_theme["url"] = get_url2("The WMTS layer '{}'".format(layer.name), layer.url, self.request, errors=errors) if layer.style: layer_theme["style"] = layer.style if layer.matrix_set: layer_theme["matrixSet"] = layer.matrix_set layer_theme["layer"] = layer.layer layer_theme["imageType"] = layer.image_type
def wms_capabilities(self) -> bytes: errors: Set[str] = set() url = get_url2( "The OGC server '{}'".format(self._ogc_server.name), self._ogc_server.url, self._request, errors, ) if url is None: raise Exception("\n".join(errors)) # Add functionality params # sparams = get_mapserver_substitution_params(self.request) # url.add_query(url, sparams) url.add_query( { "SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities", "ROLE_ID": "0", "USER_ID": "0", }, ) self._logger.info("Get WMS GetCapabilities from: %s", url) headers = {} # Add headers for Geoserver if self._ogc_server.auth == main.OGCSERVER_AUTH_GEOSERVER: headers["sec-username"] = "******" headers["sec-roles"] = "root" response = requests.get(url.url(), headers=headers, timeout=300) self._logger.info("Got response %s in %.1fs.", response.status_code, response.elapsed.total_seconds()) response.raise_for_status() # With WMS 1.3 it returns text/xml also in case of error :-( if response.headers.get("Content-Type", "").split(";")[0].strip() not in [ "application/vnd.ogc.wms_xml", "text/xml", ]: raise Exception( "GetCapabilities from URL {} returns a wrong Content-Type: {}\n{}".format( url, response.headers.get("Content-Type", ""), response.text ) ) return response.content
def test_get_url2_dict(self): request = DummyRequest() request.registry.settings = { "package": "my_project", "servers": { "srv": { "url": "https://example.com/test_params", "params": { "MAP": "éàè" } } }, } request.scheme = "https" self.assertEqual( get_url2("test", "config://srv/icon.png?SALT=456", request, set()).url(), "https://example.com/test_params/icon.png?MAP=%C3%A9%C3%A0%C3%A8&SALT=456", )
def __init__(self, server_iface: qgis.server.QgsServerInterface): super().__init__(server_iface) self.server_iface = server_iface self.initialized = False try: config.init( os.environ.get("GEOMAPFISH_CONFIG", "/etc/qgisserver/geomapfish.yaml")) c2cwsgiutils.broadcast.init() DBSession = create_session_factory( # noqa: ignore=N806 config.get("sqlalchemy_slave.url"), config.get_config().get("sqlalchemy", {})) if "GEOMAPFISH_OGCSERVER" in os.environ: self.single = True self.ogcserver_accesscontrol = OGCServerAccessControl( server_iface, os.environ["GEOMAPFISH_OGCSERVER"], os.environ["QGIS_PROJECT_FILE"], config.get("srid"), DBSession, ) LOG.info("Use OGC server named '%s'.", os.environ["GEOMAPFISH_OGCSERVER"]) self.initialized = True elif "GEOMAPFISH_ACCESSCONTROL_CONFIG" in os.environ: self.single = False self.ogcserver_accesscontrols = {} with open(os.environ["GEOMAPFISH_ACCESSCONTROL_CONFIG"], encoding="utf-8") as ac_config_file: ac_config = yaml.safe_load(ac_config_file.read()) for map_, map_config in ac_config.get("map_config").items(): map_config["access_control"] = OGCServerAccessControl( server_iface, map_config["ogc_server"], map_, config.get("srid"), DBSession) self.ogcserver_accesscontrols[map_] = map_config LOG.info("Use config '%s'.", os.environ["GEOMAPFISH_ACCESSCONTROL_CONFIG"]) self.initialized = True elif "GEOMAPFISH_ACCESSCONTROL_BASE_URL" in os.environ: self.ogcserver_accesscontrols = {} single_ogc_server = None base_url = Url(os.environ["GEOMAPFISH_ACCESSCONTROL_BASE_URL"]) session = DBSession() try: from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel OGCServer, ) for ogcserver in session.query(OGCServer).all(): errors: Set[str] = set() url = get_url2( f"The OGC server '{ogcserver.name}'", ogcserver.url, None, errors, config.get_config().get("servers", {}), ) if errors: LOG.warning( "Ignoring OGC server '%s', get error on parsing URL:\n%s", ogcserver.name, "\n".join(errors), ) continue if url is None: LOG.warning( "Ignoring OGC server '%s', the URL is None", ogcserver.name) continue if (base_url.scheme == url.scheme and base_url.netloc == url.netloc and base_url.path == url.path): query = url.query_lower if "map" not in query: if single_ogc_server is None: single_ogc_server = ogcserver LOG.debug( "OGC server '%s', 'map' is not in the parameters => single server?", ogcserver.name, ) else: LOG.error( "OGC server '%s', 'map' is not in the parameters and we already have a single OCG server '%s'", ogcserver.name, single_ogc_server.name, ) continue map_ = url.query_lower["map"] self.ogcserver_accesscontrols[map_] = { "ogcserver": ogcserver.name, "access_control": OGCServerAccessControl( server_iface, ogcserver.name, map_, config.get("srid"), DBSession, ogcserver=ogcserver, ), } LOG.info("OGC server '%s' registered for map", ogcserver.name) else: LOG.debug( "Ignoring OGC server '%s', Don't match the base URL '%s' and '%s'", ogcserver.name, base_url, url, ) if self.ogcserver_accesscontrols and single_ogc_server is not None: if os.environ.get("QGIS_PROJECT_FILE"): LOG.error( "We have OGC servers with and without parameter MAP and a value in QGIS_PROJECT_FILE, fallback to single OGC server mode." ) self.ogcserver_accesscontrols = {} else: LOG.error( "We have OGC servers with and without parameter MAP but no value in QGIS_PROJECT_FILE, fallback to multiple OGC server mode." ) single_ogc_server = None if single_ogc_server is not None: self.single = True self.ogcserver_accesscontrol = OGCServerAccessControl( server_iface, single_ogc_server.name, os.environ["QGIS_PROJECT_FILE"], config.get("srid"), DBSession, single_ogc_server, ) LOG.info("Use OGC server named '%s'.", single_ogc_server.name) else: self.single = False self.initialized = True finally: session.close() else: LOG.error( "The environment variable 'GEOMAPFISH_OGCSERVER', 'GEOMAPFISH_ACCESSCONTROL_CONFIG' " "or 'GEOMAPFISH_ACCESSCONTROL_BASE_URL' should be defined.", ) except Exception: # pylint: disable=broad-except LOG.error("Cannot setup GeoMapFishAccessControl", exc_info=True) server_iface.registerAccessControl( self, int(os.environ.get("GEOMAPFISH_POSITION", 100)))
def _vectortiles_layers(self, layer_theme: Dict[str, Any], layer: main.Layer, errors: Set[str]) -> None: style = get_url2(f"The VectorTiles layer '{layer.name}'", layer.style, self.request, errors=errors) layer_theme["style"] = style.url() if style is not None else None if layer.xyz: layer_theme["xyz"] = layer.xyz
async def _wms_getcap_cached( self, ogc_server: main.OGCServer ) -> Tuple[Optional[Url], Optional[bytes], Set[str]]: errors: Set[str] = set() url = get_url2(f"The OGC server '{ogc_server.name}'", ogc_server.url, self.request, errors) if errors or url is None: return url, None, errors # Add functionality params if ( ogc_server.auth == main.OGCSERVER_AUTH_STANDARD and ogc_server.type == main.OGCSERVER_TYPE_MAPSERVER ): url.add_query(get_mapserver_substitution_params(self.request)) url.add_query( { "SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities", "ROLE_IDS": "0", "USER_ID": "0", }, ) LOG.debug("Get WMS GetCapabilities for URL: %s", url) # Forward request to target (without Host Header) headers = dict(self.request.headers) # Add headers for Geoserver if ogc_server.auth == main.OGCSERVER_AUTH_GEOSERVER: headers["sec-username"] = "******" headers["sec-roles"] = "root" if url.hostname != "localhost" and "Host" in headers: headers.pop("Host") headers = restrict_headers(headers, self.headers_whitelist, self.headers_blacklist) try: content, content_type = await asyncio.get_event_loop().run_in_executor( None, get_http_cached, self.http_options, url, headers ) except Exception: error = f"Unable to GetCapabilities from URL {url}" errors.add(error) LOG.error(error, exc_info=True) return url, None, errors # With wms 1.3 it returns text/xml also in case of error :-( if content_type.split(";")[0].strip() not in [ "application/vnd.ogc.wms_xml", "text/xml", ]: error = ( f"GetCapabilities from URL '{url}' returns a wrong Content-Type: {content_type}\n" f"{content.decode()}" ) errors.add(error) LOG.error(error) return url, None, errors return url, content, errors
def _layer_attributes(self, url: str, layer: str) -> Tuple[List[str], List[str]]: errors: Set[str] = set() request = _Request() request.registry.settings = self.config # Static schema will not be supported url_obj_ = get_url2("Layer", url, request, errors) if errors: print("\n".join(errors)) return [], [] if not url_obj_: print("No URL for: {}".format(url)) return [], [] url_obj: Url = url_obj_ url_obj, headers, kwargs = self._build_url(url_obj) if url not in self.wmscap_cache: print("Get WMS GetCapabilities for URL: {}".format(url_obj)) self.wmscap_cache[url] = None wms_getcap_url = (url_obj.clone().add_query({ "SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities", "ROLE_IDS": "0", "USER_ID": "0", }).url()) try: print("Get WMS GetCapabilities for URL {},\nwith headers: {}". format( wms_getcap_url, " ".join([ "{}={}".format( h, v if h not in ("Authorization", "Cookies") else "***") for h, v in headers.items() ]), )) response = requests.get(wms_getcap_url, headers=headers, **kwargs) try: self.wmscap_cache[url] = WebMapService( None, xml=response.content) except Exception as e: print( colorize( "ERROR! an error occurred while trying to " "parse the GetCapabilities document.", Color.RED, )) print(colorize(str(e), Color.RED)) print("URL: {}\nxml:\n{}".format(wms_getcap_url, response.text)) if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE": raise except Exception as e: print(colorize(str(e), Color.RED)) print( colorize( "ERROR! Unable to GetCapabilities from URL: {},\nwith headers: {}" .format( wms_getcap_url, " ".join([ "{}={}".format( h, v if h not in ("Authorization", "Cookies") else "***") for h, v in headers.items() ]), ), Color.RED, )) if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE": raise wmscap = self.wmscap_cache[url] if url not in self.featuretype_cache: print("Get WFS DescribeFeatureType for URL: {}".format(url_obj)) self.featuretype_cache[url] = None wfs_descrfeat_url = (url_obj.clone().add_query({ "SERVICE": "WFS", "VERSION": "1.1.0", "REQUEST": "DescribeFeatureType", "ROLE_IDS": "0", "USER_ID": "0", }).url()) try: response = requests.get(wfs_descrfeat_url, headers=headers, **kwargs) except Exception as e: print(colorize(str(e), Color.RED)) print( colorize( "ERROR! Unable to DescribeFeatureType from URL: {}". format(wfs_descrfeat_url), Color.RED, )) if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE": return [], [] raise if not response.ok: print( colorize( "ERROR! DescribeFeatureType from URL {} return the error: {:d} {}" .format(wfs_descrfeat_url, response.status_code, response.reason), Color.RED, )) if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE": return [], [] raise Exception("Aborted") try: describe = parseString(response.text) featurestype: Optional[Dict[str, Node]] = {} self.featuretype_cache[url] = featurestype for type_element in describe.getElementsByTagNameNS( "http://www.w3.org/2001/XMLSchema", "complexType"): cast(Dict[str, Node], featurestype)[ type_element.getAttribute("name")] = type_element except ExpatError as e: print( colorize( "ERROR! an error occurred while trying to " "parse the DescribeFeatureType document.", Color.RED, )) print(colorize(str(e), Color.RED)) print("URL: {}\nxml:\n{}".format(wfs_descrfeat_url, response.text)) if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE": return [], [] raise except AttributeError: print( colorize( "ERROR! an error occurred while trying to " "read the Mapfile and recover the themes.", Color.RED, )) print("URL: {}\nxml:\n{}".format(wfs_descrfeat_url, response.text)) if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE": return [], [] raise else: featurestype = self.featuretype_cache[url] if featurestype is None: return [], [] layers: List[str] = [layer] if wmscap is not None and layer in list(wmscap.contents): layer_obj = wmscap[layer] if layer_obj.layers: layers = [layer.name for layer in layer_obj.layers] attributes: List[str] = [] for sub_layer in layers: # Should probably be adapted for other king of servers type_element = featurestype.get("{}Type".format(sub_layer)) if type_element is not None: for element in type_element.getElementsByTagNameNS( "http://www.w3.org/2001/XMLSchema", "element"): if not element.getAttribute("type").startswith("gml:"): attributes.append(element.getAttribute("name")) return attributes, layers
def get_typed( name: str, value: str, types: Dict[str, Any], request: pyramid.request.Request, errors: Set[str], layer_name: str = None, ) -> Union[str, int, float, bool, None, List[Any], Dict[str, Any]]: prefix = "Layer '{}': ".format( layer_name) if layer_name is not None else "" type_ = {"type": "not init"} try: if name not in types: errors.add("{}Type '{}' not defined.".format(prefix, name)) return None type_ = types[name] if type_.get("type", "string") == "string": return value if type_["type"] == "list": return [v.strip() for v in value.split(",")] if type_["type"] == "boolean": value = value.lower() if value in ["yes", "y", "on", "1", "true"]: return True if value in ["no", "n", "off", "0", "false"]: return False errors.add("{}The boolean attribute '{}'='{}' is not in " "[yes, y, on, 1, true, no, n, off, 0, false].".format( prefix, name, value.lower())) elif type_["type"] == "integer": return int(value) elif type_["type"] == "float": return float(value) elif type_["type"] == "date": date = dateutil.parser.parse(value, default=datetime.datetime( 1, 1, 1, 0, 0, 0)) # type: ignore if date.time() != datetime.time(0, 0, 0): errors.add( "{}The date attribute '{}'='{}' should not have any time". format(prefix, name, value)) else: return datetime.date.strftime(date.date(), "%Y-%m-%d") elif type_["type"] == "time": date = dateutil.parser.parse(value, default=datetime.datetime( 1, 1, 1, 0, 0, 0)) # type: ignore if date.date() != datetime.date(1, 1, 1): errors.add( "{}The time attribute '{}'='{}' should not have any date". format(prefix, name, value)) else: return datetime.time.strftime(date.time(), "%H:%M:%S") elif type_["type"] == "datetime": date = dateutil.parser.parse(value, default=datetime.datetime( 1, 1, 1, 0, 0, 0)) # type: ignore return datetime.datetime.strftime(date, "%Y-%m-%dT%H:%M:%S") elif type_["type"] == "url": url = get_url2("{}The attribute '{}'".format(prefix, name), value, request, errors) return url.url() if url else "" elif type_["type"] == "json": try: return cast(Dict[str, Any], json.loads(value)) except Exception as e: errors.add("{}The attribute '{}'='{}' has an error: {}".format( prefix, name, value, str(e))) else: errors.add("{}Unknown type '{}'.".format(prefix, type_["type"])) except Exception as e: errors.add( "{}Unable to parse the attribute '{}'='{}' with the type '{}', error:\n{}" .format(prefix, name, value, type_.get("type", "string"), str(e))) return None
async def _wms_getcap_cached( self, ogc_server: main.OGCServer ) -> Tuple[Optional[Url], Optional[bytes], Set[str]]: errors: Set[str] = set() url = get_url2("The OGC server '{}'".format(ogc_server.name), ogc_server.url, self.request, errors) if errors or url is None: return url, None, errors # Add functionality params sparams = get_mapserver_substitution_params(self.request) url.add_query(sparams) url.add_query( { "SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities", "ROLE_ID": "0", "USER_ID": "0", }, ) LOG.debug("Get WMS GetCapabilities for url: %s", url) # Forward request to target (without Host Header) headers = dict(self.request.headers) # Add headers for Geoserver if ogc_server.auth == main.OGCSERVER_AUTH_GEOSERVER: headers["sec-username"] = "******" headers["sec-roles"] = "root" if url.hostname != "localhost" and "Host" in headers: headers.pop("Host") try: response = await asyncio.get_event_loop().run_in_executor( None, get_http_cached, self.http_options, url, headers) except Exception: error = "Unable to GetCapabilities from URL {}".format(url) errors.add(error) LOG.error(error, exc_info=True) return url, None, errors if not response.ok: error = "GetCapabilities from URL {} return the error: {:d} {}".format( url, response.status_code, response.reason) errors.add(error) LOG.error(error) return url, None, errors # With wms 1.3 it returns text/xml also in case of error :-( if response.headers.get("Content-Type", "").split(";")[0].strip() not in [ "application/vnd.ogc.wms_xml", "text/xml", ]: error = "GetCapabilities from URL {} returns a wrong Content-Type: {}\n{}".format( url, response.headers.get("Content-Type", ""), response.text) errors.add(error) LOG.error(error) return url, None, errors return url, response.content, errors
def test_get_url2(self): request = DummyRequest() request.registry.settings = { "package": "my_project", "servers": { "srv": "https://example.com/test", "srv_alt": "https://example.com/test/", "full_url": "https://example.com/test.xml", "srv_no_path": "https://example.com", }, } request.scheme = "https" def static_url(path, **kwargs): del kwargs # Unused return "http://server.org/" + path request.static_url = static_url self.assertEqual( get_url2("test", "static://pr:st/icon.png", request, set()).url(), "http://server.org/pr:st/icon.png", ) self.assertEqual( get_url2("test", "static:///icon.png", request, set()).url(), "http://server.org//etc/geomapfish/static/icon.png", ) self.assertEqual( get_url2("test", "config://srv/icon.png", request, set()).url(), "https://example.com/test/icon.png", ) self.assertEqual( get_url2("test", "config://srv/", request, set()).url(), "https://example.com/test/") self.assertEqual( get_url2("test", "config://srv", request, set()).url(), "https://example.com/test") self.assertEqual( get_url2("test", "config://srv/icon.png?test=aaa", request, set()).url(), "https://example.com/test/icon.png?test=aaa", ) self.assertEqual( get_url2("test", "config://srv_alt/icon.png", request, set()).url(), "https://example.com/test/icon.png", ) self.assertEqual( get_url2("test", "config://full_url", request, set()).url(), "https://example.com/test.xml") self.assertEqual( get_url2("test", "http://example.com/icon.png", request, set()).url(), "http://example.com/icon.png", ) self.assertEqual( get_url2("test", "https://example.com/icon.png", request, set()).url(), "https://example.com/icon.png", ) errors: Set[str] = set() self.assertEqual( get_url2("test", "config://srv2/icon.png", request, errors=errors), None) self.assertEqual( errors, { "test: The server 'srv2' (config://srv2/icon.png) is not found in the config: [srv, srv_alt, full_url, srv_no_path]", }, ) self.assertEqual( get_url2("test", "config://srv_no_path/icon.png", request, errors=errors).url(), "https://example.com/icon.png", ) self.assertEqual( get_url2("test", "config://srv_no_path", request, errors=errors).url(), "https://example.com")
def get_typed( name: str, value: str, types: Dict[str, Any], request: pyramid.request.Request, errors: Set[str], layer_name: Optional[str] = None, ) -> Union[str, int, float, bool, None, List[Any], Dict[str, Any]]: """Get the typed (parsed) value of a metadata or a functionality.""" prefix = f"Layer '{layer_name}': " if layer_name is not None else "" type_ = {"type": "not init"} try: if name not in types: errors.add(f"{prefix}Type '{name}' not defined.") return None type_ = types[name] if type_.get("type", "string") == "string": return value if type_["type"] == "list": return [v.strip() for v in value.split(",")] if type_["type"] == "boolean": value = value.lower() if value in ["yes", "y", "on", "1", "true"]: return True if value in ["no", "n", "off", "0", "false"]: return False errors.add( f"{prefix}The boolean attribute '{name}'='{value.lower()}' is not in " "[yes, y, on, 1, true, no, n, off, 0, false]." ) elif type_["type"] == "integer": return int(value) elif type_["type"] == "float": return float(value) elif type_["type"] == "date": date = dateutil.parser.parse(value, default=datetime.datetime(1, 1, 1, 0, 0, 0)) # type: ignore if date.time() != datetime.time(0, 0, 0): errors.add(f"{prefix}The date attribute '{name}'='{value}' should not have any time") else: return datetime.date.strftime(date.date(), "%Y-%m-%d") elif type_["type"] == "time": date = dateutil.parser.parse(value, default=datetime.datetime(1, 1, 1, 0, 0, 0)) # type: ignore if date.date() != datetime.date(1, 1, 1): errors.add(f"{prefix}The time attribute '{name}'='{value}' should not have any date") else: return datetime.time.strftime(date.time(), "%H:%M:%S") elif type_["type"] == "datetime": date = dateutil.parser.parse(value, default=datetime.datetime(1, 1, 1, 0, 0, 0)) # type: ignore return datetime.datetime.strftime(date, "%Y-%m-%dT%H:%M:%S") elif type_["type"] == "url": url = get_url2(f"{prefix}The attribute '{name}'", value, request, errors) return url.url() if url else "" elif type_["type"] == "json": try: return cast(Dict[str, Any], json.loads(value)) except Exception as e: errors.add(f"{prefix}The attribute '{name}'='{value}' has an error: {str(e)}") elif type_["type"] == "regex": pattern = type_["regex"] if re.match(pattern, value) is None: errors.add( f"{prefix}The regex attribute '{name}'='{value}' " f"does not match expected pattern '{pattern}'." ) else: return value else: errors.add(f"{prefix}Unknown type '{type_['type']}'.") except Exception as e: errors.add( f"{prefix}Unable to parse the attribute '{name}'='{value}' with the type " f"'{type_.get('type', 'string')}', error:\n{e!s}" ) return None
def url_description(self, request: pyramid.request.Request) -> str: errors: Set[str] = set() url = get_url2(self.name, self.url, request, errors) return url.url() if url else "\n".join(errors)