def geoserver_post_save(instance, sender, created, **kwargs): from geonode.messaging import producer # this is attached to various models, (ResourceBase, Document) # so we should select what will be handled here if isinstance(instance, Layer): instance_dict = model_to_dict(instance) payload = json_serializer_producer(instance_dict) producer.geoserver_upload_layer(payload) if getattr(settings, 'DELAYED_SECURITY_SIGNALS', False): instance.set_dirty_state() if instance.storeType != 'remoteStore' and created: logger.info("... Creating Default Resource Links for Layer [%s]" % (instance.alternate)) try: set_resource_default_links(instance, sender, prune=True) except BaseException: from django.db import connection connection._rollback() logger.warn( "Failure Creating Default Resource Links for Layer [%s]" % (instance.alternate)) logger.info("... Creating Thumbnail for Layer [%s]" % (instance.alternate)) try: create_gs_thumbnail(instance, overwrite=True, check_bbox=True) except BaseException: logger.warn("Failure Creating Thumbnail for Layer [%s]" % (instance.alternate))
def handle(self, *args, **options): ignore_errors = options.get('ignore_errors') filter = options.get('filter') if not options.get('username'): username = None else: username = options.get('username') all_layers = Layer.objects.all().order_by('name') if filter: all_layers = all_layers.filter(name__icontains=filter) if username: all_layers = all_layers.filter(owner__username=username) for index, layer in enumerate(all_layers): print "[%s / %s] Updating Layer [%s] ..." % ( (index + 1), len(all_layers), layer.name) try: # recalculate the layer statistics set_attributes(layer, overwrite=True) # refresh metadata links set_resource_default_links(layer, layer, prune=False) # refresh catalogue metadata records catalogue_post_save(instance=layer, sender=layer.__class__) except BaseException as e: # import traceback # traceback.print_exc() if ignore_errors: print "[ERROR] Layer [%s] couldn't be updated" % ( layer.name) else: raise e
def handle(self, *args, **options): ignore_errors = options.get('ignore_errors') filter = options.get('filter') if not options.get('username'): username = None else: username = options.get('username') all_layers = Layer.objects.all().order_by('name') if filter: all_layers = all_layers.filter(name__icontains=filter) if username: all_layers = all_layers.filter(owner__username=username) for index, layer in enumerate(all_layers): print "[%s / %s] Updating Layer [%s] ..." % ((index + 1), len(all_layers), layer.name) try: # recalculate the layer statistics set_attributes(layer, overwrite=True) # refresh metadata links set_resource_default_links(layer, layer, prune=True) # refresh catalogue metadata records catalogue_post_save(instance=layer, sender=layer.__class__) except BaseException as e: # import traceback # traceback.print_exc() if ignore_errors: print "[ERROR] Layer [%s] couldn't be updated" % (layer.name) else: raise e
def handle(self, *args, **options): ignore_errors = options.get('ignore_errors') remove_duplicates = options.get('remove_duplicates') filter = options.get('filter') if not options.get('username'): username = None else: username = options.get('username') all_layers = Layer.objects.all().order_by('name') if filter: all_layers = all_layers.filter(name__icontains=filter) if username: all_layers = all_layers.filter(owner__username=username) for index, layer in enumerate(all_layers): print "[%s / %s] Updating Layer [%s] ..." % ( (index + 1), len(all_layers), layer.name) try: # recalculate the layer statistics set_attributes(layer, overwrite=True) # refresh metadata links set_resource_default_links(layer, layer, prune=False) # refresh catalogue metadata records catalogue_post_save(instance=layer, sender=layer.__class__) if remove_duplicates: # remove duplicates for _n in _names: _links = Link.objects.filter(resource__id=layer.id, name=_n) while _links.count() > 1: _links.last().delete() print '.', # fixup Legend links legend_url_template = ogc_server_settings.PUBLIC_LOCATION + \ 'ows?service=WMS&request=GetLegendGraphic&format=image/png&WIDTH=20&HEIGHT=20&LAYER=' + \ '{alternate}&STYLE={style_name}' + \ '&legend_options=fontAntiAliasing:true;fontSize:12;forceLabels:on' if layer.default_style and not layer.get_legend_url( style_name=layer.default_style.name): Link.objects.update_or_create( resource=layer.resourcebase_ptr, name='Legend', extension='png', url=legend_url_template.format( alternate=layer.alternate, style_name=layer.default_style.name), mime='image/png', link_type='image') except BaseException as e: import traceback traceback.print_exc() if ignore_errors: print "[ERROR] Layer [%s] couldn't be updated" % ( layer.name) else: raise e
def handle(self, *args, **options): ignore_errors = options.get('ignore_errors') remove_duplicates = options.get('remove_duplicates') prune = options.get('prune') set_uuid = ast.literal_eval(options.get('set_uuid', 'False')) set_attrib = ast.literal_eval(options.get('set_attrib', 'True')) set_links = ast.literal_eval(options.get('set_links', 'True')) delete_orphaned_thumbnails = options.get('delete_orphaned_thumbnails') filter = options.get('filter') if not options.get('username'): username = None else: username = options.get('username') all_layers = Layer.objects.all().order_by('name') if filter: all_layers = all_layers.filter(name__icontains=filter) if username: all_layers = all_layers.filter(owner__username=username) for index, layer in enumerate(all_layers): print(f"[{(index + 1)} / {len(all_layers)}] Updating Layer [{layer.name}] ...") try: # recalculate the layer statistics if set_attrib: set_attributes(layer, overwrite=True) if set_uuid and hasattr(settings, 'LAYER_UUID_HANDLER') and settings.LAYER_UUID_HANDLER != '': from geonode.layers.utils import get_uuid_handler uuid = get_uuid_handler()(layer).create_uuid() la = Layer.objects.filter(resourcebase_ptr=layer.resourcebase_ptr) la.update(uuid=uuid) layer.refresh_from_db() # refresh metadata links if set_links: set_resource_default_links(layer, layer, prune=prune) # refresh catalogue metadata records catalogue_post_save(instance=layer, sender=layer.__class__) # remove duplicates if remove_duplicates: remove_duplicate_links(layer) except Exception as e: import traceback traceback.print_exc() if ignore_errors: logger.error(f"[ERROR] Layer [{layer.name}] couldn't be updated") else: raise e # delete orphaned thumbs if delete_orphaned_thumbnails: delete_orphaned_thumbs()
def handle(self, *args, **options): ignore_errors = options.get('ignore_errors') remove_duplicates = options.get('remove_duplicates') prune = options.get('prune') delete_orphaned_thumbnails = options.get('delete_orphaned_thumbnails') filter = options.get('filter') if not options.get('username'): username = None else: username = options.get('username') all_layers = Layer.objects.all().order_by('name') if filter: all_layers = all_layers.filter(name__icontains=filter) if username: all_layers = all_layers.filter(owner__username=username) for index, layer in enumerate(all_layers): print( f"[{(index + 1)} / {len(all_layers)}] Updating Layer [{layer.name}] ..." ) try: # recalculate the layer statistics set_attributes(layer, overwrite=True) # refresh metadata links set_resource_default_links(layer, layer, prune=prune) # refresh catalogue metadata records catalogue_post_save(instance=layer, sender=layer.__class__) # remove duplicates if remove_duplicates: remove_duplicate_links(layer) except Exception as e: import traceback traceback.print_exc() if ignore_errors: logger.error( f"[ERROR] Layer [{layer.name}] couldn't be updated") else: raise e # delete orphaned thumbs if delete_orphaned_thumbnails: delete_orphaned_thumbs()
def set_resource_links(*args, **kwargs): from geonode.utils import set_resource_default_links from geonode.catalogue.models import catalogue_post_save from geonode.layers.models import Layer if settings.UPDATE_RESOURCE_LINKS_AT_MIGRATE: _all_layers = Layer.objects.all() for index, layer in enumerate(_all_layers, start=1): _lyr_name = layer.name message = f"[{index} / {len(_all_layers)}] Updating Layer [{_lyr_name}] ..." logger.debug(message) try: set_resource_default_links(layer, layer) catalogue_post_save(instance=layer, sender=layer.__class__) except Exception: logger.exception( f"[ERROR] Layer [{_lyr_name}] couldn't be updated")
def set_resource_links(*args, **kwargs): from geonode.utils import set_resource_default_links from geonode.catalogue.models import catalogue_post_save from geonode.layers.models import Layer _all_layers = Layer.objects.all() for index, layer in enumerate(_all_layers, start=1): _lyr_name = layer.name message = "[%s / %s] Updating Layer [%s] ..." % ( index, len(_all_layers), _lyr_name) print(message) logger.debug(message) try: set_resource_default_links(layer, layer) catalogue_post_save(instance=layer, sender=layer.__class__) except BaseException: logger.exception("[ERROR] Layer [%s] couldn't be updated" % _lyr_name)
def geoserver_post_save(instance, sender, created, **kwargs): from geonode.messaging import producer # this is attached to various models, (ResourceBase, Document) # so we should select what will be handled here if isinstance(instance, Layer): instance_dict = model_to_dict(instance) payload = json_serializer_producer(instance_dict) producer.geoserver_upload_layer(payload) if getattr(settings, 'DELAYED_SECURITY_SIGNALS', False): instance.set_dirty_state() if instance.storeType != 'remoteStore' and created: logger.info("... Creating Default Resource Linkks for Layer [%s]" % (instance.alternate)) set_resource_default_links(instance, sender, prune=True) logger.info("... Creating Thumbnail for Layer [%s]" % (instance.alternate)) try: create_gs_thumbnail(instance, overwrite=True, check_bbox=True) except BaseException: logger.warn("!WARNING! - Failure while Creating Thumbnail for Layer [%s]" % (instance.alternate))
def geoserver_post_save_local(instance, *args, **kwargs): """Send information to geoserver. The attributes sent include: * Title * Abstract * Name * Keywords * Metadata Links, * Point of Contact name and url """ instance.refresh_from_db() # Don't run this signal if is a Layer from a remote service if getattr(instance, "remote_service", None) is not None: return # Don't run this signal handler if it is a tile layer or a remote store (Service) # Currently only gpkg files containing tiles will have this type & will be served via MapProxy. if hasattr(instance, 'storeType') and getattr(instance, 'storeType') in ['tileStore', 'remoteStore']: return instance gs_resource = None values = None _tries = 0 _max_tries = getattr(ogc_server_settings, "MAX_RETRIES", 2) # If the store in None then it's a new instance from an upload, # only in this case run the geoserver_upload method if not instance.store or getattr(instance, 'overwrite', False): base_file, info = instance.get_base_file() # There is no need to process it if there is not file. if base_file is None: return gs_name, workspace, values, gs_resource = geoserver_upload(instance, base_file.file.path, instance.owner, instance.name, overwrite=True, title=instance.title, abstract=instance.abstract, # keywords=instance.keywords, charset=instance.charset) def fetch_gs_resource(values, tries): try: gs_resource = gs_catalog.get_resource( name=instance.name, store=instance.store, workspace=instance.workspace) except Exception: try: gs_resource = gs_catalog.get_resource( name=instance.alternate, store=instance.store, workspace=instance.workspace) except Exception: try: gs_resource = gs_catalog.get_resource( name=instance.alternate or instance.typename) except Exception: gs_resource = None if gs_resource: gs_resource.title = instance.title or "" gs_resource.abstract = instance.abstract or "" gs_resource.name = instance.name or "" if not values: values = dict(store=gs_resource.store.name, storeType=gs_resource.store.resource_type, alternate=gs_resource.store.workspace.name + ':' + gs_resource.name, title=gs_resource.title or gs_resource.store.name, abstract=gs_resource.abstract or '', owner=instance.owner) else: msg = "There isn't a geoserver resource for this layer: %s" % instance.name logger.exception(msg) if tries >= _max_tries: # raise GeoNodeException(msg) return (values, None) gs_resource = None sleep(3.00) return (values, gs_resource) while not gs_resource and _tries < _max_tries: values, gs_resource = fetch_gs_resource(values, _tries) _tries += 1 # Get metadata links metadata_links = [] for link in instance.link_set.metadata(): metadata_links.append((link.mime, link.name, link.url)) if gs_resource: logger.debug("Found geoserver resource for this layer: %s" % instance.name) gs_resource.metadata_links = metadata_links # gs_resource should only be called if # ogc_server_settings.BACKEND_WRITE_ENABLED == True if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): try: gs_catalog.save(gs_resource) except geoserver.catalog.FailedRequestError as e: msg = ('Error while trying to save resource named %s in GeoServer, ' 'try to use: "%s"' % (gs_resource, str(e))) e.args = (msg,) logger.exception(e) # Update Attribution link if instance.poc: # gsconfig now utilizes an attribution dictionary gs_resource.attribution = {'title': str(instance.poc), 'width': None, 'height': None, 'href': None, 'url': None, 'type': None} profile = get_user_model().objects.get(username=instance.poc.username) site_url = settings.SITEURL.rstrip('/') if settings.SITEURL.startswith('http') else settings.SITEURL gs_resource.attribution_link = site_url + profile.get_absolute_url() # gs_resource should only be called if # ogc_server_settings.BACKEND_WRITE_ENABLED == True if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): try: gs_catalog.save(gs_resource) except geoserver.catalog.FailedRequestError as e: msg = ('Error while trying to save layer named %s in GeoServer, ' 'try to use: "%s"' % (gs_resource, str(e))) e.args = (msg,) logger.exception(e) else: msg = "There isn't a geoserver resource for this layer: %s" % instance.name logger.warn(msg) if isinstance(instance, ResourceBase): if hasattr(instance, 'layer'): instance = instance.layer else: return # Save layer attributes set_attributes_from_geoserver(instance) # Save layer styles set_styles(instance, gs_catalog) # Invalidate GeoWebCache for the updated resource try: _stylefilterparams_geowebcache_layer(instance.alternate) _invalidate_geowebcache_layer(instance.alternate) except Exception: pass if instance.storeType == "remoteStore": return if gs_resource: """Get information from geoserver. The attributes retrieved include: * Bounding Box * SRID * Download links (WMS, WCS or WFS and KML) * Styles (SLD) """ try: instance.abstract = gs_resource.abstract or '' except Exception as e: logger.exception(e) instance.abstract = '' instance.workspace = gs_resource.store.workspace.name instance.store = gs_resource.store.name try: bbox = gs_resource.native_bbox # Set bounding box values instance.bbox_x0 = bbox[0] instance.bbox_x1 = bbox[1] instance.bbox_y0 = bbox[2] instance.bbox_y1 = bbox[3] instance.srid = bbox[4] except Exception as e: logger.exception(e) if instance.srid: instance.srid_url = "http://www.spatialreference.org/ref/" + \ instance.srid.replace(':', '/').lower() + "/" elif instance.bbox_x0 and instance.bbox_x1 and instance.bbox_y0 and instance.bbox_y1: # Guessing 'EPSG:4326' by default instance.srid = 'EPSG:4326' else: raise GeoNodeException("Invalid Projection. Layer is missing CRS!") # Iterate over values from geoserver. if gs_resource: for key in ['alternate', 'store', 'storeType']: # attr_name = key if 'typename' not in key else 'alternate' # print attr_name setattr(instance, key, values[key]) if gs_resource: try: if settings.RESOURCE_PUBLISHING: if instance.is_published != gs_resource.advertised: if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): gs_resource.advertised = 'true' gs_catalog.save(gs_resource) if not settings.FREETEXT_KEYWORDS_READONLY: # AF: Warning - this won't allow people to have empty keywords on GeoNode if len(instance.keyword_list()) == 0 and gs_resource.keywords: for keyword in gs_resource.keywords: if keyword not in instance.keyword_list(): instance.keywords.add(keyword) if any(instance.keyword_list()): keywords = instance.keyword_list() gs_resource.keywords = [kw for kw in list(set(keywords))] # gs_resource should only be called if # ogc_server_settings.BACKEND_WRITE_ENABLED == True if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): gs_catalog.save(gs_resource) except Exception as e: msg = ('Error while trying to save resource named %s in GeoServer, ' 'try to use: "%s"' % (gs_resource, str(e))) e.args = (msg,) logger.exception(e) to_update = { 'title': instance.title or instance.name, 'abstract': instance.abstract or "", 'alternate': instance.alternate, 'bbox_x0': instance.bbox_x0, 'bbox_x1': instance.bbox_x1, 'bbox_y0': instance.bbox_y0, 'bbox_y1': instance.bbox_y1, 'srid': instance.srid } # Update ResourceBase resources = ResourceBase.objects.filter(id=instance.resourcebase_ptr.id) resources.update(**to_update) # to_update['name'] = instance.name, to_update['workspace'] = instance.workspace to_update['store'] = instance.store to_update['storeType'] = instance.storeType to_update['typename'] = instance.alternate # Save all the modified information in the instance without triggering signals. Layer.objects.filter(id=instance.id).update(**to_update) # Refresh from DB instance.refresh_from_db() # Updating the Catalogue catalogue_post_save(instance=instance, sender=instance.__class__) # store the resource to avoid another geoserver call in the post_save if gs_resource: instance.gs_resource = gs_resource # some thumbnail generators will update thumbnail_url. If so, don't # immediately re-generate the thumbnail here. use layer#save(update_fields=['thumbnail_url']) if gs_resource: logger.debug("... Creating Default Resource Links for Layer [%s]" % (instance.alternate)) set_resource_default_links(instance, instance, prune=True) if 'update_fields' in kwargs and kwargs['update_fields'] is not None and \ 'thumbnail_url' in kwargs['update_fields']: logger.debug("... Creating Thumbnail for Layer [%s]" % (instance.alternate)) create_gs_thumbnail(instance, overwrite=True) # Updating HAYSTACK Indexes if needed if settings.HAYSTACK_SEARCH: from django.core.management import call_command call_command('update_index')
def test_layer_links(self): lyr = Layer.objects.filter(storeType="dataStore").first() self.assertEquals(lyr.storeType, "dataStore") if check_ogc_backend(geoserver.BACKEND_PACKAGE): links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") _def_link_types = ('data', 'image', 'original', 'html', 'OGC:WMS', 'OGC:WFS', 'OGC:WCS') Link.objects.filter(resource=lyr.resourcebase_ptr, link_type__in=_def_link_types).delete() links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 0) set_resource_default_links(lyr, lyr) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 6) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="image") self.assertIsNotNone(links) self.assertEquals(len(links), 3) lyr = Layer.objects.filter(storeType="coverageStore").first() self.assertEquals(lyr.storeType, "coverageStore") if check_ogc_backend(geoserver.BACKEND_PACKAGE): links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") _def_link_types = ('data', 'image', 'original', 'html', 'OGC:WMS', 'OGC:WFS', 'OGC:WCS') Link.objects.filter(resource=lyr.resourcebase_ptr, link_type__in=_def_link_types).delete() links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 0) set_resource_default_links(lyr, lyr) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 2) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="image") self.assertIsNotNone(links) self.assertEquals(len(links), 7)
def qgis_server_post_save(instance, sender, **kwargs): """Save keywords to QGIS Server. The way keywords are implemented requires the layer to be saved to the database before accessing them. This hook also creates QGIS Project. Which is essentials for QGIS Server. There are also several Geonode Links generated, like thumbnail and legends :param instance: geonode Layer :type instance: Layer :param sender: geonode Layer type :type sender: type(Layer) """ if not sender == Layer: return # TODO # 1. Create or update associated QGISServerLayer [Done] # 2. Create Link for the tile and legend. logger.debug('QGIS Server Post Save') # copy layer to QGIS Layer Directory try: geonode_layer_path = instance.get_base_file()[0].file.path except AttributeError: logger.debug('Layer does not have base file') return qgis_layer, created = QGISServerLayer.objects.get_or_create(layer=instance) logger.debug('Geonode Layer Path %s' % geonode_layer_path) base_filename, original_ext = os.path.splitext(geonode_layer_path) extensions = QGISServerLayer.accepted_format is_shapefile = False for ext in extensions: if os.path.exists(base_filename + '.' + ext): is_shapefile = is_shapefile or ext == 'shp' try: if created: # Assuming different layer has different filename because # geonode rename it shutil.copy2(base_filename + '.' + ext, QGIS_layer_directory) logger.debug('Create new basefile') else: # If there is already a file, replace the old one qgis_layer_base_filename = qgis_layer.qgis_layer_path_prefix shutil.copy2(base_filename + '.' + ext, qgis_layer_base_filename + '.' + ext) logger.debug('Overwrite existing basefile') logger.debug('Copying %s' % base_filename + '.' + ext + ' Success') logger.debug('Into %s' % QGIS_layer_directory) except IOError as e: logger.debug('Copying %s' % base_filename + '.' + ext + ' FAILED ' + e) if created: # Only set when creating new QGISServerLayer Object geonode_filename = os.path.basename(geonode_layer_path) basename = os.path.splitext(geonode_filename)[0] qgis_layer.base_layer_path = os.path.join( QGIS_layer_directory, basename + original_ext # Already with dot ) qgis_layer.save() # refresh to get QGIS Layer instance.refresh_from_db() # Set layer crs try: if is_shapefile: dataset = ogr.Open(geonode_layer_path) layer = dataset.GetLayer() spatial_ref = layer.GetSpatialRef() srid = spatial_ref.GetAuthorityCode(None) if spatial_ref else None if srid: instance.srid = srid else: dataset = gdal.Open(geonode_layer_path) prj = dataset.GetProjection() srs = osr.SpatialReference(wkt=prj) srid = srs.GetAuthorityCode(None) if srs else None if srid: instance.srid = srid except Exception as e: logger.debug("Can't retrieve projection: {layer}".format( layer=geonode_layer_path)) logger.exception(e) # Refresh and create the instance default links set_resource_default_links(instance, qgis_layer, is_shapefile=is_shapefile, original_ext=original_ext) # Create thumbnail overwrite = getattr(instance, 'overwrite', False) create_qgis_server_thumbnail.delay(get_model_path(instance), instance.id, overwrite=overwrite) # Attributes set_attributes(instance) # Update xml file # Read metadata from layer that InaSAFE use. # Some are not found: organisation, email, url try: new_values = { 'date': instance.date.isoformat(), 'abstract': instance.abstract, 'title': instance.title, 'license': instance.license_verbose, } except (TypeError, AttributeError): new_values = {} pass # Get the path of the metadata file basename, _ = os.path.splitext(qgis_layer.base_layer_path) xml_file_path = basename + '.xml' if os.path.exists(xml_file_path): try: update_xml(xml_file_path, new_values) except (TypeError, AttributeError): pass # Also update xml in QGIS Server xml_file_path = qgis_layer.qgis_layer_path_prefix + '.xml' if os.path.exists(xml_file_path): try: update_xml(xml_file_path, new_values) except (TypeError, AttributeError): pass # Remove existing tile caches if overwrite if overwrite: tiles_directory = settings.QGIS_SERVER_CONFIG['tiles_directory'] basename, _ = os.path.splitext(qgis_layer.base_layer_path) basename = os.path.basename(basename) tiles_cache_path = os.path.join(tiles_directory, basename) try: shutil.rmtree(tiles_cache_path) except OSError: # The path doesn't exists yet pass
def test_base_resources(self): """ Ensure we can access the Resource Base list. """ url = reverse('base-resources-list') # Anonymous response = self.client.get(url, format='json') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 5) self.assertEqual(response.data['total'], 26) # Pagination self.assertEqual(len(response.data['resources']), 10) logger.debug(response.data) # Remove public permissions to Layers from geonode.layers.utils import set_layers_permissions set_layers_permissions( "read", # permissions_name None, # resources_names == None (all layers) [get_anonymous_user()], # users_usernames None, # groups_names True, # delete_flag ) response = self.client.get(url, format='json') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 5) self.assertEqual(response.data['total'], 26) # Pagination self.assertEqual(len(response.data['resources']), 10) # Admin self.assertTrue(self.client.login(username='******', password='******')) response = self.client.get(url, format='json') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 5) self.assertEqual(response.data['total'], 26) # response has link to the response self.assertTrue('link' in response.data['resources'][0].keys()) # Pagination self.assertEqual(len(response.data['resources']), 10) logger.debug(response.data) # Bobby self.assertTrue(self.client.login(username='******', password='******')) response = self.client.get(url, format='json') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 5) self.assertEqual(response.data['total'], 26) # Pagination self.assertEqual(len(response.data['resources']), 10) logger.debug(response.data) # Norman self.assertTrue(self.client.login(username='******', password='******')) response = self.client.get(url, format='json') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 5) self.assertEqual(response.data['total'], 26) # Pagination self.assertEqual(len(response.data['resources']), 10) logger.debug(response.data) # Pagination # Admin self.assertTrue(self.client.login(username='******', password='******')) response = self.client.get(f"{url}?page_size=17", format='json') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 5) self.assertEqual(response.data['total'], 26) # Pagination self.assertEqual(len(response.data['resources']), 17) # Check user permissions resource = ResourceBase.objects.filter(owner__username='******').first() # Admin response = self.client.get(f"{url}/{resource.id}/", format='json') self.assertTrue( 'change_resourcebase' in list(response.data['resource']['perms'])) # Annonymous self.assertIsNone(self.client.logout()) response = self.client.get(f"{url}/{resource.id}/", format='json') self.assertFalse( 'change_resourcebase' in list(response.data['resource']['perms'])) # user owner self.assertTrue(self.client.login(username='******', password='******')) response = self.client.get(f"{url}/{resource.id}/", format='json') self.assertTrue( 'change_resourcebase' in list(response.data['resource']['perms'])) # user not owner and not assigned self.assertTrue(self.client.login(username='******', password='******')) response = self.client.get(f"{url}/{resource.id}/", format='json') self.assertFalse( 'change_resourcebase' in list(response.data['resource']['perms'])) # response has links property # create link if check_ogc_backend(geoserver.BACKEND_PACKAGE): layer = Layer.objects.first() set_resource_default_links(layer, layer) response = self.client.get(f"{url}/{layer.id}/", format='json') self.assertTrue('links' in response.data['resource'].keys())
def test_layer_links(self): lyr = Layer.objects.filter(storeType="dataStore").first() self.assertEquals(lyr.storeType, "dataStore") if check_ogc_backend(geoserver.BACKEND_PACKAGE): links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") _def_link_types = ( 'data', 'image', 'original', 'html', 'OGC:WMS', 'OGC:WFS', 'OGC:WCS') Link.objects.filter(resource=lyr.resourcebase_ptr, link_type__in=_def_link_types).delete() links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 0) set_resource_default_links(lyr, lyr) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 6) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="image") self.assertIsNotNone(links) self.assertEquals(len(links), 3) lyr = Layer.objects.filter(storeType="coverageStore").first() self.assertEquals(lyr.storeType, "coverageStore") if check_ogc_backend(geoserver.BACKEND_PACKAGE): links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") _def_link_types = ( 'data', 'image', 'original', 'html', 'OGC:WMS', 'OGC:WFS', 'OGC:WCS') Link.objects.filter(resource=lyr.resourcebase_ptr, link_type__in=_def_link_types).delete() links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 0) set_resource_default_links(lyr, lyr) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="metadata") self.assertIsNotNone(links) self.assertEquals(len(links), 7) for ll in links: self.assertEquals(ll.link_type, "metadata") links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="data") self.assertIsNotNone(links) self.assertEquals(len(links), 2) links = Link.objects.filter(resource=lyr.resourcebase_ptr, link_type="image") self.assertIsNotNone(links) self.assertEquals(len(links), 7)
def geoserver_post_save_layers(self, instance_id, *args, **kwargs): """ Runs update layers. """ instance = None try: instance = Layer.objects.get(id=instance_id) except Layer.DoesNotExist: logger.debug(f"Layer id {instance_id} does not exist yet!") raise lock_id = f'{self.request.id}' with AcquireLock(lock_id) as lock: if lock.acquire() is True: # Don't run this signal if is a Layer from a remote service if getattr(instance, "remote_service", None) is not None: return if instance.storeType == "remoteStore": return # Don't run this signal handler if it is a tile layer or a remote store (Service) # Currently only gpkg files containing tiles will have this type & will be served via MapProxy. if hasattr(instance, 'storeType') and getattr( instance, 'storeType') in ['tileStore', 'remoteStore']: return instance if isinstance(instance, ResourceBase): if hasattr(instance, 'layer'): instance = instance.layer else: return geonode_upload_sessions = UploadSession.objects.filter( resource=instance) geonode_upload_sessions.update(processed=False) gs_resource = None values = None _tries = 0 _max_tries = getattr(ogc_server_settings, "MAX_RETRIES", 2) # If the store in None then it's a new instance from an upload, # only in this case run the geoserver_upload method if not instance.store or getattr(instance, 'overwrite', False): base_file, info = instance.get_base_file() # There is no need to process it if there is no file. if base_file is None: return gs_name, workspace, values, gs_resource = geoserver_upload( instance, base_file.file.path, instance.owner, instance.name, overwrite=True, title=instance.title, abstract=instance.abstract, charset=instance.charset) values, gs_resource = fetch_gs_resource(instance, values, _tries) while not gs_resource and _tries < _max_tries: values, gs_resource = fetch_gs_resource( instance, values, _tries) _tries += 1 # Get metadata links metadata_links = [] for link in instance.link_set.metadata(): metadata_links.append((link.mime, link.name, link.url)) if gs_resource: logger.debug("Found geoserver resource for this layer: %s" % instance.name) gs_resource.metadata_links = metadata_links instance.gs_resource = gs_resource # Update Attribution link if instance.poc: # gsconfig now utilizes an attribution dictionary gs_resource.attribution = { 'title': str(instance.poc), 'width': None, 'height': None, 'href': None, 'url': None, 'type': None } profile = get_user_model().objects.get( username=instance.poc.username) site_url = settings.SITEURL.rstrip( '/') if settings.SITEURL.startswith( 'http') else settings.SITEURL gs_resource.attribution_link = site_url + profile.get_absolute_url( ) """Get information from geoserver. The attributes retrieved include: * Bounding Box * SRID * Download links (WMS, WCS or WFS and KML) * Styles (SLD) """ try: # This is usually done in Layer.pre_save, however if the hooks # are bypassed by custom create/updates we need to ensure the # bbox is calculated properly. bbox = gs_resource.native_bbox instance.set_bbox_polygon( [bbox[0], bbox[2], bbox[1], bbox[3]], gs_resource.projection) except Exception as e: logger.exception(e) if instance.srid: instance.srid_url = "http://www.spatialreference.org/ref/" + \ instance.srid.replace(':', '/').lower() + "/" elif instance.bbox_polygon is not None: # Guessing 'EPSG:4326' by default instance.srid = 'EPSG:4326' else: raise GeoNodeException( "Invalid Projection. Layer is missing CRS!") # Iterate over values from geoserver. for key in ['alternate', 'store', 'storeType']: # attr_name = key if 'typename' not in key else 'alternate' # print attr_name setattr(instance, key, values[key]) try: if settings.RESOURCE_PUBLISHING: if instance.is_published != gs_resource.advertised: gs_resource.advertised = 'true' if not settings.FREETEXT_KEYWORDS_READONLY: # AF: Warning - this won't allow people to have empty keywords on GeoNode if len(instance.keyword_list() ) == 0 and gs_resource.keywords: for keyword in gs_resource.keywords: if keyword not in instance.keyword_list(): instance.keywords.add(keyword) if any(instance.keyword_list()): keywords = instance.keyword_list() gs_resource.keywords = [ kw for kw in list(set(keywords)) ] # gs_resource should only be called if # ogc_server_settings.BACKEND_WRITE_ENABLED == True if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): gs_catalog.save(gs_resource) except Exception as e: msg = ( 'Error while trying to save resource named %s in GeoServer, ' 'try to use: "%s"' % (gs_resource, str(e))) e.args = (msg, ) logger.exception(e) # store the resource to avoid another geoserver call in the post_save to_update = { 'title': instance.title or instance.name, 'abstract': instance.abstract or "", 'alternate': instance.alternate, 'bbox_polygon': instance.bbox_polygon, 'srid': 'EPSG:4326' } if is_monochromatic_image(instance.thumbnail_url): to_update['thumbnail_url'] = staticfiles.static( settings.MISSING_THUMBNAIL) # Save all the modified information in the instance without triggering signals. try: with transaction.atomic(): ResourceBase.objects.filter( id=instance.resourcebase_ptr.id).update( **to_update) # to_update['name'] = instance.name, to_update[ 'workspace'] = gs_resource.store.workspace.name to_update['store'] = gs_resource.store.name to_update['storeType'] = instance.storeType to_update['typename'] = instance.alternate Layer.objects.filter(id=instance.id).update( **to_update) # Refresh from DB instance.refresh_from_db() except Exception as e: logger.exception(e) # Refreshing CSW records logger.debug( f"... Updating the Catalogue entries for Layer {instance.title}" ) try: catalogue_post_save(instance=instance, sender=instance.__class__) except Exception as e: logger.exception(e) # Refreshing layer links logger.debug( f"... Creating Default Resource Links for Layer {instance.title}" ) try: set_resource_default_links(instance, instance, prune=True) except Exception as e: logger.exception(e) # Save layer attributes logger.debug( f"... Refresh GeoServer attributes list for Layer {instance.title}" ) try: set_attributes_from_geoserver(instance) except Exception as e: logger.exception(e) # Save layer styles logger.debug( f"... Refresh Legend links for Layer {instance.title}") try: set_styles(instance, gs_catalog) except Exception as e: logger.exception(e) # Invalidate GeoWebCache for the updated resource try: _stylefilterparams_geowebcache_layer(instance.alternate) _invalidate_geowebcache_layer(instance.alternate) except Exception: pass # Creating Layer Thumbnail by sending a signal from geonode.geoserver.signals import geoserver_post_save_complete geoserver_post_save_complete.send(sender=instance.__class__, instance=instance) try: geonode_upload_sessions = UploadSession.objects.filter( resource=instance) geonode_upload_sessions.update(processed=True) except Exception as e: logger.exception(e) # Updating HAYSTACK Indexes if needed if settings.HAYSTACK_SEARCH: call_command('update_index')
def geoserver_post_save_local(instance, *args, **kwargs): """Send information to geoserver. The attributes sent include: * Title * Abstract * Name * Keywords * Metadata Links, * Point of Contact name and url """ # Don't run this signal if is a Layer from a remote service if getattr(instance, "remote_service", None) is not None: return # Don't run this signal handler if it is a tile layer or a remote store (Service) # Currently only gpkg files containing tiles will have this type & will be served via MapProxy. if hasattr(instance, 'storeType') and getattr(instance, 'storeType') in ['tileStore', 'remoteStore']: return instance gs_resource = None values = None # If the store in None then it's a new instance from an upload, # only in this case run the geoserver_upload method if not instance.store or getattr(instance, 'overwrite', False): base_file, info = instance.get_base_file() # There is no need to process it if there is not file. if base_file is None: return gs_name, workspace, values, gs_resource = geoserver_upload(instance, base_file.file.path, instance.owner, instance.name, overwrite=True, title=instance.title, abstract=instance.abstract, # keywords=instance.keywords, charset=instance.charset) if not gs_resource: gs_resource = gs_catalog.get_resource( instance.name, store=instance.store, workspace=instance.workspace) if not gs_resource: gs_resource = gs_catalog.get_resource(instance.alternate) if gs_resource: gs_resource.title = instance.title or "" gs_resource.abstract = instance.abstract or "" gs_resource.name = instance.name or "" if not values: values = dict(store=gs_resource.store.name, storeType=gs_resource.store.resource_type, alternate=gs_resource.store.workspace.name + ':' + gs_resource.name, title=gs_resource.title or gs_resource.store.name, abstract=gs_resource.abstract or '', owner=instance.owner) else: msg = "There isn't a geoserver resource for this layer: %s" % instance.name logger.exception(msg) raise Exception(msg) # Get metadata links metadata_links = [] for link in instance.link_set.metadata(): metadata_links.append((link.mime, link.name, link.url)) gs_resource.metadata_links = metadata_links # gs_resource should only be called if # ogc_server_settings.BACKEND_WRITE_ENABLED == True if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): try: gs_catalog.save(gs_resource) except geoserver.catalog.FailedRequestError as e: msg = ('Error while trying to save resource named %s in GeoServer, ' 'try to use: "%s"' % (gs_resource, str(e))) e.args = (msg,) logger.exception(e) # Update Attribution link if instance.poc: # gsconfig now utilizes an attribution dictionary gs_resource.attribution = {'title': str(instance.poc), 'width': None, 'height': None, 'href': None, 'url': None, 'type': None} profile = Profile.objects.get(username=instance.poc.username) site_url = settings.SITEURL.rstrip('/') if settings.SITEURL.startswith('http') else settings.SITEURL gs_resource.attribution_link = site_url + profile.get_absolute_url() # gs_resource should only be called if # ogc_server_settings.BACKEND_WRITE_ENABLED == True if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): try: gs_catalog.save(gs_resource) except geoserver.catalog.FailedRequestError as e: msg = ('Error while trying to save layer named %s in GeoServer, ' 'try to use: "%s"' % (gs_resource, str(e))) e.args = (msg,) logger.exception(e) if isinstance(instance, ResourceBase): if hasattr(instance, 'layer'): instance = instance.layer else: return # Save layer attributes set_attributes_from_geoserver(instance) # Save layer styles set_styles(instance, gs_catalog) # set SLD sld = instance.default_style.sld_body if instance.default_style else None if sld: set_layer_style(instance, instance.alternate, sld) # Invalidate GeoWebCache for the updated resource try: _stylefilterparams_geowebcache_layer(instance.alternate) _invalidate_geowebcache_layer(instance.alternate) except BaseException: pass if instance.storeType == "remoteStore": # Save layer attributes set_attributes_from_geoserver(instance) return """Get information from geoserver. The attributes retrieved include: * Bounding Box * SRID * Download links (WMS, WCS or WFS and KML) * Styles (SLD) """ # instance.name = instance.name or gs_resource.name # instance.title = instance.title or gs_resource.title instance.abstract = gs_resource.abstract or '' instance.workspace = gs_resource.store.workspace.name instance.store = gs_resource.store.name try: logger.debug(" -------------------------------------------------- ") bbox = gs_resource.native_bbox logger.debug(bbox) logger.debug(" -------------------------------------------------- ") # Set bounding box values instance.bbox_x0 = bbox[0] instance.bbox_x1 = bbox[1] instance.bbox_y0 = bbox[2] instance.bbox_y1 = bbox[3] instance.srid = bbox[4] except BaseException: pass if instance.srid: instance.srid_url = "http://www.spatialreference.org/ref/" + \ instance.srid.replace(':', '/').lower() + "/" elif instance.bbox_x0 and instance.bbox_x1 and instance.bbox_y0 and instance.bbox_y1: # Guessing 'EPSG:4326' by default instance.srid = 'EPSG:4326' else: raise GeoNodeException("Invalid Projection. Layer is missing CRS!") # Iterate over values from geoserver. for key in ['alternate', 'store', 'storeType']: # attr_name = key if 'typename' not in key else 'alternate' # print attr_name setattr(instance, key, values[key]) if settings.RESOURCE_PUBLISHING: if instance.is_published != gs_resource.advertised: if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): gs_resource.advertised = 'true' gs_catalog.save(gs_resource) if not settings.FREETEXT_KEYWORDS_READONLY: try: if len(instance.keyword_list()) == 0 and gs_resource.keywords: for keyword in gs_resource.keywords: if keyword not in instance.keyword_list(): instance.keywords.add(keyword) except BaseException: pass if any(instance.keyword_list()): keywords = instance.keyword_list() gs_resource.keywords = [kw for kw in list(set(keywords))] # gs_resource should only be called if # ogc_server_settings.BACKEND_WRITE_ENABLED == True if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True): try: gs_catalog.save(gs_resource) except geoserver.catalog.FailedRequestError as e: msg = ('Error while trying to save resource named %s in GeoServer, ' 'try to use: "%s"' % (gs_resource, str(e))) e.args = (msg,) logger.exception(e) to_update = { 'title': instance.title or instance.name, 'abstract': instance.abstract or "", 'alternate': instance.alternate, 'bbox_x0': instance.bbox_x0, 'bbox_x1': instance.bbox_x1, 'bbox_y0': instance.bbox_y0, 'bbox_y1': instance.bbox_y1, 'srid': instance.srid } # Update ResourceBase resources = ResourceBase.objects.filter(id=instance.resourcebase_ptr.id) resources.update(**to_update) # to_update['name'] = instance.name, to_update['workspace'] = instance.workspace to_update['store'] = instance.store to_update['storeType'] = instance.storeType to_update['typename'] = instance.alternate # Save all the modified information in the instance without triggering signals. Layer.objects.filter(id=instance.id).update(**to_update) # Refresh from DB instance.refresh_from_db() # store the resource to avoid another geoserver call in the post_save instance.gs_resource = gs_resource # Refresh and create the instance default links layer = Layer.objects.get(id=instance.id) set_resource_default_links(instance, layer, prune=True) # some thumbnail generators will update thumbnail_url. If so, don't # immediately re-generate the thumbnail here. use layer#save(update_fields=['thumbnail_url']) if 'update_fields' in kwargs and kwargs['update_fields'] is not None and \ 'thumbnail_url' in kwargs['update_fields']: logger.info("... Creating Thumbnail for Layer [%s]" % (instance.alternate)) create_gs_thumbnail(instance, overwrite=True) # NOTTODO by simod: we should not do this! # need to be removed when fixing #2015 catalogue_post_save(instance, Layer) # Updating HAYSTACK Indexes if needed if settings.HAYSTACK_SEARCH: from django.core.management import call_command call_command('update_index')
def qgis_server_post_save(instance, sender, **kwargs): """Save keywords to QGIS Server. The way keywords are implemented requires the layer to be saved to the database before accessing them. This hook also creates QGIS Project. Which is essentials for QGIS Server. There are also several Geonode Links generated, like thumbnail and legends :param instance: geonode Layer :type instance: Layer :param sender: geonode Layer type :type sender: type(Layer) """ if not sender == Layer: return # TODO # 1. Create or update associated QGISServerLayer [Done] # 2. Create Link for the tile and legend. logger.debug('QGIS Server Post Save') # copy layer to QGIS Layer Directory try: geonode_layer_path = instance.get_base_file()[0].file.path except AttributeError: logger.debug('Layer does not have base file') return qgis_layer, created = QGISServerLayer.objects.get_or_create( layer=instance) logger.debug('Geonode Layer Path %s' % geonode_layer_path) base_filename, original_ext = os.path.splitext(geonode_layer_path) extensions = QGISServerLayer.accepted_format is_shapefile = False for ext in extensions: if os.path.exists(base_filename + '.' + ext): is_shapefile = is_shapefile or ext == 'shp' try: if created: # Assuming different layer has different filename because # geonode rename it shutil.copy2( base_filename + '.' + ext, QGIS_layer_directory ) logger.debug('Create new basefile') else: # If there is already a file, replace the old one qgis_layer_base_filename = qgis_layer.qgis_layer_path_prefix shutil.copy2( base_filename + '.' + ext, qgis_layer_base_filename + '.' + ext ) logger.debug('Overwrite existing basefile') logger.debug( 'Copying %s' % base_filename + '.' + ext + ' Success') logger.debug('Into %s' % QGIS_layer_directory) except IOError as e: logger.debug( 'Copying %s' % base_filename + '.' + ext + ' FAILED ' + e) if created: # Only set when creating new QGISServerLayer Object geonode_filename = os.path.basename(geonode_layer_path) basename = os.path.splitext(geonode_filename)[0] qgis_layer.base_layer_path = os.path.join( QGIS_layer_directory, basename + original_ext # Already with dot ) qgis_layer.save() # refresh to get QGIS Layer instance.refresh_from_db() # Set layer crs try: if is_shapefile: dataset = ogr.Open(geonode_layer_path) layer = dataset.GetLayer() spatial_ref = layer.GetSpatialRef() srid = spatial_ref.GetAuthorityCode(None) if spatial_ref else None if srid: instance.srid = srid else: dataset = gdal.Open(geonode_layer_path) prj = dataset.GetProjection() srs = osr.SpatialReference(wkt=prj) srid = srs.GetAuthorityCode(None) if srs else None if srid: instance.srid = srid except Exception as e: logger.debug("Can't retrieve projection: {layer}".format( layer=geonode_layer_path)) logger.exception(e) # Refresh and create the instance default links set_resource_default_links(instance, qgis_layer, is_shapefile=is_shapefile, original_ext=original_ext) # Create thumbnail overwrite = getattr(instance, 'overwrite', False) create_qgis_server_thumbnail.delay( instance, overwrite=overwrite) # Attributes set_attributes(instance) # Update xml file # Read metadata from layer that InaSAFE use. # Some are not found: organisation, email, url try: new_values = { 'date': instance.date.isoformat(), 'abstract': instance.abstract, 'title': instance.title, 'license': instance.license_verbose, } except (TypeError, AttributeError): new_values = {} pass # Get the path of the metadata file basename, _ = os.path.splitext(qgis_layer.base_layer_path) xml_file_path = basename + '.xml' if os.path.exists(xml_file_path): try: update_xml(xml_file_path, new_values) except (TypeError, AttributeError): pass # Also update xml in QGIS Server xml_file_path = qgis_layer.qgis_layer_path_prefix + '.xml' if os.path.exists(xml_file_path): try: update_xml(xml_file_path, new_values) except (TypeError, AttributeError): pass # Remove existing tile caches if overwrite if overwrite: tiles_directory = settings.QGIS_SERVER_CONFIG['tiles_directory'] basename, _ = os.path.splitext(qgis_layer.base_layer_path) basename = os.path.basename(basename) tiles_cache_path = os.path.join(tiles_directory, basename) try: shutil.rmtree(tiles_cache_path) except OSError: # The path doesn't exists yet pass