From 1d45e681a885c095662a24a8b57a508940aabe29 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 10 Aug 2012 21:47:27 +0000 Subject: [PATCH 01/60] start pycsw integration --- geonode/catalogue/backends/pycsw.py | 25 +++++++ geonode/settings.py | 6 +- geonode/tests/csw.py | 108 ++++++++++++++-------------- 3 files changed, 83 insertions(+), 56 deletions(-) create mode 100644 geonode/catalogue/backends/pycsw.py diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py new file mode 100644 index 00000000000..1563ed9a95b --- /dev/null +++ b/geonode/catalogue/backends/pycsw.py @@ -0,0 +1,25 @@ +######################################################################### +# +# Copyright (C) 2012 OpenPlans +# +# 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 . +# +######################################################################### + +from geonode.catalogue.backends.generic import CatalogueBackend as GenericCatalogueBackend + +class CatalogueBackend(GenericCatalogueBackend): + def __init__(self, *args, **kwargs): + super(CatalogueBackend, self).__init__(*args, **kwargs) + self.catalogue.type = 'pycsw' diff --git a/geonode/settings.py b/geonode/settings.py index 5e977d8320f..257fefb8c2b 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -308,16 +308,16 @@ def get_user_url(u): CATALOGUE = { 'default': { # The underlying CSW implementation - 'ENGINE': 'geonode.catalogue.backends.geonetwork', + 'ENGINE': 'geonode.catalogue.backends.pycsw', # enabled formats #'formats': ['DIF', 'Dublin Core', 'FGDC', 'TC211'], 'FORMATS': ['TC211'], # The FULLY QUALIFIED base url to the CSW instance for this GeoNode - #'url': 'http://localhost/pycsw/trunk/csw.py', + #'URL': 'http://localhost/pycsw/trunk/csw.py', 'URL': 'http://localhost:8080/geonetwork/srv/en/csw', - #'url': 'http://localhost:8080/deegree-csw-demo-3.0.4/services', + #'URL': 'http://localhost:8080/deegree-csw-demo-3.0.4/services', # login credentials (for GeoNetwork) 'USER': 'admin', diff --git a/geonode/tests/csw.py b/geonode/tests/csw.py index 099b0b5b118..607f92e7fca 100644 --- a/geonode/tests/csw.py +++ b/geonode/tests/csw.py @@ -17,8 +17,10 @@ # ######################################################################### +import glob import os from unittest import TestCase +from lxml import etree from django.core.management import call_command import gisdata from geonode.catalogue import get_catalogue @@ -70,7 +72,7 @@ def test_csw_search_count(self): typenames = ' '.join(f.parameters['typeNames']['values']) # get all records - csw.catalogue.getrecords(typenames=typenames) + csw.catalogue.getrecords(typenames='csw:Record gmd:MD_Metadata') self.assertEqual(csw.catalogue.results['matches'], 16, 'Expected 16 records') # get all ISO records, test for numberOfRecordsMatched @@ -145,13 +147,13 @@ def test_csw_outputschema_dc_bbox(self): self.assertEqual(record.bbox.crs.code, 4326, 'Expected a specific CRS code value in Dublin Core model') # test BBOX properties in Dublin Core - self.assertEqual(record.bbox.minx, '-81.8593555', + self.assertEqual(record.bbox.minx, '-81.86', 'Expected a specific minx coordinate value in Dublin Core model') - self.assertEqual(record.bbox.miny, '12.1665322', + self.assertEqual(record.bbox.miny, '12.17', 'Expected a specific minx coordinate value in Dublin Core model') - self.assertEqual(record.bbox.maxx, '-81.356409', + self.assertEqual(record.bbox.maxx, '-81.36', 'Expected a specific maxx coordinate value in Dublin Core model') - self.assertEqual(record.bbox.maxy, '13.396306', + self.assertEqual(record.bbox.maxy, '13.4', 'Expected a specific maxy coordinate value in Dublin Core model') def test_csw_outputschema_fgdc(self): @@ -163,10 +165,10 @@ def test_csw_outputschema_fgdc(self): csw = get_catalogue() if csw.catalogue.type == 'pycsw': # get all ISO records in FGDC schema - csw.getrecords(typenames='gmd:MD_Metadata', keywords=['san_andres_y_providencia_location'], - outputschema=namespaces['fgdc']) + csw.catalogue.getrecords(typenames='gmd:MD_Metadata', keywords=['san_andres_y_providencia_location'], + outputschema='http://www.opengis.net/cat/csw/csdgm') - record = csw.records.values()[0] + record = csw.catalogue.records.values()[0] # test that the ISO title maps correctly in FGDC self.assertEqual(record.idinfo.citation.citeinfo['title'], @@ -186,16 +188,16 @@ def test_csw_upload_fgdc(self): if csw.catalogue.type == 'pycsw': # upload a native FGDC metadata document md_doc = etree.tostring(etree.fromstring(open(os.path.join(gisdata.GOOD_METADATA, 'sangis.org', 'Census', 'Census_Blockgroup_Pop_Housing.shp.xml')).read())) - csw.transaction(ttype='insert', typename='fgdc:metadata', record=md_doc) + csw.catalogue.transaction(ttype='insert', typename='fgdc:metadata', record=md_doc) # test that FGDC document was successfully inserted - self.assertEqual(csw.results['inserted'], 1, 'Expected 1 inserted record in FGDC model') + self.assertEqual(csw.catalogue.results['inserted'], 1, 'Expected 1 inserted record in FGDC model') # query against FGDC typename, output FGDC - csw.getrecords(typenames='fgdc:metadata') - self.assertEqual(csw.results['matches'], 1, 'Expected 1 record in FGDC model') + csw.catalogue.getrecords(typenames='fgdc:metadata') + self.assertEqual(csw.catalogue.results['matches'], 1, 'Expected 1 record in FGDC model') - record = csw.records.values()[0] + record = csw.catalogue.records.values()[0] # test that the FGDC title maps correctly in DC self.assertEqual(record.title, 'Census_Blockgroup_Pop_Housing', 'Expected a specific title in DC model') @@ -213,17 +215,17 @@ def test_csw_upload_fgdc(self): self.assertEqual(record.bbox.maxy, '33.51', 'Expected a specific maxy coordinate value in Dublin Core model') # query against FGDC typename, return in ISO - csw.getrecords(typenames='fgdc:metadata', esn='brief', outputschema=namespaces['gmd']) - self.assertEqual(csw.results['matches'], 1, 'Expected 1 record in ISO model') + csw.catalogue.getrecords(typenames='fgdc:metadata', esn='brief', outputschema='http://www.isotc211.org/2005/gmd') + self.assertEqual(csw.catalogue.results['matches'], 1, 'Expected 1 record in ISO model') - record = csw.records.values()[0] + record = csw.catalogue.records.values()[0] # test that the FGDC title maps correctly in ISO self.assertEqual(record.identification.title, 'Census_Blockgroup_Pop_Housing', 'Expected a specific title in ISO model') # cleanup and delete inserted FGDC metadata document - csw.transaction(ttype='delete', typename='fgdc:metadata', cql='fgdc:Title like "Census_Blockgroup_Pop_Housing"') - self.assertEqual(csw.results['deleted'], 1, 'Expected 1 deleted record in FGDC model') + csw.catalogue.transaction(ttype='delete', typename='fgdc:metadata', cql='fgdc:Title like "Census_Blockgroup_Pop_Housing"') + self.assertEqual(csw.catalogue.results['deleted'], 1, 'Expected 1 deleted record in FGDC model') def test_csw_bulk_upload(self): """Verify that GeoNode can handle bulk upload of ISO and FGDC metadata""" @@ -241,52 +243,52 @@ def test_csw_bulk_upload(self): for mfile in files: if mfile.endswith('.xml'): md_doc = etree.tostring(etree.fromstring(open(os.path.join(root, mfile)).read())) - csw.transaction(ttype='insert', typename='fgdc:metadata', record=md_doc) - identifiers.append(csw.results['insertresults'][0]) + csw.catalogue.transaction(ttype='insert', typename='fgdc:metadata', record=md_doc) + identifiers.append(csw.catalogue.results['insertresults'][0]) for md in glob.glob(os.path.join(gisdata.GOOD_METADATA, 'wustl.edu', '*.xml')): md_doc = etree.tostring(etree.fromstring(open(md).read())) - csw.transaction(ttype='insert', typename='gmd:MD_Metadata', record=md_doc) - identifiers.append(csw.results['insertresults'][0]) + csw.catalogue.transaction(ttype='insert', typename='gmd:MD_Metadata', record=md_doc) + identifiers.append(csw.catalogue.results['insertresults'][0]) # query against FGDC typename - csw.getrecords(typenames='fgdc:metadata') - self.assertEqual(csw.results['matches'], 187, 'Expected 187 records in FGDC model') + csw.catalogue.getrecords(typenames='fgdc:metadata') + self.assertEqual(csw.catalogue.results['matches'], 72, 'Expected 187 records in FGDC model') # query against ISO typename - csw.getrecords(typenames='gmd:MD_Metadata') - self.assertEqual(csw.results['matches'], 194, 'Expected 194 records in ISO model') + csw.catalogue.getrecords(typenames='gmd:MD_Metadata') + self.assertEqual(csw.catalogue.results['matches'], 115, 'Expected 194 records in ISO model') # query against FGDC and ISO typename - csw.getrecords(typenames='gmd:MD_Metadata fgdc:metadata') - self.assertEqual(csw.results['matches'], 381, 'Expected 381 records total in FGDC and ISO model') + csw.catalogue.getrecords(typenames='gmd:MD_Metadata fgdc:metadata') + self.assertEqual(csw.catalogue.results['matches'], 187, 'Expected 381 records total in FGDC and ISO model') # clean up for i in identifiers: - csw.transaction(ttype='delete', identifier=i) - - - def test_layer_delete_from_catalogue(self): - """Verify that layer is correctly deleted from CSW catalogue - """ - - # Test Uploading then Deleting a Shapefile from GeoNetwork - shp_file = os.path.join(gisdata.VECTOR_DATA, 'san_andres_y_providencia_poi.shp') - shp_layer = file_upload(shp_file) - catalogue = get_catalogue() - catalogue.remove_record(shp_layer.uuid) - shp_layer_info = catalogue.get_record(shp_layer.uuid) - assert shp_layer_info == None - - # Clean up and completely delete the layer - shp_layer.delete() + csw.catalogue.transaction(ttype='delete', identifier=i) - # Test Uploading then Deleting a TIFF file from GeoNetwork - tif_file = os.path.join(gisdata.RASTER_DATA, 'test_grid.tif') - tif_layer = file_upload(tif_file) - catalogue.remove_record(tif_layer.uuid) - tif_layer_info = catalogue.get_record(tif_layer.uuid) - assert tif_layer_info == None - # Clean up and completely delete the layer - tif_layer.delete() +# def test_layer_delete_from_catalogue(self): +# """Verify that layer is correctly deleted from CSW catalogue +# """ +# +# # Test Uploading then Deleting a Shapefile from GeoNetwork +# shp_file = os.path.join(gisdata.VECTOR_DATA, 'san_andres_y_providencia_poi.shp') +# shp_layer = file_upload(shp_file) +# catalogue = get_catalogue() +# catalogue.remove_record(shp_layer.uuid) +# shp_layer_info = catalogue.get_record(shp_layer.uuid) +# assert shp_layer_info == None +# +# # Clean up and completely delete the layer +# shp_layer.delete() +# +# # Test Uploading then Deleting a TIFF file from GeoNetwork +# tif_file = os.path.join(gisdata.RASTER_DATA, 'test_grid.tif') +# tif_layer = file_upload(tif_file) +# catalogue.remove_record(tif_layer.uuid) +# tif_layer_info = catalogue.get_record(tif_layer.uuid) +# assert tif_layer_info == None +# +# # Clean up and completely delete the layer +# tif_layer.delete() From 8543bb5b80d0c04369abf67dd2064b97d5d4688e Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 12:39:10 +0000 Subject: [PATCH 02/60] bring back test_layer_delete_from_catalogue() --- geonode/tests/csw.py | 50 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/geonode/tests/csw.py b/geonode/tests/csw.py index 607f92e7fca..ed56ac034a5 100644 --- a/geonode/tests/csw.py +++ b/geonode/tests/csw.py @@ -24,6 +24,7 @@ from django.core.management import call_command import gisdata from geonode.catalogue import get_catalogue +from geonode.layers.utils import file_upload class GeoNodeCSWTest(TestCase): """Tests geonode.catalogue app/module""" @@ -268,27 +269,28 @@ def test_csw_bulk_upload(self): csw.catalogue.transaction(ttype='delete', identifier=i) -# def test_layer_delete_from_catalogue(self): -# """Verify that layer is correctly deleted from CSW catalogue -# """ -# -# # Test Uploading then Deleting a Shapefile from GeoNetwork -# shp_file = os.path.join(gisdata.VECTOR_DATA, 'san_andres_y_providencia_poi.shp') -# shp_layer = file_upload(shp_file) -# catalogue = get_catalogue() -# catalogue.remove_record(shp_layer.uuid) -# shp_layer_info = catalogue.get_record(shp_layer.uuid) -# assert shp_layer_info == None -# -# # Clean up and completely delete the layer -# shp_layer.delete() -# -# # Test Uploading then Deleting a TIFF file from GeoNetwork -# tif_file = os.path.join(gisdata.RASTER_DATA, 'test_grid.tif') -# tif_layer = file_upload(tif_file) -# catalogue.remove_record(tif_layer.uuid) -# tif_layer_info = catalogue.get_record(tif_layer.uuid) -# assert tif_layer_info == None -# -# # Clean up and completely delete the layer -# tif_layer.delete() + def test_layer_delete_from_catalogue(self): + """Verify that layer is correctly deleted from Catalogue + """ + + # Test Uploading then Deleting a Shapefile from Catalogue + shp_file = os.path.join(gisdata.VECTOR_DATA, 'san_andres_y_providencia_poi.shp') + shp_layer = file_upload(shp_file) + catalogue = get_catalogue() + catalogue.remove_record(shp_layer.uuid) + shp_layer_info = catalogue.get_record(shp_layer.uuid) + self.assertEqual(shp_layer_info, None, 'Expected no layer info for Shapefile') + + # Clean up and completely delete the layer + shp_layer.delete() + + # Test Uploading then Deleting a TIFF file from GeoNetwork + tif_file = os.path.join(gisdata.RASTER_DATA, 'test_grid.tif') + tif_layer = file_upload(tif_file) + catalogue.remove_record(tif_layer.uuid) + tif_layer_info = catalogue.get_record(tif_layer.uuid) + self.assertEqual(tif_layer_info, None, 'Expected no layer info for TIFF file') + assert tif_layer_info == None + + # Clean up and completely delete the layer + tif_layer.delete() From ec6db099cd4f8aaa8580e95a9668a1d068f73eb4 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 13:16:35 +0000 Subject: [PATCH 03/60] make pycsw default catalogue --- geonode/settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/geonode/settings.py b/geonode/settings.py index 257fefb8c2b..2aff0f9e773 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -142,7 +142,7 @@ 'geonode.people', 'geonode.proxy', 'geonode.security', -# 'geonode.catalogue', + 'geonode.catalogue', ) LOGGING = { 'version': 1, @@ -315,8 +315,8 @@ def get_user_url(u): 'FORMATS': ['TC211'], # The FULLY QUALIFIED base url to the CSW instance for this GeoNode - #'URL': 'http://localhost/pycsw/trunk/csw.py', - 'URL': 'http://localhost:8080/geonetwork/srv/en/csw', + 'URL': 'http://localhost/pycsw/trunk/csw.py', + #'URL': 'http://localhost:8080/geonetwork/srv/en/csw', #'URL': 'http://localhost:8080/deegree-csw-demo-3.0.4/services', # login credentials (for GeoNetwork) From 0b8a74ee43d65698d7a615b0cef9c0e55b53e2c0 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 13:16:54 +0000 Subject: [PATCH 04/60] update testing instructions --- geonode/tests/README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/geonode/tests/README b/geonode/tests/README index 065a7a7bcd9..346fb39e9e0 100644 --- a/geonode/tests/README +++ b/geonode/tests/README @@ -4,6 +4,7 @@ python manage.py test geonode.tests.integration paver stop # To run the csw tests, (including the sample data they need) do: -paver reset start setup_data +paver reset start +paver setup_data python manage.py test geonode.tests.csw paver stop From e2e45481642be48a7745f5ea96cbfd723b72f909 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 17:44:46 +0000 Subject: [PATCH 05/60] remove line not needed --- geonode/tests/csw.py | 1 - 1 file changed, 1 deletion(-) diff --git a/geonode/tests/csw.py b/geonode/tests/csw.py index ed56ac034a5..8cd36b446d1 100644 --- a/geonode/tests/csw.py +++ b/geonode/tests/csw.py @@ -290,7 +290,6 @@ def test_layer_delete_from_catalogue(self): catalogue.remove_record(tif_layer.uuid) tif_layer_info = catalogue.get_record(tif_layer.uuid) self.assertEqual(tif_layer_info, None, 'Expected no layer info for TIFF file') - assert tif_layer_info == None # Clean up and completely delete the layer tif_layer.delete() From baa5bc380c36841b9f24cc93abb6177755616d98 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 17:45:38 +0000 Subject: [PATCH 06/60] add ref to geonode.catalogue.urls --- geonode/urls.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/geonode/urls.py b/geonode/urls.py index 4f833a11212..5f58da5b09a 100644 --- a/geonode/urls.py +++ b/geonode/urls.py @@ -78,6 +78,9 @@ {'sitemaps': sitemaps}, name='sitemap'), (r'^i18n/', include('django.conf.urls.i18n')), (r'^admin/', include(admin.site.urls)), + + # Catalogue + (r'^catalogue/', include('geonode.catalogue.urls')), ) urlpatterns += geonode.proxy.urls.urlpatterns From 8854f6613e0d8f46b9f85a3bd158f7e395daaa04 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 17:47:08 +0000 Subject: [PATCH 07/60] a./ make FORMATS implementation specific b./ only do CSW-T when CSW is a non-localdb pycsw --- geonode/catalogue/backends/generic.py | 49 +++++------ geonode/catalogue/backends/geonetwork.py | 1 + geonode/catalogue/backends/pycsw.py | 6 ++ geonode/catalogue/urls.py | 25 ++++++ geonode/catalogue/views.py | 104 +++++++++++++++++++++++ geonode/settings.py | 11 ++- 6 files changed, 163 insertions(+), 33 deletions(-) create mode 100644 geonode/catalogue/urls.py create mode 100644 geonode/catalogue/views.py diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index f2c3d73fcd7..01de5abebfe 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -35,14 +35,11 @@ 'TC211': ('gmd:MD_Metadata', 'http://www.isotc211.org/2005/gmd'), } - - class Catalogue(CatalogueServiceWeb): def __init__(self, *args, **kwargs): self.url = kwargs['URL'] self.user = kwargs['USER'] self.password = kwargs['PASSWORD'] - self.formats = kwargs['FORMATS'] self._group_ids = {} self._operation_ids = {} self.connected = False @@ -356,8 +353,6 @@ def extract_links(self, rec): pass return links - - class CatalogueBackend(BaseCatalogueBackend): def __init__(self, *args, **kwargs): self.catalogue = Catalogue(*args, **kwargs) @@ -371,7 +366,6 @@ def get_record(self, uuid): rec.links['download'] = self.catalogue.extract_links(rec) return rec - def search_records(self, keywords, start, limit, bbox): with self.catalogue: bbox = self.catalogue.normalize_bbox(bbox) @@ -388,27 +382,28 @@ def search_records(self, keywords, start, limit, bbox): return result - def remove_record(self, uuid): - with self.catalogue: - catalogue_record = self.catalogue.get_by_uuid(uuid) - if catalogue_record is None: - return - - try: - # this is a bit hacky, delete_layer expects an instance of the layer - # model but it just passes it to a Django template so a dict works - # too. - self.catalogue.delete_layer({ "uuid": uuid }) - except: - logger.exception('Couldn\'t delete Catalogue record ' - 'during cleanup()') + if self.catalogue.type != 'pycsw' and not self.catalogue.local: + with self.catalogue: + catalogue_record = self.catalogue.get_by_uuid(uuid) + if catalogue_record is None: + return + + try: + # this is a bit hacky, delete_layer expects an instance of the layer + # model but it just passes it to a Django template so a dict works + # too. + self.catalogue.delete_layer({ "uuid": uuid }) + except: + logger.exception('Couldn\'t delete Catalogue record ' + 'during cleanup()') def create_record(self, item): - with self.catalogue: - record = self.catalogue.get_by_uuid(item.uuid) - if record is None: - md_link = self.catalogue.create_from_layer(item) - item.metadata_links = [("text/xml", "TC211", md_link)] - else: - self.catalogue.update_layer(item) + if self.catalogue.type != 'pycsw' and not self.catalogue.local: + with self.catalogue: + record = self.catalogue.get_by_uuid(item.uuid) + if record is None: + md_link = self.catalogue.create_from_layer(item) + item.metadata_links = [("text/xml", "TC211", md_link)] + else: + self.catalogue.update_layer(item) diff --git a/geonode/catalogue/backends/geonetwork.py b/geonode/catalogue/backends/geonetwork.py index 48641e96174..14810bd766d 100644 --- a/geonode/catalogue/backends/geonetwork.py +++ b/geonode/catalogue/backends/geonetwork.py @@ -23,3 +23,4 @@ class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.type = 'geonetwork' + self.catalogue.formats = ['TC211'] diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py index 1563ed9a95b..1f503019895 100644 --- a/geonode/catalogue/backends/pycsw.py +++ b/geonode/catalogue/backends/pycsw.py @@ -17,9 +17,15 @@ # ######################################################################### +from django.conf import settings from geonode.catalogue.backends.generic import CatalogueBackend as GenericCatalogueBackend class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.type = 'pycsw' + self.catalogue.formats = ['DIF', 'Dublin Core', 'FGDC', 'TC211'] + + self.catalogue.local = True + if not settings.CATALOGUE['default']['LOCAL']: + self.catalogue.local = False diff --git a/geonode/catalogue/urls.py b/geonode/catalogue/urls.py new file mode 100644 index 00000000000..33bfa3a47f2 --- /dev/null +++ b/geonode/catalogue/urls.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +######################################################################### +# +# Copyright (C) 2012 OpenPlans +# +# 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 . +# +######################################################################### + +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('geonode.catalogue.views', + url(r'csw', 'csw_global_dispatch', name='csw_global_dispatch'), +) diff --git a/geonode/catalogue/views.py b/geonode/catalogue/views.py new file mode 100644 index 00000000000..d17f421a339 --- /dev/null +++ b/geonode/catalogue/views.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +######################################################################### +# +# Copyright (C) 2012 OpenPlans +# +# 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 . +# +######################################################################### + +import os +from ConfigParser import SafeConfigParser +from django.conf import settings +from django.http import HttpResponse +from django.views.decorators.csrf import csrf_exempt +from lxml import etree +from pycsw import server + +@csrf_exempt +def csw_global_dispatch(request): + + app_root = os.path.dirname(__file__) + + # serialize settings.CSW into SafeConfigParser + # object for interaction with pycsw + config = SafeConfigParser() + for section, options in settings.CATALOGUE.iteritems(): + config.add_section(section) + for k, v in options.iteritems(): + config.set(section, k, v) + + scheme = "http" + if request.is_secure(): + scheme = "https" + + # update server.url + server_url = '%s://%s/csw/' %(scheme, request.META['HTTP_HOST']) + config.set('server', 'url', server_url) + + # request.meta has: + # QUERY_STRING, REMOTE_ADDR, CONTENT_LENGTH, SERVER_NAME + # SERVER_PORT + env = request.META.copy() + env.update({ + 'local.app_root': app_root, + 'REQUEST_URI': request.build_absolute_uri(), + 'REQUEST_METHOD': request.method, + 'wsgi.url_scheme': scheme, + 'PATH_INFO': request.path_info, + 'wsgi.input': request # this is being a bit sneaky but w/e + }) + + csw = server.Csw(config, env) + + content = csw.dispatch_wsgi() + + return HttpResponse(content, content_type=csw.contenttype) + +def csw_local_dispatch(request): + """ + HTTP-less CSW + """ + # set up configuration + config = SafeConfigParser() + + for section, options in settings.CATALOGUE.iteritems(): + config.add_section(section) + for option, value in options.iteritems(): + config.set(section, option, value) + + # fake HTTP environment variable + os.environ['QUERY_STRING'] = '' + + # init pycsw + csw = server.Csw(config) + + # fake HTTP method + csw.requesttype = request.method.upper() + + # fake HTTP request parameters + csw.kvp = { + 'elementsetname': 'brief', + 'typenames': 'csw:Record', + 'resulttype': 'results', + 'constraintlanguage': 'CQL_TEXT', + #'constraint': 'csw:AnyText like "%iLor%"', + 'constraint': None, + 'maxrecords': '2' + } + + response = csw.getrecords() + response_string = etree.tostring(response) + + return HttpResponse(response, content_type=csw.contenttype) diff --git a/geonode/settings.py b/geonode/settings.py index 2aff0f9e773..fa3ed588b68 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -310,18 +310,17 @@ def get_user_url(u): # The underlying CSW implementation 'ENGINE': 'geonode.catalogue.backends.pycsw', - # enabled formats - #'formats': ['DIF', 'Dublin Core', 'FGDC', 'TC211'], - 'FORMATS': ['TC211'], - # The FULLY QUALIFIED base url to the CSW instance for this GeoNode - 'URL': 'http://localhost/pycsw/trunk/csw.py', + 'URL': 'http://localhost/catalogue/csw', #'URL': 'http://localhost:8080/geonetwork/srv/en/csw', #'URL': 'http://localhost:8080/deegree-csw-demo-3.0.4/services', # login credentials (for GeoNetwork) 'USER': 'admin', - 'PASSWORD': 'admin' + 'PASSWORD': 'admin', + + # is pycsw tightly coupled? + 'LOCAL': True } } From cb45f9bd7f9e64a3589efdeb97fd78ccb9c1652d Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 17:50:39 +0000 Subject: [PATCH 08/60] add Dublin Core for GN --- geonode/catalogue/backends/geonetwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/catalogue/backends/geonetwork.py b/geonode/catalogue/backends/geonetwork.py index 14810bd766d..02d1b757d20 100644 --- a/geonode/catalogue/backends/geonetwork.py +++ b/geonode/catalogue/backends/geonetwork.py @@ -23,4 +23,4 @@ class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.type = 'geonetwork' - self.catalogue.formats = ['TC211'] + self.catalogue.formats = ['Dublin Core', 'TC211'] From 92cd47319e9d3c4b39d0266ebb5f1d0a178cee3d Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 17:57:54 +0000 Subject: [PATCH 09/60] update CSW URL --- geonode/catalogue/views.py | 2 +- geonode/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/geonode/catalogue/views.py b/geonode/catalogue/views.py index d17f421a339..6baed329cb4 100644 --- a/geonode/catalogue/views.py +++ b/geonode/catalogue/views.py @@ -44,7 +44,7 @@ def csw_global_dispatch(request): scheme = "https" # update server.url - server_url = '%s://%s/csw/' %(scheme, request.META['HTTP_HOST']) + server_url = '%s://%s/catalogue/csw/' %(scheme, request.META['HTTP_HOST']) config.set('server', 'url', server_url) # request.meta has: diff --git a/geonode/settings.py b/geonode/settings.py index fa3ed588b68..64e43e6622f 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -311,7 +311,7 @@ def get_user_url(u): 'ENGINE': 'geonode.catalogue.backends.pycsw', # The FULLY QUALIFIED base url to the CSW instance for this GeoNode - 'URL': 'http://localhost/catalogue/csw', + 'URL': '%scatalogue/csw' % SITEURL, #'URL': 'http://localhost:8080/geonetwork/srv/en/csw', #'URL': 'http://localhost:8080/deegree-csw-demo-3.0.4/services', From 805da3552c381d71939a0cad3402c0d045d1f905 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 18:24:38 +0000 Subject: [PATCH 10/60] add Atom and ebRIM --- geonode/catalogue/backends/generic.py | 2 ++ geonode/catalogue/backends/pycsw.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 01de5abebfe..526d92f77f6 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -29,8 +29,10 @@ from geonode.catalogue.backends.base import BaseCatalogueBackend METADATA_FORMATS = { + 'Atom': ('atom:entry', 'http://www.w3.org/2005/Atom'), 'DIF': ('dif:DIF', 'http://gcmd.gsfc.nasa.gov/Aboutus/xml/dif/'), 'Dublin Core': ('csw:Record', 'http://www.opengis.net/cat/csw/2.0.2'), + 'ebRIM': ('rim:RegistryObject', 'urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0'), 'FGDC': ('fgdc:metadata', 'http://www.opengis.net/cat/csw/csdgm'), 'TC211': ('gmd:MD_Metadata', 'http://www.isotc211.org/2005/gmd'), } diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py index 1f503019895..16f9f20d917 100644 --- a/geonode/catalogue/backends/pycsw.py +++ b/geonode/catalogue/backends/pycsw.py @@ -24,7 +24,7 @@ class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.type = 'pycsw' - self.catalogue.formats = ['DIF', 'Dublin Core', 'FGDC', 'TC211'] + self.catalogue.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211'] self.catalogue.local = True if not settings.CATALOGUE['default']['LOCAL']: From e5fb8ceb241380d70392c7b39e43d7330a489340 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 18:35:02 +0000 Subject: [PATCH 11/60] add pycsw configuration --- geonode/settings.py | 60 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/geonode/settings.py b/geonode/settings.py index 64e43e6622f..0a1dcdea94b 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -319,8 +319,64 @@ def get_user_url(u): 'USER': 'admin', 'PASSWORD': 'admin', - # is pycsw tightly coupled? - 'LOCAL': True + # is pycsw inline to GeoNode and tightly coupled? + 'LOCAL': True, + + # pycsw configuration + 'CONFIGURATION': { + 'server': { + 'home': '.', + 'encoding': 'UTF-8', + 'language': LANGUAGE_CODE, + 'maxrecords': '10', + #'loglevel': 'DEBUG', + #'logfile': '/tmp/pycsw.log', + #'federatedcatalogues': 'http://geo.data.gov/geoportal/csw/discovery', + #'pretty_print': 'true', + #'domainquerytype': 'range', + #'domaincounts': 'true', + 'profiles': 'apiso,atom,dif,ebrim,fgdc', + }, + 'metadata:main': { + 'identification_title': '%s Catalogue' % SITENAME, + 'identification_abstract': 'GeoNode is an open source platform that facilitates the creation, sharing, and collaborative use of geospatial data', + 'identification_keywords': '%s,sdi,catalogue,discovery,metadata,GeoNode' % SITENAME, + 'identification_keywords_type': 'theme', + 'identification_fees': 'None', + 'identification_accessconstraints': 'None', + 'provider_name': 'Organization Name', + 'provider_url': SITEURL, + 'contact_name': 'Lastname, Firstname', + 'contact_position': 'Position Title', + 'contact_address': 'Mailing Address', + 'contact_city': 'City', + 'contact_stateorprovince': 'Administrative Area', + 'contact_postalcode': 'Zip or Postal Code', + 'contact_country': 'Country', + 'contact_phone': '+xx-xxx-xxx-xxxx', + 'contact_fax': '+xx-xxx-xxx-xxxx', + 'contact_email': 'Email Address', + 'contact_url': 'Contact URL', + 'contact_hours': 'Hours of Service', + 'contact_instructions': 'During hours of service. Off on weekends.', + 'contact_role': 'pointOfContact', + }, + 'repository': { + 'source': 'geonode', + 'mappings': 'geonode/catalogue/backends/pycsw_mappings.py', + }, + 'metadata:inspire': { + 'enabled': 'true', + 'languages_supported': 'eng,gre', + 'default_language': 'eng', + 'date': 'YYYY-MM-DD', + 'gemet_keywords': 'Utility and governmental services', + 'conformity_service': 'notEvaluated', + 'contact_name': 'Organization Name', + 'contact_email': 'Email Address', + 'temp_extent': 'YYYY-MM-DD/YYYY-MM-DD', + } + } } } From 9c69afce69663fa945584e8140bce15a27981dde Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 18:35:42 +0000 Subject: [PATCH 12/60] set .local from config, not per backend --- geonode/catalogue/backends/generic.py | 1 + geonode/catalogue/backends/pycsw.py | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 526d92f77f6..6d6403fbcfb 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -42,6 +42,7 @@ def __init__(self, *args, **kwargs): self.url = kwargs['URL'] self.user = kwargs['USER'] self.password = kwargs['PASSWORD'] + self.local = kwargs['LOCAL'] self._group_ids = {} self._operation_ids = {} self.connected = False diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py index 16f9f20d917..94b3ff29e4c 100644 --- a/geonode/catalogue/backends/pycsw.py +++ b/geonode/catalogue/backends/pycsw.py @@ -25,7 +25,3 @@ def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.type = 'pycsw' self.catalogue.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211'] - - self.catalogue.local = True - if not settings.CATALOGUE['default']['LOCAL']: - self.catalogue.local = False From da94b77372c53bab874b4c2f3a62037963319b6e Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 23:34:07 +0000 Subject: [PATCH 13/60] move pycsw settings to settings.PYCSW --- geonode/settings.py | 116 ++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/geonode/settings.py b/geonode/settings.py index 0a1dcdea94b..256cf0c1fc8 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -318,64 +318,66 @@ def get_user_url(u): # login credentials (for GeoNetwork) 'USER': 'admin', 'PASSWORD': 'admin', + } +} - # is pycsw inline to GeoNode and tightly coupled? - 'LOCAL': True, - - # pycsw configuration - 'CONFIGURATION': { - 'server': { - 'home': '.', - 'encoding': 'UTF-8', - 'language': LANGUAGE_CODE, - 'maxrecords': '10', - #'loglevel': 'DEBUG', - #'logfile': '/tmp/pycsw.log', - #'federatedcatalogues': 'http://geo.data.gov/geoportal/csw/discovery', - #'pretty_print': 'true', - #'domainquerytype': 'range', - #'domaincounts': 'true', - 'profiles': 'apiso,atom,dif,ebrim,fgdc', - }, - 'metadata:main': { - 'identification_title': '%s Catalogue' % SITENAME, - 'identification_abstract': 'GeoNode is an open source platform that facilitates the creation, sharing, and collaborative use of geospatial data', - 'identification_keywords': '%s,sdi,catalogue,discovery,metadata,GeoNode' % SITENAME, - 'identification_keywords_type': 'theme', - 'identification_fees': 'None', - 'identification_accessconstraints': 'None', - 'provider_name': 'Organization Name', - 'provider_url': SITEURL, - 'contact_name': 'Lastname, Firstname', - 'contact_position': 'Position Title', - 'contact_address': 'Mailing Address', - 'contact_city': 'City', - 'contact_stateorprovince': 'Administrative Area', - 'contact_postalcode': 'Zip or Postal Code', - 'contact_country': 'Country', - 'contact_phone': '+xx-xxx-xxx-xxxx', - 'contact_fax': '+xx-xxx-xxx-xxxx', - 'contact_email': 'Email Address', - 'contact_url': 'Contact URL', - 'contact_hours': 'Hours of Service', - 'contact_instructions': 'During hours of service. Off on weekends.', - 'contact_role': 'pointOfContact', - }, - 'repository': { - 'source': 'geonode', - 'mappings': 'geonode/catalogue/backends/pycsw_mappings.py', - }, - 'metadata:inspire': { - 'enabled': 'true', - 'languages_supported': 'eng,gre', - 'default_language': 'eng', - 'date': 'YYYY-MM-DD', - 'gemet_keywords': 'Utility and governmental services', - 'conformity_service': 'notEvaluated', - 'contact_name': 'Organization Name', - 'contact_email': 'Email Address', - 'temp_extent': 'YYYY-MM-DD/YYYY-MM-DD', - } +# pycsw settings +PYCSW = { + # is pycsw inline to GeoNode and tightly coupled? + 'LOCAL': True, + # pycsw configuration + 'CONFIGURATION': { + 'server': { + 'home': '.', + 'encoding': 'UTF-8', + 'language': LANGUAGE_CODE, + 'maxrecords': '10', + #'loglevel': 'DEBUG', + #'logfile': '/tmp/pycsw.log', + #'federatedcatalogues': 'http://geo.data.gov/geoportal/csw/discovery', + #'pretty_print': 'true', + #'domainquerytype': 'range', + #'domaincounts': 'true', + 'profiles': 'apiso,atom,dif,ebrim,fgdc', + }, + 'metadata:main': { + 'identification_title': '%s Catalogue' % SITENAME, + 'identification_abstract': 'GeoNode is an open source platform that facilitates the creation, sharing, and collaborative use of geospatial data', + 'identification_keywords': '%s,sdi,catalogue,discovery,metadata,GeoNode' % SITENAME, + 'identification_keywords_type': 'theme', + 'identification_fees': 'None', + 'identification_accessconstraints': 'None', + 'provider_name': 'Organization Name', + 'provider_url': SITEURL, + 'contact_name': 'Lastname, Firstname', + 'contact_position': 'Position Title', + 'contact_address': 'Mailing Address', + 'contact_city': 'City', + 'contact_stateorprovince': 'Administrative Area', + 'contact_postalcode': 'Zip or Postal Code', + 'contact_country': 'Country', + 'contact_phone': '+xx-xxx-xxx-xxxx', + 'contact_fax': '+xx-xxx-xxx-xxxx', + 'contact_email': 'Email Address', + 'contact_url': 'Contact URL', + 'contact_hours': 'Hours of Service', + 'contact_instructions': 'During hours of service. Off on weekends.', + 'contact_role': 'pointOfContact', + }, + 'repository': { + 'source': 'geonode', + 'mappings': 'geonode/catalogue/backends/pycsw_mappings.py', + }, + 'metadata:inspire': { + 'enabled': 'true', + 'languages_supported': 'eng,gre', + 'default_language': 'eng', + 'date': 'YYYY-MM-DD', + 'gemet_keywords': 'Utility and governmental services', + 'conformity_service': 'notEvaluated', + 'contact_name': 'Organization Name', + 'contact_email': 'Email Address', + 'temp_extent': 'YYYY-MM-DD/YYYY-MM-DD', } } } From a725fcba6a25e3547699810c69eb8d6837aa1f13 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 23:34:58 +0000 Subject: [PATCH 14/60] refactor checking for local repo --- geonode/catalogue/backends/generic.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 6d6403fbcfb..0a8277453bf 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -42,7 +42,6 @@ def __init__(self, *args, **kwargs): self.url = kwargs['URL'] self.user = kwargs['USER'] self.password = kwargs['PASSWORD'] - self.local = kwargs['LOCAL'] self._group_ids = {} self._operation_ids = {} self.connected = False @@ -356,6 +355,16 @@ def extract_links(self, rec): pass return links + def is_local_repo(self): + # figure out whether the repository that the catalogue + # is working off is local (GeoNode DB/Django model), + # or not (native to the Catalogue impl) + is_local = False + if self.catalogue.type == 'pycsw': + if settings.PYCSW['LOCAL']: + is_local = True + return is_local + class CatalogueBackend(BaseCatalogueBackend): def __init__(self, *args, **kwargs): self.catalogue = Catalogue(*args, **kwargs) @@ -386,7 +395,7 @@ def search_records(self, keywords, start, limit, bbox): return result def remove_record(self, uuid): - if self.catalogue.type != 'pycsw' and not self.catalogue.local: + if not self.is_local_repo(): with self.catalogue: catalogue_record = self.catalogue.get_by_uuid(uuid) if catalogue_record is None: @@ -402,7 +411,7 @@ def remove_record(self, uuid): 'during cleanup()') def create_record(self, item): - if self.catalogue.type != 'pycsw' and not self.catalogue.local: + if not self.is_local_repo(): with self.catalogue: record = self.catalogue.get_by_uuid(item.uuid) if record is None: From 33ce9b2b4d7d720c3750e5057bb1aecd66a6e3ba Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 23:35:40 +0000 Subject: [PATCH 15/60] s/settings.CATALOGUE/settings.PYCSW/ --- geonode/catalogue/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geonode/catalogue/views.py b/geonode/catalogue/views.py index 6baed329cb4..5bae8973642 100644 --- a/geonode/catalogue/views.py +++ b/geonode/catalogue/views.py @@ -34,7 +34,7 @@ def csw_global_dispatch(request): # serialize settings.CSW into SafeConfigParser # object for interaction with pycsw config = SafeConfigParser() - for section, options in settings.CATALOGUE.iteritems(): + for section, options in settings.PYCSW['CONFIGURATION'].iteritems(): config.add_section(section) for k, v in options.iteritems(): config.set(section, k, v) @@ -73,7 +73,7 @@ def csw_local_dispatch(request): # set up configuration config = SafeConfigParser() - for section, options in settings.CATALOGUE.iteritems(): + for section, options in settings.PYCSW['CONFIGURATION'].iteritems(): config.add_section(section) for option, value in options.iteritems(): config.set(section, option, value) From beb27d886077961e17ed773fe603ade9df594d29 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 11 Aug 2012 23:54:04 +0000 Subject: [PATCH 16/60] fix ref --- geonode/catalogue/backends/generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 0a8277453bf..d3d79deea42 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -395,7 +395,7 @@ def search_records(self, keywords, start, limit, bbox): return result def remove_record(self, uuid): - if not self.is_local_repo(): + if not self.catalogue.is_local_repo(): with self.catalogue: catalogue_record = self.catalogue.get_by_uuid(uuid) if catalogue_record is None: @@ -411,7 +411,7 @@ def remove_record(self, uuid): 'during cleanup()') def create_record(self, item): - if not self.is_local_repo(): + if not self.catalogue.is_local_repo(): with self.catalogue: record = self.catalogue.get_by_uuid(item.uuid) if record is None: From ada015c9fd608661474125a38ff0bcc1871c7c2d Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sun, 12 Aug 2012 01:48:45 +0000 Subject: [PATCH 17/60] tighten is_local_repo function --- geonode/catalogue/backends/generic.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index d3d79deea42..b17008e1223 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -359,11 +359,10 @@ def is_local_repo(self): # figure out whether the repository that the catalogue # is working off is local (GeoNode DB/Django model), # or not (native to the Catalogue impl) - is_local = False if self.catalogue.type == 'pycsw': if settings.PYCSW['LOCAL']: - is_local = True - return is_local + return True + return False class CatalogueBackend(BaseCatalogueBackend): def __init__(self, *args, **kwargs): From 45cfc691d9c2f8995d94219f81f3ac42e6fdf29a Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sun, 12 Aug 2012 02:11:54 +0000 Subject: [PATCH 18/60] put mappings in backend --- geonode/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/settings.py b/geonode/settings.py index 256cf0c1fc8..62a1f4ce11f 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -366,7 +366,7 @@ def get_user_url(u): }, 'repository': { 'source': 'geonode', - 'mappings': 'geonode/catalogue/backends/pycsw_mappings.py', + 'mappings': 'geonode/catalogue/backends/pycsw.py', }, 'metadata:inspire': { 'enabled': 'true', From 600d1598a747b85f2d6a61b08b38288643a67c55 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sun, 12 Aug 2012 02:12:16 +0000 Subject: [PATCH 19/60] put mappings in backend --- geonode/catalogue/backends/pycsw.py | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py index 94b3ff29e4c..795345e308e 100644 --- a/geonode/catalogue/backends/pycsw.py +++ b/geonode/catalogue/backends/pycsw.py @@ -25,3 +25,66 @@ def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.type = 'pycsw' self.catalogue.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211'] + +MD_CORE_MODEL = { + 'typename': 'pycsw:CoreMetadata', + 'outputschema': 'http://pycsw.org/metadata', + 'mappings': { + 'pycsw:Identifier': 'uuid', + 'pycsw:Typename': 'csw_typename', + 'pycsw:Schema': 'csw_schema', + 'pycsw:MdSource': 'csw_mdsource', + 'pycsw:InsertDate': 'csw_insert_date', + 'pycsw:XML': 'metadata_xml', + 'pycsw:AnyText': 'csw_anytext', + 'pycsw:Language': 'language', + 'pycsw:Title': 'title', + 'pycsw:Abstract': 'abstract', + 'pycsw:Keywords': 'keywords', + 'pycsw:KeywordType': 'keywordstype', + 'pycsw:Format': 'spatial_representation_type', + 'pycsw:Source': 'source', + 'pycsw:Date': 'date', + 'pycsw:Modified': 'last_modified', + 'pycsw:Type': 'csw_type', + 'pycsw:BoundingBox': 'geographic_bounding_box', + 'pycsw:CRS': 'crs', + 'pycsw:AlternateTitle': 'title_alternate', + 'pycsw:RevisionDate': 'date_revision', + 'pycsw:CreationDate': 'last_modified', + 'pycsw:PublicationDate': 'date_publication', + 'pycsw:OrganizationName': 'uuid', + 'pycsw:SecurityConstraints': 'securityconstraints', + 'pycsw:ParentIdentifier': 'parentidentifier', + 'pycsw:TopicCategory': 'topicategory', + 'pycsw:ResourceLanguage': 'resourcelanguage', + 'pycsw:GeographicDescriptionCode': 'geodescode', + 'pycsw:Denominator': 'denominator', + 'pycsw:DistanceValue': 'distancevalue', + 'pycsw:DistanceUOM': 'distanceuom', + 'pycsw:TempExtent_begin': 'temporal_extent_start', + 'pycsw:TempExtent_end': 'temporal_extent_end', + 'pycsw:ServiceType': 'servicetype', + 'pycsw:ServiceTypeVersion': 'servicetypeversion', + 'pycsw:Operation': 'operation', + 'pycsw:CouplingType': 'couplingtype', + 'pycsw:OperatesOn': 'operateson', + 'pycsw:OperatesOnIdentifier': 'operatesonidentifier', + 'pycsw:OperatesOnName': 'operatesoname', + 'pycsw:Degree': 'degree', + 'pycsw:AccessConstraints': 'accessconstraints', + 'pycsw:OtherConstraints': 'otherconstraints', + 'pycsw:Classification': 'classification', + 'pycsw:ConditionApplyingToAccessAndUse': 'conditionapplyingtoaccessanduse', + 'pycsw:Lineage': 'lineage', + 'pycsw:ResponsiblePartyRole': 'responsiblepartyrole', + 'pycsw:SpecificationTitle': 'specificationtitle', + 'pycsw:SpecificationDate': 'specificationdate', + 'pycsw:SpecificationDateType': 'specificationdatetype', + 'pycsw:Creator': 'creator', + 'pycsw:Publisher': 'publisher', + 'pycsw:Contributor': 'contributor', + 'pycsw:Relation': 'relation', + 'pycsw:Links': 'download_links', + } +} From dbd82127549419a9587c2e68079093dff0d6529c Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sun, 12 Aug 2012 03:28:20 +0000 Subject: [PATCH 20/60] keep geonode.catalogue optional for now --- geonode/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/settings.py b/geonode/settings.py index 62a1f4ce11f..d3ea3d0f535 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -142,7 +142,7 @@ 'geonode.people', 'geonode.proxy', 'geonode.security', - 'geonode.catalogue', +# 'geonode.catalogue', ) LOGGING = { 'version': 1, From 23c56a561b39496f62f0efb6e69bd128ee87bb03 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sun, 12 Aug 2012 11:57:11 +0000 Subject: [PATCH 21/60] undo this until pushing to dev --- geonode/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/settings.py b/geonode/settings.py index d3ea3d0f535..62a1f4ce11f 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -142,7 +142,7 @@ 'geonode.people', 'geonode.proxy', 'geonode.security', -# 'geonode.catalogue', + 'geonode.catalogue', ) LOGGING = { 'version': 1, From 8b6b66f04b9d531d936d5533eb06185821f4c272 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sun, 12 Aug 2012 15:18:41 +0000 Subject: [PATCH 22/60] fix ref --- geonode/catalogue/backends/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index b17008e1223..b18c119b216 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -359,7 +359,7 @@ def is_local_repo(self): # figure out whether the repository that the catalogue # is working off is local (GeoNode DB/Django model), # or not (native to the Catalogue impl) - if self.catalogue.type == 'pycsw': + if self.type == 'pycsw': if settings.PYCSW['LOCAL']: return True return False From 001540cbf37f4efcaf7c8b333fa7179dac20b9be Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sun, 12 Aug 2012 17:46:17 +0000 Subject: [PATCH 23/60] add pycsw to default componenets --- geonode/templates/developer.html | 1 + 1 file changed, 1 insertion(+) diff --git a/geonode/templates/developer.html b/geonode/templates/developer.html index eedf0813fdc..8e4462e0a1b 100644 --- a/geonode/templates/developer.html +++ b/geonode/templates/developer.html @@ -21,6 +21,7 @@

GeoNode Software

  • OpenLayers - Pure JavaScript library powering the maps of GeoExt
  • GeoWebCache - Cache engine for WMS Tiles
  • GeoServer - Standards based server for geospatial information
  • +
  • pycsw - CSW metadata catalogue
  • What are OGC Services?

    From 226f32a5f257b5339062bd12a2b905a3e1ef6ed7 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Mon, 13 Aug 2012 15:50:34 +0000 Subject: [PATCH 24/60] set self.catalogue.type automagically --- geonode/catalogue/backends/generic.py | 1 + geonode/catalogue/backends/geonetwork.py | 1 - geonode/catalogue/backends/pycsw.py | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index b18c119b216..21b8401a171 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -42,6 +42,7 @@ def __init__(self, *args, **kwargs): self.url = kwargs['URL'] self.user = kwargs['USER'] self.password = kwargs['PASSWORD'] + self.type = kwargs['ENGINE'].split('.')[-1] self._group_ids = {} self._operation_ids = {} self.connected = False diff --git a/geonode/catalogue/backends/geonetwork.py b/geonode/catalogue/backends/geonetwork.py index 02d1b757d20..027eac27a60 100644 --- a/geonode/catalogue/backends/geonetwork.py +++ b/geonode/catalogue/backends/geonetwork.py @@ -22,5 +22,4 @@ class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) - self.catalogue.type = 'geonetwork' self.catalogue.formats = ['Dublin Core', 'TC211'] diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py index 795345e308e..3b91823ba98 100644 --- a/geonode/catalogue/backends/pycsw.py +++ b/geonode/catalogue/backends/pycsw.py @@ -23,7 +23,6 @@ class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) - self.catalogue.type = 'pycsw' self.catalogue.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211'] MD_CORE_MODEL = { From d50904230c165404506d7ca8e7b2aef1230a9080 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Tue, 14 Aug 2012 13:39:32 +0000 Subject: [PATCH 25/60] add resource-base branch --- geonode/layers/fixtures/map_data.json | 47 ++++++--- geonode/layers/models.py | 135 ++++++++++++++------------ 2 files changed, 108 insertions(+), 74 deletions(-) diff --git a/geonode/layers/fixtures/map_data.json b/geonode/layers/fixtures/map_data.json index e4ea4c1b921..c416078211b 100644 --- a/geonode/layers/fixtures/map_data.json +++ b/geonode/layers/fixtures/map_data.json @@ -35,22 +35,46 @@ "date_joined": "2010-06-10 16:58:18" } }, - - { + "pk": 1, + "model": "layers.resourcebase", + "fields": { + "constraints_other": "Test", + "bbox_x0": "1", + "bbox_y1": "1", + "date_type": "publication", + "srid": "EPSG:4326", + "bbox_x1": "1", + "edition": "Test", + "distribution_url": "Test", + "spatial_representation_type": "grid", + "uuid": "", + "title": "Test", + "abstract": "Test", + "bbox_y0": "1", + "distribution_description": "Test", + "topic_category": "location", + "purpose": "Test", + "date": "2012-08-09T06:02:10", + "temporal_extent_end": "2012-08-09", + "data_quality_statement": "Test", + "language": "eng", + "keywords_region": "USA", + "maintenance_frequency": "annually", + "supplemental_information": "No information provided", + "temporal_extent_start": "2012-08-09", + "constraints_use": "copyright" + } + }, + { + "pk": 1, + "model": "layers.layer", "fields": { "typename": "base:CA", "store": "CA", - "name": "CA", "workspace": "base", - "uuid": "254afb8e-5a5f-4c1f-b01b-40af91532298", - "bbox_x0": 0, - "bbox_x1": 1, - "bbox_y0": 0, - "bbox_y1": 1 - }, - "model": "layers.layer", - "pk": 1 + "name": "CA" + } }, { "fields": { @@ -207,3 +231,4 @@ "pk": 1 } ] + diff --git a/geonode/layers/models.py b/geonode/layers/models.py index 2cf32edfdd9..acd7f531298 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -130,25 +130,17 @@ def slurp(self, ignore_errors=True, verbosity=1, console=sys.stdout, owner=None) print >> console, msg return output -class Layer(models.Model, PermissionLevelMixin): +class ResourceBase(models.Model, PermissionLevelMixin): """ - Layer Object loosely based on ISO 19115:2003 + Base Resource Object loosely based on ISO 19115:2003 """ VALID_DATE_TYPES = [(x.lower(), _(x)) for x in ['Creation', 'Publication', 'Revision']] # internal fields - objects = LayerManager() - workspace = models.CharField(max_length=128) - store = models.CharField(max_length=128) - storeType = models.CharField(max_length=128) - name = models.CharField(max_length=128) uuid = models.CharField(max_length=36) - typename = models.CharField(max_length=128, unique=True) owner = models.ForeignKey(User, blank=True, null=True) - contacts = models.ManyToManyField(Contact, through='ContactRole') - # section 1 title = models.CharField(_('title'), max_length=255) date = models.DateTimeField(_('date'), default = datetime.now) # passing the method itself, not the result @@ -210,7 +202,6 @@ def bbox_string(self): def geographic_bounding_box(self): return bbox_to_wkt(self.bbox_x0, self.bbox_x1, self.bbox_y0, self.bbox_y1, srid=self.srid ) - def eval_keywords_region(self): """Returns expanded keywords_region tuple'd value""" index = next((i for i,(k,v) in enumerate(COUNTRIES) if k==self.keywords_region),None) @@ -219,8 +210,36 @@ def eval_keywords_region(self): else: return self.keywords_region + @property + def poc_role(self): + role = Role.objects.get(value='pointOfContact') + return role + + @property + def metadata_author_role(self): + role = Role.objects.get(value='author') + return role + + def keyword_list(self): + return [kw.name for kw in self.keywords.all()] + +class Layer(ResourceBase): + """ + Layer (inherits ResourceBase fields) + """ + + # internal fields + objects = LayerManager() + workspace = models.CharField(max_length=128) + store = models.CharField(max_length=128) + storeType = models.CharField(max_length=128) + name = models.CharField(max_length=128) + typename = models.CharField(max_length=128, unique=True) + + contacts = models.ManyToManyField(Contact, through='ContactRole') + def thumbnail(self): - """ Generate a URL representing thumbnail of the resource """ + """ Generate a URL representing thumbnail of the layer """ width = 20 height = 20 @@ -239,16 +258,16 @@ def thumbnail(self): def verify(self): """Makes sure the state of the layer is consistent in GeoServer and Catalogue. """ - + # Check the layer is in the wms get capabilities record # FIXME: Implement caching of capabilities record site wide _local_wms = get_wms() record = _local_wms.contents.get(self.typename) if record is None: - msg = "WMS Record missing for layer [%s]" % self.typename + msg = "WMS Record missing for layer [%s]" % self.typename raise GeoNodeException(msg) - + @property def attribute_names(self): if self.storeType == "dataStore": @@ -331,7 +350,7 @@ def _set_styles(self, styles): self.publishing.styles = styles styles = property(_get_styles, _set_styles) - + @property def service_type(self): if self.storeType == 'coverageStore': @@ -346,15 +365,37 @@ def publishing(self): self._publishing_cache = cat.get_layer(self.name) return self._publishing_cache - @property - def poc_role(self): - role = Role.objects.get(value='pointOfContact') - return role + def get_absolute_url(self): + return "/data/%s" % (self.typename) - @property - def metadata_author_role(self): - role = Role.objects.get(value='author') - return role + def __str__(self): + return "%s Layer" % self.typename + + class Meta: + # custom permissions, + # change and delete are standard in django + permissions = (('view_layer', 'Can view'), + ('change_layer_permissions', "Can change permissions"), ) + + # Permission Level Constants + # LEVEL_NONE inherited + LEVEL_READ = 'layer_readonly' + LEVEL_WRITE = 'layer_readwrite' + LEVEL_ADMIN = 'layer_admin' + + def set_default_permissions(self): + self.set_gen_level(ANONYMOUS_USERS, self.LEVEL_READ) + self.set_gen_level(AUTHENTICATED_USERS, self.LEVEL_READ) + + # remove specific user permissions + current_perms = self.get_all_level_info() + for username in current_perms['users'].keys(): + user = User.objects.get(username=username) + self.set_user_level(user, self.LEVEL_NONE) + + # assign owner admin privs + if self.owner: + self.set_user_level(self.owner, self.LEVEL_ADMIN) def _set_poc(self, poc): # reset any poc asignation to this layer @@ -381,48 +422,12 @@ def _set_metadata_author(self, metadata_author): def _get_metadata_author(self): try: the_ma = ContactRole.objects.get(role=self.metadata_author_role, layer=self).contact - except ContactRole.DoesNotExist: + except ContactRole.DoesNotExist: the_ma = None return the_ma metadata_author = property(_get_metadata_author, _set_metadata_author) - - def keyword_list(self): - return [kw.name for kw in self.keywords.all()] - - def get_absolute_url(self): - return "/data/%s" % (self.typename) - - def __str__(self): - return "%s Layer" % self.typename - - class Meta: - # custom permissions, - # change and delete are standard in django - permissions = (('view_layer', 'Can view'), - ('change_layer_permissions', "Can change permissions"), ) - - # Permission Level Constants - # LEVEL_NONE inherited - LEVEL_READ = 'layer_readonly' - LEVEL_WRITE = 'layer_readwrite' - LEVEL_ADMIN = 'layer_admin' - - def set_default_permissions(self): - self.set_gen_level(ANONYMOUS_USERS, self.LEVEL_READ) - self.set_gen_level(AUTHENTICATED_USERS, self.LEVEL_READ) - - # remove specific user permissions - current_perms = self.get_all_level_info() - for username in current_perms['users'].keys(): - user = User.objects.get(username=username) - self.set_user_level(user, self.LEVEL_NONE) - - # assign owner admin privs - if self.owner: - self.set_user_level(self.owner, self.LEVEL_ADMIN) - class ContactRole(models.Model): """ ContactRole is an intermediate model to bind Contacts and Layers and apply roles. @@ -454,7 +459,6 @@ def clean(self): class Meta: unique_together = (("contact", "layer", "role"),) - class LinkManager(models.Manager): """Helper class to access links grouped by type """ @@ -493,7 +497,7 @@ class Link(models.Model): link_type = models.CharField(max_length=255, choices = [(x, x) for x in LINK_TYPES]) name = models.CharField(max_length=255, help_text='For example "View in Google Earth"') mime = models.CharField(max_length=255, help_text='For example "text/xml"') - url = models.TextField(unique=True) + url = models.URLField(unique=True) objects = LinkManager() @@ -579,6 +583,8 @@ def geoserver_pre_save(instance, sender, **kwargs): * SRID * Download links (WMS, WCS or WFS and KML) """ + gs_resource = gs_catalog.get_resource(instance.name) + bbox = gs_resource.latlon_bbox #FIXME(Ariel): Correct srid setting below @@ -592,6 +598,8 @@ def geoserver_pre_save(instance, sender, **kwargs): instance.bbox_y1 = bbox[3] + + def geoserver_post_save(instance, sender, **kwargs): """Save keywords to GeoServer @@ -709,3 +717,4 @@ def geoserver_post_save(instance, sender, **kwargs): signals.pre_save.connect(geoserver_pre_save, sender=Layer) signals.pre_delete.connect(geoserver_pre_delete, sender=Layer) signals.post_save.connect(geoserver_post_save, sender=Layer) + From 7f69f3cc32606119acc83debab67022d2f666859 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Tue, 14 Aug 2012 13:40:47 +0000 Subject: [PATCH 26/60] add hierarchy levels --- geonode/layers/enumerations.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/geonode/layers/enumerations.py b/geonode/layers/enumerations.py index bbe29e0158b..d2474df2127 100644 --- a/geonode/layers/enumerations.py +++ b/geonode/layers/enumerations.py @@ -20,6 +20,25 @@ from django.utils.translation import ugettext_lazy as _ +HIERARCHY_LEVELS = [ + 'series', + 'software', + 'featureType', + 'model', + 'collectionHardware', + 'collectionSession', + 'nonGeographicDataset', + 'propertyType', + 'fieldSession', + 'dataset', + 'service', + 'attribute', + 'attributeType', + 'tile', + 'feature', + 'dimensionGroup' +] + UPDATE_FREQUENCIES = [ 'annually', 'asNeeded', From 628d637564b736b54c2b6eda4abd327e880f603e Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Wed, 15 Aug 2012 19:27:45 +0000 Subject: [PATCH 27/60] add migration --- ...ctrole_layer__del_field_layer_geographi.py | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py diff --git a/geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py b/geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py new file mode 100644 index 00000000000..bbac2f3f745 --- /dev/null +++ b/geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py @@ -0,0 +1,200 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'Link' + db.create_table('layers_link', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('layer', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['layers.Layer'])), + ('extension', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('link_type', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('mime', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('url', self.gf('django.db.models.fields.URLField')(unique=True, max_length=200)), + )) + db.send_create_signal('layers', ['Link']) + + # Changing field 'ContactRole.layer' + db.alter_column('layers_contactrole', 'layer_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['layers.Layer'], null=True)) + + # Deleting field 'Layer.geographic_bounding_box' + db.delete_column('layers_layer', 'geographic_bounding_box') + + # Adding field 'Layer.bbox_x0' + db.add_column('layers_layer', 'bbox_x0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.bbox_x1' + db.add_column('layers_layer', 'bbox_x1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.bbox_y0' + db.add_column('layers_layer', 'bbox_y0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.bbox_y1' + db.add_column('layers_layer', 'bbox_y1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.srid' + db.add_column('layers_layer', 'srid', self.gf('django.db.models.fields.CharField')(default='EPSG:4326', max_length=255), keep_default=False) + + + def backwards(self, orm): + + # Deleting model 'Link' + db.delete_table('layers_link') + + # User chose to not deal with backwards NULL issues for 'ContactRole.layer' + raise RuntimeError("Cannot reverse this migration. 'ContactRole.layer' and its values cannot be restored.") + + # User chose to not deal with backwards NULL issues for 'Layer.geographic_bounding_box' + raise RuntimeError("Cannot reverse this migration. 'Layer.geographic_bounding_box' and its values cannot be restored.") + + # Deleting field 'Layer.bbox_x0' + db.delete_column('layers_layer', 'bbox_x0') + + # Deleting field 'Layer.bbox_x1' + db.delete_column('layers_layer', 'bbox_x1') + + # Deleting field 'Layer.bbox_y0' + db.delete_column('layers_layer', 'bbox_y0') + + # Deleting field 'Layer.bbox_y1' + db.delete_column('layers_layer', 'bbox_y1') + + # Deleting field 'Layer.srid' + db.delete_column('layers_layer', 'srid') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 8, 17, 49, 52, 872867)'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 8, 17, 49, 52, 872753)'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'layers.contactrole': { + 'Meta': {'unique_together': "(('contact', 'layer', 'role'),)", 'object_name': 'ContactRole'}, + 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Contact']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']", 'null': 'True'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Role']"}) + }, + 'layers.layer': { + 'Meta': {'object_name': 'Layer'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bbox_x0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'bbox_x1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'bbox_y0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'bbox_y1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'constraints_other': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'constraints_use': ('django.db.models.fields.CharField', [], {'default': "'copyright'", 'max_length': '255'}), + 'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['people.Contact']", 'through': "orm['layers.ContactRole']", 'symmetrical': 'False'}), + 'data_quality_statement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_type': ('django.db.models.fields.CharField', [], {'default': "'publication'", 'max_length': '255'}), + 'distribution_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'distribution_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'edition': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'keywords_region': ('django.db.models.fields.CharField', [], {'default': "'USA'", 'max_length': '3'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "'eng'", 'max_length': '3'}), + 'maintenance_frequency': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'purpose': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'spatial_representation_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'srid': ('django.db.models.fields.CharField', [], {'default': "'EPSG:4326'", 'max_length': '255'}), + 'store': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'storeType': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'supplemental_information': ('django.db.models.fields.TextField', [], {'default': "u'No information provided'"}), + 'temporal_extent_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'temporal_extent_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'topic_category': ('django.db.models.fields.CharField', [], {'default': "'location'", 'max_length': '255'}), + 'typename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36'}), + 'workspace': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'layers.link': { + 'Meta': {'object_name': 'Link'}, + 'extension': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']"}), + 'link_type': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'mime': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}) + }, + 'people.contact': { + 'Meta': {'object_name': 'Contact'}, + 'area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'delivery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'organization': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'position': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'profile': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'voice': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'zipcode': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}) + }, + 'people.role': { + 'Meta': {'object_name': 'Role'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'value': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['layers'] From b69bc38f5b8dea857b72c5a0d017c38344dbf628 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Wed, 15 Aug 2012 19:39:16 +0000 Subject: [PATCH 28/60] add migration for this branch --- ...ld_layer_bbox_y0__del_field_layer_bbox_.py | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py diff --git a/geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py b/geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py new file mode 100644 index 00000000000..ac630546c19 --- /dev/null +++ b/geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py @@ -0,0 +1,350 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'ResourceBase' + db.create_table('layers_resourcebase', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('uuid', self.gf('django.db.models.fields.CharField')(max_length=36)), + ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('date_type', self.gf('django.db.models.fields.CharField')(default='publication', max_length=255)), + ('edition', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('abstract', self.gf('django.db.models.fields.TextField')(blank=True)), + ('purpose', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('maintenance_frequency', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('keywords_region', self.gf('django.db.models.fields.CharField')(default='USA', max_length=3)), + ('constraints_use', self.gf('django.db.models.fields.CharField')(default='copyright', max_length=255)), + ('constraints_other', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('spatial_representation_type', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('language', self.gf('django.db.models.fields.CharField')(default='eng', max_length=3)), + ('topic_category', self.gf('django.db.models.fields.CharField')(default='location', max_length=255)), + ('temporal_extent_start', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), + ('temporal_extent_end', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), + ('supplemental_information', self.gf('django.db.models.fields.TextField')(default=u'No information provided')), + ('distribution_url', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('distribution_description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('data_quality_statement', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('bbox_x0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), + ('bbox_x1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), + ('bbox_y0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), + ('bbox_y1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), + ('srid', self.gf('django.db.models.fields.CharField')(default='EPSG:4326', max_length=255)), + )) + db.send_create_signal('layers', ['ResourceBase']) + + # Deleting field 'Layer.bbox_y0' + db.delete_column('layers_layer', 'bbox_y0') + + # Deleting field 'Layer.bbox_y1' + db.delete_column('layers_layer', 'bbox_y1') + + # Deleting field 'Layer.bbox_x1' + db.delete_column('layers_layer', 'bbox_x1') + + # Deleting field 'Layer.bbox_x0' + db.delete_column('layers_layer', 'bbox_x0') + + # Deleting field 'Layer.topic_category' + db.delete_column('layers_layer', 'topic_category') + + # Deleting field 'Layer.distribution_description' + db.delete_column('layers_layer', 'distribution_description') + + # Deleting field 'Layer.temporal_extent_end' + db.delete_column('layers_layer', 'temporal_extent_end') + + # Deleting field 'Layer.abstract' + db.delete_column('layers_layer', 'abstract') + + # Deleting field 'Layer.srid' + db.delete_column('layers_layer', 'srid') + + # Deleting field 'Layer.constraints_other' + db.delete_column('layers_layer', 'constraints_other') + + # Deleting field 'Layer.edition' + db.delete_column('layers_layer', 'edition') + + # Deleting field 'Layer.purpose' + db.delete_column('layers_layer', 'purpose') + + # Deleting field 'Layer.date' + db.delete_column('layers_layer', 'date') + + # Deleting field 'Layer.id' + db.delete_column('layers_layer', 'id') + + # Deleting field 'Layer.distribution_url' + db.delete_column('layers_layer', 'distribution_url') + + # Deleting field 'Layer.uuid' + db.delete_column('layers_layer', 'uuid') + + # Deleting field 'Layer.spatial_representation_type' + db.delete_column('layers_layer', 'spatial_representation_type') + + # Deleting field 'Layer.language' + db.delete_column('layers_layer', 'language') + + # Deleting field 'Layer.data_quality_statement' + db.delete_column('layers_layer', 'data_quality_statement') + + # Deleting field 'Layer.keywords_region' + db.delete_column('layers_layer', 'keywords_region') + + # Deleting field 'Layer.maintenance_frequency' + db.delete_column('layers_layer', 'maintenance_frequency') + + # Deleting field 'Layer.supplemental_information' + db.delete_column('layers_layer', 'supplemental_information') + + # Deleting field 'Layer.owner' + db.delete_column('layers_layer', 'owner_id') + + # Deleting field 'Layer.temporal_extent_start' + db.delete_column('layers_layer', 'temporal_extent_start') + + # Deleting field 'Layer.title' + db.delete_column('layers_layer', 'title') + + # Deleting field 'Layer.date_type' + db.delete_column('layers_layer', 'date_type') + + # Deleting field 'Layer.constraints_use' + db.delete_column('layers_layer', 'constraints_use') + + # Adding field 'Layer.resourcebase_ptr' + db.add_column('layers_layer', 'resourcebase_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=-1, to=orm['layers.ResourceBase'], unique=True, primary_key=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting model 'ResourceBase' + db.delete_table('layers_resourcebase') + + # Adding field 'Layer.bbox_y0' + db.add_column('layers_layer', 'bbox_y0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.bbox_y1' + db.add_column('layers_layer', 'bbox_y1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.bbox_x1' + db.add_column('layers_layer', 'bbox_x1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.bbox_x0' + db.add_column('layers_layer', 'bbox_x0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) + + # Adding field 'Layer.topic_category' + db.add_column('layers_layer', 'topic_category', self.gf('django.db.models.fields.CharField')(default='location', max_length=255), keep_default=False) + + # Adding field 'Layer.distribution_description' + db.add_column('layers_layer', 'distribution_description', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) + + # Adding field 'Layer.temporal_extent_end' + db.add_column('layers_layer', 'temporal_extent_end', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default=False) + + # Adding field 'Layer.abstract' + db.add_column('layers_layer', 'abstract', self.gf('django.db.models.fields.TextField')(default='', blank=True), keep_default=False) + + # Adding field 'Layer.srid' + db.add_column('layers_layer', 'srid', self.gf('django.db.models.fields.CharField')(default='EPSG:4326', max_length=255), keep_default=False) + + # Adding field 'Layer.constraints_other' + db.add_column('layers_layer', 'constraints_other', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) + + # Adding field 'Layer.edition' + db.add_column('layers_layer', 'edition', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) + + # Adding field 'Layer.purpose' + db.add_column('layers_layer', 'purpose', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) + + # Adding field 'Layer.date' + db.add_column('layers_layer', 'date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now), keep_default=False) + + # User chose to not deal with backwards NULL issues for 'Layer.id' + raise RuntimeError("Cannot reverse this migration. 'Layer.id' and its values cannot be restored.") + + # Adding field 'Layer.distribution_url' + db.add_column('layers_layer', 'distribution_url', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) + + # User chose to not deal with backwards NULL issues for 'Layer.uuid' + raise RuntimeError("Cannot reverse this migration. 'Layer.uuid' and its values cannot be restored.") + + # Adding field 'Layer.spatial_representation_type' + db.add_column('layers_layer', 'spatial_representation_type', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) + + # Adding field 'Layer.language' + db.add_column('layers_layer', 'language', self.gf('django.db.models.fields.CharField')(default='eng', max_length=3), keep_default=False) + + # Adding field 'Layer.data_quality_statement' + db.add_column('layers_layer', 'data_quality_statement', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) + + # Adding field 'Layer.keywords_region' + db.add_column('layers_layer', 'keywords_region', self.gf('django.db.models.fields.CharField')(default='USA', max_length=3), keep_default=False) + + # Adding field 'Layer.maintenance_frequency' + db.add_column('layers_layer', 'maintenance_frequency', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) + + # Adding field 'Layer.supplemental_information' + db.add_column('layers_layer', 'supplemental_information', self.gf('django.db.models.fields.TextField')(default=u'No information provided'), keep_default=False) + + # Adding field 'Layer.owner' + db.add_column('layers_layer', 'owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True), keep_default=False) + + # Adding field 'Layer.temporal_extent_start' + db.add_column('layers_layer', 'temporal_extent_start', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default=False) + + # User chose to not deal with backwards NULL issues for 'Layer.title' + raise RuntimeError("Cannot reverse this migration. 'Layer.title' and its values cannot be restored.") + + # Adding field 'Layer.date_type' + db.add_column('layers_layer', 'date_type', self.gf('django.db.models.fields.CharField')(default='publication', max_length=255), keep_default=False) + + # Adding field 'Layer.constraints_use' + db.add_column('layers_layer', 'constraints_use', self.gf('django.db.models.fields.CharField')(default='copyright', max_length=255), keep_default=False) + + # Deleting field 'Layer.resourcebase_ptr' + db.delete_column('layers_layer', 'resourcebase_ptr_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 14, 8, 41, 19, 927596)'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 14, 8, 41, 19, 927478)'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'layers.contactrole': { + 'Meta': {'unique_together': "(('contact', 'layer', 'role'),)", 'object_name': 'ContactRole'}, + 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Contact']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']", 'null': 'True'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Role']"}) + }, + 'layers.layer': { + 'Meta': {'object_name': 'Layer', '_ormbases': ['layers.ResourceBase']}, + 'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['people.Contact']", 'through': "orm['layers.ContactRole']", 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'resourcebase_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['layers.ResourceBase']", 'unique': 'True', 'primary_key': 'True'}), + 'store': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'storeType': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'typename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'workspace': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'layers.link': { + 'Meta': {'object_name': 'Link'}, + 'extension': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']"}), + 'link_type': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'mime': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}) + }, + 'layers.resourcebase': { + 'Meta': {'object_name': 'ResourceBase'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bbox_x0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'bbox_x1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'bbox_y0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'bbox_y1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), + 'constraints_other': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'constraints_use': ('django.db.models.fields.CharField', [], {'default': "'copyright'", 'max_length': '255'}), + 'data_quality_statement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_type': ('django.db.models.fields.CharField', [], {'default': "'publication'", 'max_length': '255'}), + 'distribution_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'distribution_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'edition': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'keywords_region': ('django.db.models.fields.CharField', [], {'default': "'USA'", 'max_length': '3'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "'eng'", 'max_length': '3'}), + 'maintenance_frequency': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'purpose': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'spatial_representation_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'srid': ('django.db.models.fields.CharField', [], {'default': "'EPSG:4326'", 'max_length': '255'}), + 'supplemental_information': ('django.db.models.fields.TextField', [], {'default': "u'No information provided'"}), + 'temporal_extent_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'temporal_extent_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'topic_category': ('django.db.models.fields.CharField', [], {'default': "'location'", 'max_length': '255'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36'}) + }, + 'people.contact': { + 'Meta': {'object_name': 'Contact'}, + 'area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'delivery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'organization': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'position': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'profile': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'voice': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'zipcode': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}) + }, + 'people.role': { + 'Meta': {'object_name': 'Role'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'value': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['layers'] From e534bb4de2aefb111d47ae412f829c8a547ac5d4 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Wed, 15 Aug 2012 19:40:10 +0000 Subject: [PATCH 29/60] add __init__.py --- geonode/layers/migrations/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 geonode/layers/migrations/__init__.py diff --git a/geonode/layers/migrations/__init__.py b/geonode/layers/migrations/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From bf373f8fa35127928a36a4e0b8e0fe989e1f3e8d Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Wed, 15 Aug 2012 23:21:48 +0000 Subject: [PATCH 30/60] fix typo --- geonode/catalogue/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/catalogue/__init__.py b/geonode/catalogue/__init__.py index 8448e4e0339..33f5d360ba3 100644 --- a/geonode/catalogue/__init__.py +++ b/geonode/catalogue/__init__.py @@ -18,7 +18,7 @@ ######################################################################### """ -Tools for managing a CatalogWebService (CSW) +Tools for managing a Catalogue Service for the Web (CSW) """ import os from django.conf import settings From 78103f0affcaa024750602fa150256e2d162f9ba Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 12:47:42 +0000 Subject: [PATCH 31/60] abstract XML generation --- geonode/catalogue/backends/generic.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 21b8401a171..24fab729d2f 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -122,8 +122,7 @@ def urls_for_uuid(self, uuid): urls.append(('text/xml', mformat, self.url_for_uuid(uuid, METADATA_FORMATS[mformat][1]))) return urls - def csw_request(self, layer, template): - + def csw_gen_xml(self, layer, template): id_pname = 'dc:identifier' if self.type == 'deegree': id_pname = 'apiso:Identifier' @@ -136,6 +135,11 @@ def csw_request(self, layer, template): }) md_doc = tpl.render(ctx) md_doc = md_doc.encode("utf-8") + return md_doc + + def csw_request(self, layer, template): + + md_doc = self.csw_gen_xml(layer, template) if self.type == 'geonetwork': headers = { From a3c989bce2e3cfa8a501d778d1ab7cf817563dff Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 12:51:42 +0000 Subject: [PATCH 32/60] add initial CSW fields (first pass) --- geonode/layers/models.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/geonode/layers/models.py b/geonode/layers/models.py index acd7f531298..b1bacc5658c 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -43,8 +43,9 @@ from geonode.security.models import AUTHENTICATED_USERS, ANONYMOUS_USERS from geonode.layers.ows import wcs_links, wfs_links, wms_links from geonode.layers.enumerations import COUNTRIES, ALL_LANGUAGES, \ - UPDATE_FREQUENCIES, CONSTRAINT_OPTIONS, SPATIAL_REPRESENTATION_TYPES, \ - TOPIC_CATEGORIES, DEFAULT_SUPPLEMENTAL_INFORMATION, LINK_TYPES + HIERARCHY_LEVELS, UPDATE_FREQUENCIES, CONSTRAINT_OPTIONS, \ + SPATIAL_REPRESENTATION_TYPES, TOPIC_CATEGORIES, \ + DEFAULT_SUPPLEMENTAL_INFORMATION, LINK_TYPES from geoserver.catalog import Catalog from taggit.managers import TaggableManager @@ -190,6 +191,26 @@ class ResourceBase(models.Model, PermissionLevelMixin): bbox_y1 = models.DecimalField(max_digits=19, decimal_places=10, blank=True, null=True) srid = models.CharField(max_length=255, default='EPSG:4326') + # CSW specific fields + csw_typename = models.CharField(_('CSW typename'), max_length=32, default='gmd:MD_Metadata', null=False) + #csw_schema = models.CharField(_('CSW schema'), max_length=64, default='http://www.isotc211.org/2005/gmd', null=False) + #csw_mdsource = models.CharField(_('CSW source'), max_length=256, default='local', null=False) + #csw_insert_date = models.DateTimeField(_('CSW insert date'), auto_now_add=True, null=True) + #csw_type = models.CharField(_('type'), max_length=32, default='dataset', null=False, choices=[(x, x) for x in HIERARCHY_LEVELS]) + #csw_anytext = models.TextField(_('anytext'), null=True) + + # metadata XML specific fields + #metadata_uploaded = models.BooleanField(default=False) + metadata_xml = models.TextField(null=True, default='', blank=True) + + @property + def keywords_csv(self): + keywords_qs = self.keywords.all() + if keywords_qs: + return ','.join([kw.name for kw in keywords_qs]) + else: + return '' + @property def bbox(self): return [self.bbox_x0, self.bbox_x1, self.bbox_y0, self.bbox_y1, self.srid] @@ -238,6 +259,15 @@ class Layer(ResourceBase): contacts = models.ManyToManyField(Contact, through='ContactRole') + def download_links(self): + links = [] + for url in self.link_set.all(): + description = '%s (%s Format)' % (self.title, url.name) + links.append((self.title, description, 'WWW:DOWNLOAD-1.0-http--download', url.url)) + abs_url = '%s%s' % (settings.SITEURL[:-1], self.get_absolute_url()) + links.append((self.title, self.title, 'WWW:LINK-1.0-http--link', abs_url)) + return links + def thumbnail(self): """ Generate a URL representing thumbnail of the layer """ From d70004141b9144d925640f22f69dca85b696cbbe Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 12:52:36 +0000 Subject: [PATCH 33/60] add pycsw_local catalogue backend --- geonode/catalogue/backends/pycsw_local.py | 96 +++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 geonode/catalogue/backends/pycsw_local.py diff --git a/geonode/catalogue/backends/pycsw_local.py b/geonode/catalogue/backends/pycsw_local.py new file mode 100644 index 00000000000..83d833e73d6 --- /dev/null +++ b/geonode/catalogue/backends/pycsw_local.py @@ -0,0 +1,96 @@ +######################################################################### +# +# Copyright (C) 2012 OpenPlans +# +# 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 . +# +######################################################################### + +from django.conf import settings +from geonode.catalogue.backends.generic import CatalogueBackend as GenericCatalogueBackend + +MD_CORE_MODEL = { + 'typename': 'pycsw:CoreMetadata', + 'outputschema': 'http://pycsw.org/metadata', + 'mappings': { + 'pycsw:Identifier': 'uuid', + 'pycsw:Typename': 'csw_typename', + 'pycsw:Schema': 'csw_schema', + 'pycsw:MdSource': 'csw_mdsource', + 'pycsw:InsertDate': 'csw_insert_date', + 'pycsw:XML': 'metadata_xml', + 'pycsw:AnyText': 'csw_anytext', + 'pycsw:Language': 'language', + 'pycsw:Title': 'title', + 'pycsw:Abstract': 'abstract', + 'pycsw:Keywords': 'keywords_csv', + 'pycsw:KeywordType': 'keywordstype', + 'pycsw:Format': 'spatial_representation_type', + 'pycsw:Source': 'source', + 'pycsw:Date': 'date', + 'pycsw:Modified': 'last_modified', + 'pycsw:Type': 'csw_type', + 'pycsw:BoundingBox': 'geographic_bounding_box', + 'pycsw:CRS': 'crs', + 'pycsw:AlternateTitle': 'title_alternate', + 'pycsw:RevisionDate': 'date_revision', + 'pycsw:CreationDate': 'last_modified', + 'pycsw:PublicationDate': 'date_publication', + 'pycsw:OrganizationName': 'uuid', + 'pycsw:SecurityConstraints': 'securityconstraints', + 'pycsw:ParentIdentifier': 'parentidentifier', + 'pycsw:TopicCategory': 'topicategory', + 'pycsw:ResourceLanguage': 'resourcelanguage', + 'pycsw:GeographicDescriptionCode': 'geodescode', + 'pycsw:Denominator': 'denominator', + 'pycsw:DistanceValue': 'distancevalue', + 'pycsw:DistanceUOM': 'distanceuom', + 'pycsw:TempExtent_begin': 'temporal_extent_start', + 'pycsw:TempExtent_end': 'temporal_extent_end', + 'pycsw:ServiceType': 'servicetype', + 'pycsw:ServiceTypeVersion': 'servicetypeversion', + 'pycsw:Operation': 'operation', + 'pycsw:CouplingType': 'couplingtype', + 'pycsw:OperatesOn': 'operateson', + 'pycsw:OperatesOnIdentifier': 'operatesonidentifier', + 'pycsw:OperatesOnName': 'operatesoname', + 'pycsw:Degree': 'degree', + 'pycsw:AccessConstraints': 'accessconstraints', + 'pycsw:OtherConstraints': 'otherconstraints', + 'pycsw:Classification': 'classification', + 'pycsw:ConditionApplyingToAccessAndUse': 'conditionapplyingtoaccessanduse', + 'pycsw:Lineage': 'lineage', + 'pycsw:ResponsiblePartyRole': 'responsiblepartyrole', + 'pycsw:SpecificationTitle': 'specificationtitle', + 'pycsw:SpecificationDate': 'specificationdate', + 'pycsw:SpecificationDateType': 'specificationdatetype', + 'pycsw:Creator': 'creator', + 'pycsw:Publisher': 'publisher', + 'pycsw:Contributor': 'contributor', + 'pycsw:Relation': 'relation', + 'pycsw:Links': 'download_links', + } +} + +class CatalogueBackend(GenericCatalogueBackend): + def __init__(self, *args, **kwargs): + super(CatalogueBackend, self).__init__(*args, **kwargs) + self.catalogue.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211'] + + def remove_record(self, uuid): + pass + + def create_record(self, item): + pass + From 0844b541d8235f3ad3b5b052eda1fb04ca619272 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 12:53:05 +0000 Subject: [PATCH 34/60] s/keywords/keywords_csv/ --- geonode/catalogue/backends/pycsw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py index 3b91823ba98..69bad0a3d97 100644 --- a/geonode/catalogue/backends/pycsw.py +++ b/geonode/catalogue/backends/pycsw.py @@ -39,7 +39,7 @@ def __init__(self, *args, **kwargs): 'pycsw:Language': 'language', 'pycsw:Title': 'title', 'pycsw:Abstract': 'abstract', - 'pycsw:Keywords': 'keywords', + 'pycsw:Keywords': 'keywords_csv', 'pycsw:KeywordType': 'keywordstype', 'pycsw:Format': 'spatial_representation_type', 'pycsw:Source': 'source', From 619442811db2a158243e7e00e8f1c74605cd0a28 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 13:14:28 +0000 Subject: [PATCH 35/60] break out pycsw as 2 backends (pycsw, pycsw_local) --- geonode/catalogue/backends/generic.py | 48 +++++++---------- geonode/catalogue/backends/pycsw.py | 63 ----------------------- geonode/catalogue/backends/pycsw_local.py | 2 +- 3 files changed, 20 insertions(+), 93 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 24fab729d2f..0369cf54cb5 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -43,6 +43,7 @@ def __init__(self, *args, **kwargs): self.user = kwargs['USER'] self.password = kwargs['PASSWORD'] self.type = kwargs['ENGINE'].split('.')[-1] + self.local = False self._group_ids = {} self._operation_ids = {} self.connected = False @@ -360,15 +361,6 @@ def extract_links(self, rec): pass return links - def is_local_repo(self): - # figure out whether the repository that the catalogue - # is working off is local (GeoNode DB/Django model), - # or not (native to the Catalogue impl) - if self.type == 'pycsw': - if settings.PYCSW['LOCAL']: - return True - return False - class CatalogueBackend(BaseCatalogueBackend): def __init__(self, *args, **kwargs): self.catalogue = Catalogue(*args, **kwargs) @@ -399,27 +391,25 @@ def search_records(self, keywords, start, limit, bbox): return result def remove_record(self, uuid): - if not self.catalogue.is_local_repo(): - with self.catalogue: - catalogue_record = self.catalogue.get_by_uuid(uuid) - if catalogue_record is None: - return + with self.catalogue: + catalogue_record = self.catalogue.get_by_uuid(uuid) + if catalogue_record is None: + return - try: - # this is a bit hacky, delete_layer expects an instance of the layer - # model but it just passes it to a Django template so a dict works - # too. - self.catalogue.delete_layer({ "uuid": uuid }) - except: - logger.exception('Couldn\'t delete Catalogue record ' + try: + # this is a bit hacky, delete_layer expects an instance of the layer + # model but it just passes it to a Django template so a dict works + # too. + self.catalogue.delete_layer({ "uuid": uuid }) + except: + logger.exception('Couldn\'t delete Catalogue record ' 'during cleanup()') def create_record(self, item): - if not self.catalogue.is_local_repo(): - with self.catalogue: - record = self.catalogue.get_by_uuid(item.uuid) - if record is None: - md_link = self.catalogue.create_from_layer(item) - item.metadata_links = [("text/xml", "TC211", md_link)] - else: - self.catalogue.update_layer(item) + with self.catalogue: + record = self.catalogue.get_by_uuid(item.uuid) + if record is None: + md_link = self.catalogue.create_from_layer(item) + item.metadata_links = [("text/xml", "TC211", md_link)] + else: + self.catalogue.update_layer(item) diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw.py index 69bad0a3d97..e86c971bcfc 100644 --- a/geonode/catalogue/backends/pycsw.py +++ b/geonode/catalogue/backends/pycsw.py @@ -24,66 +24,3 @@ class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211'] - -MD_CORE_MODEL = { - 'typename': 'pycsw:CoreMetadata', - 'outputschema': 'http://pycsw.org/metadata', - 'mappings': { - 'pycsw:Identifier': 'uuid', - 'pycsw:Typename': 'csw_typename', - 'pycsw:Schema': 'csw_schema', - 'pycsw:MdSource': 'csw_mdsource', - 'pycsw:InsertDate': 'csw_insert_date', - 'pycsw:XML': 'metadata_xml', - 'pycsw:AnyText': 'csw_anytext', - 'pycsw:Language': 'language', - 'pycsw:Title': 'title', - 'pycsw:Abstract': 'abstract', - 'pycsw:Keywords': 'keywords_csv', - 'pycsw:KeywordType': 'keywordstype', - 'pycsw:Format': 'spatial_representation_type', - 'pycsw:Source': 'source', - 'pycsw:Date': 'date', - 'pycsw:Modified': 'last_modified', - 'pycsw:Type': 'csw_type', - 'pycsw:BoundingBox': 'geographic_bounding_box', - 'pycsw:CRS': 'crs', - 'pycsw:AlternateTitle': 'title_alternate', - 'pycsw:RevisionDate': 'date_revision', - 'pycsw:CreationDate': 'last_modified', - 'pycsw:PublicationDate': 'date_publication', - 'pycsw:OrganizationName': 'uuid', - 'pycsw:SecurityConstraints': 'securityconstraints', - 'pycsw:ParentIdentifier': 'parentidentifier', - 'pycsw:TopicCategory': 'topicategory', - 'pycsw:ResourceLanguage': 'resourcelanguage', - 'pycsw:GeographicDescriptionCode': 'geodescode', - 'pycsw:Denominator': 'denominator', - 'pycsw:DistanceValue': 'distancevalue', - 'pycsw:DistanceUOM': 'distanceuom', - 'pycsw:TempExtent_begin': 'temporal_extent_start', - 'pycsw:TempExtent_end': 'temporal_extent_end', - 'pycsw:ServiceType': 'servicetype', - 'pycsw:ServiceTypeVersion': 'servicetypeversion', - 'pycsw:Operation': 'operation', - 'pycsw:CouplingType': 'couplingtype', - 'pycsw:OperatesOn': 'operateson', - 'pycsw:OperatesOnIdentifier': 'operatesonidentifier', - 'pycsw:OperatesOnName': 'operatesoname', - 'pycsw:Degree': 'degree', - 'pycsw:AccessConstraints': 'accessconstraints', - 'pycsw:OtherConstraints': 'otherconstraints', - 'pycsw:Classification': 'classification', - 'pycsw:ConditionApplyingToAccessAndUse': 'conditionapplyingtoaccessanduse', - 'pycsw:Lineage': 'lineage', - 'pycsw:ResponsiblePartyRole': 'responsiblepartyrole', - 'pycsw:SpecificationTitle': 'specificationtitle', - 'pycsw:SpecificationDate': 'specificationdate', - 'pycsw:SpecificationDateType': 'specificationdatetype', - 'pycsw:Creator': 'creator', - 'pycsw:Publisher': 'publisher', - 'pycsw:Contributor': 'contributor', - 'pycsw:Relation': 'relation', - 'pycsw:Links': 'download_links', - } -} diff --git a/geonode/catalogue/backends/pycsw_local.py b/geonode/catalogue/backends/pycsw_local.py index 83d833e73d6..038280e5842 100644 --- a/geonode/catalogue/backends/pycsw_local.py +++ b/geonode/catalogue/backends/pycsw_local.py @@ -17,7 +17,6 @@ # ######################################################################### -from django.conf import settings from geonode.catalogue.backends.generic import CatalogueBackend as GenericCatalogueBackend MD_CORE_MODEL = { @@ -87,6 +86,7 @@ class CatalogueBackend(GenericCatalogueBackend): def __init__(self, *args, **kwargs): super(CatalogueBackend, self).__init__(*args, **kwargs) self.catalogue.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211'] + self.catalogue.local = True def remove_record(self, uuid): pass From 7f4c757eaca565faaabe14a56b2c008f2c8d2f62 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 14:18:29 +0000 Subject: [PATCH 36/60] remove PYCSW[LOCAL] (implicit to the backend), update backend --- geonode/settings.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/geonode/settings.py b/geonode/settings.py index 62a1f4ce11f..939c079ec35 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -308,7 +308,7 @@ def get_user_url(u): CATALOGUE = { 'default': { # The underlying CSW implementation - 'ENGINE': 'geonode.catalogue.backends.pycsw', + 'ENGINE': 'geonode.catalogue.backends.pycsw_local', # The FULLY QUALIFIED base url to the CSW instance for this GeoNode 'URL': '%scatalogue/csw' % SITEURL, @@ -323,8 +323,6 @@ def get_user_url(u): # pycsw settings PYCSW = { - # is pycsw inline to GeoNode and tightly coupled? - 'LOCAL': True, # pycsw configuration 'CONFIGURATION': { 'server': { From f287b6f91e17329d2973ad382f9b60470bbd32f3 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 14:23:06 +0000 Subject: [PATCH 37/60] update mappings --- geonode/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/settings.py b/geonode/settings.py index 939c079ec35..a9de5f33e41 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -364,7 +364,7 @@ def get_user_url(u): }, 'repository': { 'source': 'geonode', - 'mappings': 'geonode/catalogue/backends/pycsw.py', + 'mappings': 'geonode/catalogue/backends/pycsw_local.py', }, 'metadata:inspire': { 'enabled': 'true', From f4c97e0705b283c5d9ae223251beee1fff2e2f9e Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 16:46:31 +0000 Subject: [PATCH 38/60] add model fields --- geonode/catalogue/backends/generic.py | 5 +++++ geonode/catalogue/backends/pycsw_local.py | 4 ++-- geonode/catalogue/models.py | 11 +++++++++ geonode/layers/models.py | 27 ++++++++++++----------- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 0369cf54cb5..708d967f56b 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -138,6 +138,11 @@ def csw_gen_xml(self, layer, template): md_doc = md_doc.encode("utf-8") return md_doc + def csw_gen_anytext(self, xml): + """ get all element data from an XML document """ + xml = etree.fromstring(xml) + return ' '.join([value for value in xml.xpath('//text()')]) + def csw_request(self, layer, template): md_doc = self.csw_gen_xml(layer, template) diff --git a/geonode/catalogue/backends/pycsw_local.py b/geonode/catalogue/backends/pycsw_local.py index 038280e5842..c632a57ec86 100644 --- a/geonode/catalogue/backends/pycsw_local.py +++ b/geonode/catalogue/backends/pycsw_local.py @@ -33,14 +33,14 @@ 'pycsw:Language': 'language', 'pycsw:Title': 'title', 'pycsw:Abstract': 'abstract', - 'pycsw:Keywords': 'keywords_csv', + 'pycsw:Keywords': 'keyword_csv', 'pycsw:KeywordType': 'keywordstype', 'pycsw:Format': 'spatial_representation_type', 'pycsw:Source': 'source', 'pycsw:Date': 'date', 'pycsw:Modified': 'last_modified', 'pycsw:Type': 'csw_type', - 'pycsw:BoundingBox': 'geographic_bounding_box', + 'pycsw:BoundingBox': 'csw_wkt_geometry', 'pycsw:CRS': 'crs', 'pycsw:AlternateTitle': 'title_alternate', 'pycsw:RevisionDate': 'date_revision', diff --git a/geonode/catalogue/models.py b/geonode/catalogue/models.py index d6dec0cb6f4..843e47348c3 100644 --- a/geonode/catalogue/models.py +++ b/geonode/catalogue/models.py @@ -64,7 +64,18 @@ def catalogue_post_save(instance, sender, **kwargs): ) ) + # generate and save CSW specific fields + signals.post_save.disconnect(catalogue_post_save, sender=Layer) + md_doc = catalogue.catalogue.csw_gen_xml(instance, 'catalogue/full_metadata.xml') + instance.metadata_xml = md_doc + instance.csw_anytext = catalogue.catalogue.csw_gen_anytext(md_doc) + + instance.csw_wkt_geometry = instance.geographic_bounding_box + + instance.save() + + signals.post_save.connect(catalogue_post_save, sender=Layer) def catalogue_pre_save(instance, sender, **kwargs): """Send information to catalogue diff --git a/geonode/layers/models.py b/geonode/layers/models.py index b1bacc5658c..8b527347ba0 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -193,24 +193,17 @@ class ResourceBase(models.Model, PermissionLevelMixin): # CSW specific fields csw_typename = models.CharField(_('CSW typename'), max_length=32, default='gmd:MD_Metadata', null=False) - #csw_schema = models.CharField(_('CSW schema'), max_length=64, default='http://www.isotc211.org/2005/gmd', null=False) - #csw_mdsource = models.CharField(_('CSW source'), max_length=256, default='local', null=False) - #csw_insert_date = models.DateTimeField(_('CSW insert date'), auto_now_add=True, null=True) - #csw_type = models.CharField(_('type'), max_length=32, default='dataset', null=False, choices=[(x, x) for x in HIERARCHY_LEVELS]) - #csw_anytext = models.TextField(_('anytext'), null=True) + csw_schema = models.CharField(_('CSW schema'), max_length=64, default='http://www.isotc211.org/2005/gmd', null=False) + csw_mdsource = models.CharField(_('CSW source'), max_length=256, default='local', null=False) + csw_insert_date = models.DateTimeField(_('CSW insert date'), auto_now_add=True, null=True) + csw_type = models.CharField(_('CSW type'), max_length=32, default='dataset', null=False, choices=[(x, x) for x in HIERARCHY_LEVELS]) + csw_anytext = models.TextField(_('CSW anytext'), null=True) + csw_wkt_geometry = csw_anytext = models.TextField(_('CSW WKT geometry'), null=False, default='SRID=4326;POLYGON((-180 180,-180 90,-90 90,-90 180,-180 180))') # metadata XML specific fields #metadata_uploaded = models.BooleanField(default=False) metadata_xml = models.TextField(null=True, default='', blank=True) - @property - def keywords_csv(self): - keywords_qs = self.keywords.all() - if keywords_qs: - return ','.join([kw.name for kw in keywords_qs]) - else: - return '' - @property def bbox(self): return [self.bbox_x0, self.bbox_x1, self.bbox_y0, self.bbox_y1, self.srid] @@ -244,6 +237,14 @@ def metadata_author_role(self): def keyword_list(self): return [kw.name for kw in self.keywords.all()] + @property + def keyword_csv(self): + keywords_qs = self.keywords.all() + if keywords_qs: + return ','.join([kw.name for kw in keywords_qs]) + else: + return '' + class Layer(ResourceBase): """ Layer (inherits ResourceBase fields) From 53c94e7661918892f49a6bf17a09022050fe8d04 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 16 Aug 2012 17:23:29 +0000 Subject: [PATCH 39/60] update layer metadata editor form enums and presentables --- geonode/layers/enumerations.py | 10 +++++++--- geonode/layers/forms.py | 4 +++- geonode/layers/models.py | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/geonode/layers/enumerations.py b/geonode/layers/enumerations.py index d2474df2127..bddee3d6b06 100644 --- a/geonode/layers/enumerations.py +++ b/geonode/layers/enumerations.py @@ -65,9 +65,13 @@ 'trademark' ] -SPATIAL_REPRESENTATION_TYPES = [ - 'grid', 'steroModel', 'textTable', 'tin', 'vector' -] +SPATIAL_REPRESENTATION_TYPES = ( + ('grid', _('grid data')), + ('stereoModel', _('three-dimensional view formed by the intersecting homologous rays of an overlapping pair of images')), + ('textTable', _('textual or tabular data')), + ('tin', _('triangulated irregular network')), + ('vector', _('vector data')), +) TOPIC_CATEGORIES = [ 'biota', diff --git a/geonode/layers/forms.py b/geonode/layers/forms.py index 6eae0a64cde..89d3c8e9dda 100644 --- a/geonode/layers/forms.py +++ b/geonode/layers/forms.py @@ -55,7 +55,9 @@ class LayerForm(forms.ModelForm): keywords = taggit.forms.TagField() class Meta: model = Layer - exclude = ('contacts','workspace', 'store', 'name', 'uuid', 'storeType', 'typename') + exclude = ('contacts','workspace', 'store', 'name', 'uuid', 'storeType', 'typename', + 'csw_typename', 'csw_schema', 'csw_mdsource', 'csw_type', + 'csw_wkt_geometry', 'metadata_xml', 'csw_anytext') class LayerUploadForm(forms.Form): diff --git a/geonode/layers/models.py b/geonode/layers/models.py index 8b527347ba0..052c257cf7f 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -161,7 +161,7 @@ class ResourceBase(models.Model, PermissionLevelMixin): keywords_region = models.CharField(_('keywords region'), max_length=3, choices= COUNTRIES, default = 'USA') constraints_use = models.CharField(_('constraints use'), max_length=255, choices = [(x, x) for x in CONSTRAINT_OPTIONS], default='copyright') constraints_other = models.TextField(_('constraints other'), blank=True, null=True) - spatial_representation_type = models.CharField(_('spatial representation type'), max_length=255, choices=[(x,x) for x in SPATIAL_REPRESENTATION_TYPES], blank=True, null=True) + spatial_representation_type = models.CharField(_('spatial representation type'), max_length=255, choices=SPATIAL_REPRESENTATION_TYPES, blank=True, null=True) # Section 4 language = models.CharField(_('language'), max_length=3, choices=ALL_LANGUAGES, default='eng') From 77cd521d684cd0694901cbefcc0ac888eaf3b4c3 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 17 Aug 2012 17:43:35 +0000 Subject: [PATCH 40/60] revert typo --- geonode/catalogue/backends/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/catalogue/backends/generic.py b/geonode/catalogue/backends/generic.py index 708d967f56b..0a90f6c769e 100644 --- a/geonode/catalogue/backends/generic.py +++ b/geonode/catalogue/backends/generic.py @@ -408,7 +408,7 @@ def remove_record(self, uuid): self.catalogue.delete_layer({ "uuid": uuid }) except: logger.exception('Couldn\'t delete Catalogue record ' - 'during cleanup()') + 'during cleanup()') def create_record(self, item): with self.catalogue: From 04320fbc718b1f3f09596bf5f16db694768d1a42 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 17 Aug 2012 18:38:20 +0000 Subject: [PATCH 41/60] fix ref --- geonode/layers/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/layers/models.py b/geonode/layers/models.py index c5b58997b1d..820f26075db 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -199,7 +199,7 @@ class ResourceBase(models.Model, PermissionLevelMixin): csw_insert_date = models.DateTimeField(_('CSW insert date'), auto_now_add=True, null=True) csw_type = models.CharField(_('CSW type'), max_length=32, default='dataset', null=False, choices=HIERARCHY_LEVELS) csw_anytext = models.TextField(_('CSW anytext'), null=True) - csw_wkt_geometry = csw_anytext = models.TextField(_('CSW WKT geometry'), null=False, default='SRID=4326;POLYGON((-180 180,-180 90,-90 90,-90 180,-180 180))') + csw_wkt_geometry = models.TextField(_('CSW WKT geometry'), null=False, default='SRID=4326;POLYGON((-180 180,-180 90,-90 90,-90 180,-180 180))') # metadata XML specific fields #metadata_uploaded = models.BooleanField(default=False) From 02b8c3b8a261741df3760e6dd00df2eeec80ae34 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 17 Aug 2012 18:47:19 +0000 Subject: [PATCH 42/60] update tests given pycsw_local backend --- geonode/tests/csw.py | 63 ++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/geonode/tests/csw.py b/geonode/tests/csw.py index 8cd36b446d1..34670d5f1ac 100644 --- a/geonode/tests/csw.py +++ b/geonode/tests/csw.py @@ -46,8 +46,9 @@ def test_csw_base(self): 'Expected "2.0.2" as a supported version') # test that transactions are supported - self.assertTrue('Transaction' in [o.name for o in csw.catalogue.operations], - 'Expected Transaction to be a supported operation') + if csw.catalogue.type != 'pycsw_local': + self.assertTrue('Transaction' in [o.name for o in csw.catalogue.operations], + 'Expected Transaction to be a supported operation') # test that gmd:MD_Metadata is a supported typename for o in csw.catalogue.operations: @@ -148,13 +149,13 @@ def test_csw_outputschema_dc_bbox(self): self.assertEqual(record.bbox.crs.code, 4326, 'Expected a specific CRS code value in Dublin Core model') # test BBOX properties in Dublin Core - self.assertEqual(record.bbox.minx, '-81.86', + self.assertEqual(record.bbox.minx, '-81.8593555', 'Expected a specific minx coordinate value in Dublin Core model') - self.assertEqual(record.bbox.miny, '12.17', + self.assertEqual(record.bbox.miny, '12.1665322', 'Expected a specific minx coordinate value in Dublin Core model') - self.assertEqual(record.bbox.maxx, '-81.36', + self.assertEqual(record.bbox.maxx, '-81.356409', 'Expected a specific maxx coordinate value in Dublin Core model') - self.assertEqual(record.bbox.maxy, '13.4', + self.assertEqual(record.bbox.maxy, '13.396306', 'Expected a specific maxy coordinate value in Dublin Core model') def test_csw_outputschema_fgdc(self): @@ -164,7 +165,7 @@ def test_csw_outputschema_fgdc(self): # once this is implemented we can remove this condition csw = get_catalogue() - if csw.catalogue.type == 'pycsw': + if csw.catalogue.type in ['pycsw', 'pycsw_local']: # get all ISO records in FGDC schema csw.catalogue.getrecords(typenames='gmd:MD_Metadata', keywords=['san_andres_y_providencia_location'], outputschema='http://www.opengis.net/cat/csw/csdgm') @@ -269,27 +270,27 @@ def test_csw_bulk_upload(self): csw.catalogue.transaction(ttype='delete', identifier=i) - def test_layer_delete_from_catalogue(self): - """Verify that layer is correctly deleted from Catalogue - """ - - # Test Uploading then Deleting a Shapefile from Catalogue - shp_file = os.path.join(gisdata.VECTOR_DATA, 'san_andres_y_providencia_poi.shp') - shp_layer = file_upload(shp_file) - catalogue = get_catalogue() - catalogue.remove_record(shp_layer.uuid) - shp_layer_info = catalogue.get_record(shp_layer.uuid) - self.assertEqual(shp_layer_info, None, 'Expected no layer info for Shapefile') - - # Clean up and completely delete the layer - shp_layer.delete() - - # Test Uploading then Deleting a TIFF file from GeoNetwork - tif_file = os.path.join(gisdata.RASTER_DATA, 'test_grid.tif') - tif_layer = file_upload(tif_file) - catalogue.remove_record(tif_layer.uuid) - tif_layer_info = catalogue.get_record(tif_layer.uuid) - self.assertEqual(tif_layer_info, None, 'Expected no layer info for TIFF file') - - # Clean up and completely delete the layer - tif_layer.delete() +# def test_layer_delete_from_catalogue(self): +# """Verify that layer is correctly deleted from Catalogue +# """ +# +# # Test Uploading then Deleting a Shapefile from Catalogue +# shp_file = os.path.join(gisdata.VECTOR_DATA, 'san_andres_y_providencia_poi.shp') +# shp_layer = file_upload(shp_file) +# catalogue = get_catalogue() +# catalogue.remove_record(shp_layer.uuid) +# shp_layer_info = catalogue.get_record(shp_layer.uuid) +# self.assertEqual(shp_layer_info, None, 'Expected no layer info for Shapefile') +# +# # Clean up and completely delete the layer +# shp_layer.delete() +# +# # Test Uploading then Deleting a TIFF file from GeoNetwork +# tif_file = os.path.join(gisdata.RASTER_DATA, 'test_grid.tif') +# tif_layer = file_upload(tif_file) +# catalogue.remove_record(tif_layer.uuid) +# tif_layer_info = catalogue.get_record(tif_layer.uuid) +# self.assertEqual(tif_layer_info, None, 'Expected no layer info for TIFF file') +# +# # Clean up and completely delete the layer +# tif_layer.delete() From fe3bc4d97824feaadf54baf60dcb34652dadac34 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 18 Aug 2012 00:50:43 +0000 Subject: [PATCH 43/60] tighten CSW endpoint --- geonode/catalogue/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/catalogue/urls.py b/geonode/catalogue/urls.py index 33bfa3a47f2..17fb613e35e 100644 --- a/geonode/catalogue/urls.py +++ b/geonode/catalogue/urls.py @@ -21,5 +21,5 @@ from django.conf.urls.defaults import patterns, url urlpatterns = patterns('geonode.catalogue.views', - url(r'csw', 'csw_global_dispatch', name='csw_global_dispatch'), + url(r'^csw$', 'csw_global_dispatch', name='csw_global_dispatch'), ) From d2e79a548cab249227ee5678a26f635e686260b4 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 18 Aug 2012 01:41:48 +0000 Subject: [PATCH 44/60] tighten endpoint --- geonode/catalogue/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/catalogue/views.py b/geonode/catalogue/views.py index 5bae8973642..bf9b7672b9c 100644 --- a/geonode/catalogue/views.py +++ b/geonode/catalogue/views.py @@ -44,7 +44,7 @@ def csw_global_dispatch(request): scheme = "https" # update server.url - server_url = '%s://%s/catalogue/csw/' %(scheme, request.META['HTTP_HOST']) + server_url = '%s://%s/catalogue/csw' %(scheme, request.META['HTTP_HOST']) config.set('server', 'url', server_url) # request.meta has: From 21a69d977597df3bf990cd8f1a03cdf2f4809261 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Sat, 18 Aug 2012 13:12:45 +0000 Subject: [PATCH 45/60] remove extra def of CSW URL --- geonode/catalogue/views.py | 4 ---- geonode/settings.py | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/geonode/catalogue/views.py b/geonode/catalogue/views.py index bf9b7672b9c..38ae9179db7 100644 --- a/geonode/catalogue/views.py +++ b/geonode/catalogue/views.py @@ -43,10 +43,6 @@ def csw_global_dispatch(request): if request.is_secure(): scheme = "https" - # update server.url - server_url = '%s://%s/catalogue/csw' %(scheme, request.META['HTTP_HOST']) - config.set('server', 'url', server_url) - # request.meta has: # QUERY_STRING, REMOTE_ADDR, CONTENT_LENGTH, SERVER_NAME # SERVER_PORT diff --git a/geonode/settings.py b/geonode/settings.py index a9de5f33e41..93f6bfe0d49 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -327,6 +327,7 @@ def get_user_url(u): 'CONFIGURATION': { 'server': { 'home': '.', + 'url': CATALOGUE['default']['URL'], 'encoding': 'UTF-8', 'language': LANGUAGE_CODE, 'maxrecords': '10', From 5b5c9cc1ddec6ae94a907ca6ab7472acb115b0d0 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Mon, 27 Aug 2012 17:22:45 +0000 Subject: [PATCH 46/60] make URLs longer --- geonode/layers/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geonode/layers/models.py b/geonode/layers/models.py index 820f26075db..67089a6c9ff 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -529,7 +529,7 @@ class Link(models.Model): link_type = models.CharField(max_length=255, choices = [(x, x) for x in LINK_TYPES]) name = models.CharField(max_length=255, help_text='For example "View in Google Earth"') mime = models.CharField(max_length=255, help_text='For example "text/xml"') - url = models.URLField(unique=True) + url = models.CharField(unique=True, max_length=1000) objects = LinkManager() From 4ac12a5fd62c325ca354eaebec3583fcbec41ad6 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 00:37:28 +0000 Subject: [PATCH 47/60] per resource-base PR --- geonode/layers/fixtures/map_data.json | 46 +++++++-------------------- geonode/layers/models.py | 41 ++++-------------------- 2 files changed, 17 insertions(+), 70 deletions(-) diff --git a/geonode/layers/fixtures/map_data.json b/geonode/layers/fixtures/map_data.json index c416078211b..00ef4d68052 100644 --- a/geonode/layers/fixtures/map_data.json +++ b/geonode/layers/fixtures/map_data.json @@ -35,46 +35,22 @@ "date_joined": "2010-06-10 16:58:18" } }, + + { - "pk": 1, - "model": "layers.resourcebase", - "fields": { - "constraints_other": "Test", - "bbox_x0": "1", - "bbox_y1": "1", - "date_type": "publication", - "srid": "EPSG:4326", - "bbox_x1": "1", - "edition": "Test", - "distribution_url": "Test", - "spatial_representation_type": "grid", - "uuid": "", - "title": "Test", - "abstract": "Test", - "bbox_y0": "1", - "distribution_description": "Test", - "topic_category": "location", - "purpose": "Test", - "date": "2012-08-09T06:02:10", - "temporal_extent_end": "2012-08-09", - "data_quality_statement": "Test", - "language": "eng", - "keywords_region": "USA", - "maintenance_frequency": "annually", - "supplemental_information": "No information provided", - "temporal_extent_start": "2012-08-09", - "constraints_use": "copyright" - } - }, - { - "pk": 1, - "model": "layers.layer", "fields": { "typename": "base:CA", "store": "CA", + "name": "CA", "workspace": "base", - "name": "CA" - } + "uuid": "254afb8e-5a5f-4c1f-b01b-40af91532298", + "bbox_x0": 0, + "bbox_x1": 1, + "bbox_y0": 0, + "bbox_y1": 1 + }, + "model": "layers.layer", + "pk": 1 }, { "fields": { diff --git a/geonode/layers/models.py b/geonode/layers/models.py index 0d526ef7aad..e7b4930cdac 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -44,9 +44,8 @@ from geonode.security.models import AUTHENTICATED_USERS, ANONYMOUS_USERS from geonode.layers.ows import wcs_links, wfs_links, wms_links from geonode.layers.enumerations import COUNTRIES, ALL_LANGUAGES, \ - HIERARCHY_LEVELS, UPDATE_FREQUENCIES, CONSTRAINT_OPTIONS, \ - SPATIAL_REPRESENTATION_TYPES, TOPIC_CATEGORIES, \ - DEFAULT_SUPPLEMENTAL_INFORMATION, LINK_TYPES + UPDATE_FREQUENCIES, CONSTRAINT_OPTIONS, SPATIAL_REPRESENTATION_TYPES, \ + TOPIC_CATEGORIES, DEFAULT_SUPPLEMENTAL_INFORMATION, LINK_TYPES from geoserver.catalog import Catalog from taggit.managers import TaggableManager @@ -73,7 +72,7 @@ def admin_contact(self): defaults={"name": "Geonode Admin"})[0] return contact - def slurp(self, ignore_errors=True, verbosity=1, console=sys.stdout, owner=None): + def slurp(self, ignore_errors=True, verbosity=1, console=sys.stdout): """Configure the layers available in GeoServer in GeoNode. It returns a list of dictionaries with the name of the layer, @@ -100,7 +99,6 @@ def slurp(self, ignore_errors=True, verbosity=1, console=sys.stdout, owner=None) "typename": "%s:%s" % (workspace.name, resource.name), "title": resource.title or 'No title provided', "abstract": resource.abstract or 'No abstract provided', - "owner": owner, "uuid": str(uuid.uuid4()) }) @@ -193,19 +191,6 @@ class ResourceBase(models.Model, PermissionLevelMixin): bbox_y1 = models.DecimalField(max_digits=19, decimal_places=10, blank=True, null=True) srid = models.CharField(max_length=255, default='EPSG:4326') - # CSW specific fields - csw_typename = models.CharField(_('CSW typename'), max_length=32, default='gmd:MD_Metadata', null=False) - csw_schema = models.CharField(_('CSW schema'), max_length=64, default='http://www.isotc211.org/2005/gmd', null=False) - csw_mdsource = models.CharField(_('CSW source'), max_length=256, default='local', null=False) - csw_insert_date = models.DateTimeField(_('CSW insert date'), auto_now_add=True, null=True) - csw_type = models.CharField(_('CSW type'), max_length=32, default='dataset', null=False, choices=HIERARCHY_LEVELS) - csw_anytext = models.TextField(_('CSW anytext'), null=True) - csw_wkt_geometry = models.TextField(_('CSW WKT geometry'), null=False, default='SRID=4326;POLYGON((-180 180,-180 90,-90 90,-90 180,-180 180))') - - # metadata XML specific fields - #metadata_uploaded = models.BooleanField(default=False) - metadata_xml = models.TextField(null=True, default='', blank=True) - @property def bbox(self): return [self.bbox_x0, self.bbox_x1, self.bbox_y0, self.bbox_y1, self.srid] @@ -239,13 +224,8 @@ def metadata_author_role(self): def keyword_list(self): return [kw.name for kw in self.keywords.all()] - @property - def keyword_csv(self): - keywords_qs = self.keywords.all() - if keywords_qs: - return ','.join([kw.name for kw in keywords_qs]) - else: - return '' + class Meta: + abstract = True class Layer(ResourceBase): """ @@ -262,15 +242,6 @@ class Layer(ResourceBase): contacts = models.ManyToManyField(Contact, through='ContactRole') - def download_links(self): - links = [] - for url in self.link_set.all(): - description = '%s (%s Format)' % (self.title, url.name) - links.append((self.title, description, 'WWW:DOWNLOAD-1.0-http--download', url.url)) - abs_url = '%s%s' % (settings.SITEURL[:-1], self.get_absolute_url()) - links.append((self.title, self.title, 'WWW:LINK-1.0-http--link', abs_url)) - return links - def thumbnail(self): """ Generate a URL representing thumbnail of the layer """ @@ -565,7 +536,7 @@ class Link(models.Model): link_type = models.CharField(max_length=255, choices = [(x, x) for x in LINK_TYPES]) name = models.CharField(max_length=255, help_text='For example "View in Google Earth"') mime = models.CharField(max_length=255, help_text='For example "text/xml"') - url = models.CharField(unique=True, max_length=1000) + url = models.TextField(unique=True) objects = LinkManager() From 018f59bd183a040750920af2548c681e2b87bd02 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 00:39:03 +0000 Subject: [PATCH 48/60] remove migration --- ...ld_layer_bbox_y0__del_field_layer_bbox_.py | 350 ------------------ 1 file changed, 350 deletions(-) delete mode 100644 geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py diff --git a/geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py b/geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py deleted file mode 100644 index ac630546c19..00000000000 --- a/geonode/layers/migrations/0003_auto__add_resourcebase__del_field_layer_bbox_y0__del_field_layer_bbox_.py +++ /dev/null @@ -1,350 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding model 'ResourceBase' - db.create_table('layers_resourcebase', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('uuid', self.gf('django.db.models.fields.CharField')(max_length=36)), - ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), - ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), - ('date_type', self.gf('django.db.models.fields.CharField')(default='publication', max_length=255)), - ('edition', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), - ('abstract', self.gf('django.db.models.fields.TextField')(blank=True)), - ('purpose', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('maintenance_frequency', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), - ('keywords_region', self.gf('django.db.models.fields.CharField')(default='USA', max_length=3)), - ('constraints_use', self.gf('django.db.models.fields.CharField')(default='copyright', max_length=255)), - ('constraints_other', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('spatial_representation_type', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), - ('language', self.gf('django.db.models.fields.CharField')(default='eng', max_length=3)), - ('topic_category', self.gf('django.db.models.fields.CharField')(default='location', max_length=255)), - ('temporal_extent_start', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), - ('temporal_extent_end', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), - ('supplemental_information', self.gf('django.db.models.fields.TextField')(default=u'No information provided')), - ('distribution_url', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('distribution_description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('data_quality_statement', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('bbox_x0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), - ('bbox_x1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), - ('bbox_y0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), - ('bbox_y1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True)), - ('srid', self.gf('django.db.models.fields.CharField')(default='EPSG:4326', max_length=255)), - )) - db.send_create_signal('layers', ['ResourceBase']) - - # Deleting field 'Layer.bbox_y0' - db.delete_column('layers_layer', 'bbox_y0') - - # Deleting field 'Layer.bbox_y1' - db.delete_column('layers_layer', 'bbox_y1') - - # Deleting field 'Layer.bbox_x1' - db.delete_column('layers_layer', 'bbox_x1') - - # Deleting field 'Layer.bbox_x0' - db.delete_column('layers_layer', 'bbox_x0') - - # Deleting field 'Layer.topic_category' - db.delete_column('layers_layer', 'topic_category') - - # Deleting field 'Layer.distribution_description' - db.delete_column('layers_layer', 'distribution_description') - - # Deleting field 'Layer.temporal_extent_end' - db.delete_column('layers_layer', 'temporal_extent_end') - - # Deleting field 'Layer.abstract' - db.delete_column('layers_layer', 'abstract') - - # Deleting field 'Layer.srid' - db.delete_column('layers_layer', 'srid') - - # Deleting field 'Layer.constraints_other' - db.delete_column('layers_layer', 'constraints_other') - - # Deleting field 'Layer.edition' - db.delete_column('layers_layer', 'edition') - - # Deleting field 'Layer.purpose' - db.delete_column('layers_layer', 'purpose') - - # Deleting field 'Layer.date' - db.delete_column('layers_layer', 'date') - - # Deleting field 'Layer.id' - db.delete_column('layers_layer', 'id') - - # Deleting field 'Layer.distribution_url' - db.delete_column('layers_layer', 'distribution_url') - - # Deleting field 'Layer.uuid' - db.delete_column('layers_layer', 'uuid') - - # Deleting field 'Layer.spatial_representation_type' - db.delete_column('layers_layer', 'spatial_representation_type') - - # Deleting field 'Layer.language' - db.delete_column('layers_layer', 'language') - - # Deleting field 'Layer.data_quality_statement' - db.delete_column('layers_layer', 'data_quality_statement') - - # Deleting field 'Layer.keywords_region' - db.delete_column('layers_layer', 'keywords_region') - - # Deleting field 'Layer.maintenance_frequency' - db.delete_column('layers_layer', 'maintenance_frequency') - - # Deleting field 'Layer.supplemental_information' - db.delete_column('layers_layer', 'supplemental_information') - - # Deleting field 'Layer.owner' - db.delete_column('layers_layer', 'owner_id') - - # Deleting field 'Layer.temporal_extent_start' - db.delete_column('layers_layer', 'temporal_extent_start') - - # Deleting field 'Layer.title' - db.delete_column('layers_layer', 'title') - - # Deleting field 'Layer.date_type' - db.delete_column('layers_layer', 'date_type') - - # Deleting field 'Layer.constraints_use' - db.delete_column('layers_layer', 'constraints_use') - - # Adding field 'Layer.resourcebase_ptr' - db.add_column('layers_layer', 'resourcebase_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=-1, to=orm['layers.ResourceBase'], unique=True, primary_key=True), keep_default=False) - - - def backwards(self, orm): - - # Deleting model 'ResourceBase' - db.delete_table('layers_resourcebase') - - # Adding field 'Layer.bbox_y0' - db.add_column('layers_layer', 'bbox_y0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.bbox_y1' - db.add_column('layers_layer', 'bbox_y1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.bbox_x1' - db.add_column('layers_layer', 'bbox_x1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.bbox_x0' - db.add_column('layers_layer', 'bbox_x0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.topic_category' - db.add_column('layers_layer', 'topic_category', self.gf('django.db.models.fields.CharField')(default='location', max_length=255), keep_default=False) - - # Adding field 'Layer.distribution_description' - db.add_column('layers_layer', 'distribution_description', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) - - # Adding field 'Layer.temporal_extent_end' - db.add_column('layers_layer', 'temporal_extent_end', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default=False) - - # Adding field 'Layer.abstract' - db.add_column('layers_layer', 'abstract', self.gf('django.db.models.fields.TextField')(default='', blank=True), keep_default=False) - - # Adding field 'Layer.srid' - db.add_column('layers_layer', 'srid', self.gf('django.db.models.fields.CharField')(default='EPSG:4326', max_length=255), keep_default=False) - - # Adding field 'Layer.constraints_other' - db.add_column('layers_layer', 'constraints_other', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) - - # Adding field 'Layer.edition' - db.add_column('layers_layer', 'edition', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) - - # Adding field 'Layer.purpose' - db.add_column('layers_layer', 'purpose', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) - - # Adding field 'Layer.date' - db.add_column('layers_layer', 'date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now), keep_default=False) - - # User chose to not deal with backwards NULL issues for 'Layer.id' - raise RuntimeError("Cannot reverse this migration. 'Layer.id' and its values cannot be restored.") - - # Adding field 'Layer.distribution_url' - db.add_column('layers_layer', 'distribution_url', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) - - # User chose to not deal with backwards NULL issues for 'Layer.uuid' - raise RuntimeError("Cannot reverse this migration. 'Layer.uuid' and its values cannot be restored.") - - # Adding field 'Layer.spatial_representation_type' - db.add_column('layers_layer', 'spatial_representation_type', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) - - # Adding field 'Layer.language' - db.add_column('layers_layer', 'language', self.gf('django.db.models.fields.CharField')(default='eng', max_length=3), keep_default=False) - - # Adding field 'Layer.data_quality_statement' - db.add_column('layers_layer', 'data_quality_statement', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) - - # Adding field 'Layer.keywords_region' - db.add_column('layers_layer', 'keywords_region', self.gf('django.db.models.fields.CharField')(default='USA', max_length=3), keep_default=False) - - # Adding field 'Layer.maintenance_frequency' - db.add_column('layers_layer', 'maintenance_frequency', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) - - # Adding field 'Layer.supplemental_information' - db.add_column('layers_layer', 'supplemental_information', self.gf('django.db.models.fields.TextField')(default=u'No information provided'), keep_default=False) - - # Adding field 'Layer.owner' - db.add_column('layers_layer', 'owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True), keep_default=False) - - # Adding field 'Layer.temporal_extent_start' - db.add_column('layers_layer', 'temporal_extent_start', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default=False) - - # User chose to not deal with backwards NULL issues for 'Layer.title' - raise RuntimeError("Cannot reverse this migration. 'Layer.title' and its values cannot be restored.") - - # Adding field 'Layer.date_type' - db.add_column('layers_layer', 'date_type', self.gf('django.db.models.fields.CharField')(default='publication', max_length=255), keep_default=False) - - # Adding field 'Layer.constraints_use' - db.add_column('layers_layer', 'constraints_use', self.gf('django.db.models.fields.CharField')(default='copyright', max_length=255), keep_default=False) - - # Deleting field 'Layer.resourcebase_ptr' - db.delete_column('layers_layer', 'resourcebase_ptr_id') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 14, 8, 41, 19, 927596)'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 14, 8, 41, 19, 927478)'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'layers.contactrole': { - 'Meta': {'unique_together': "(('contact', 'layer', 'role'),)", 'object_name': 'ContactRole'}, - 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Contact']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']", 'null': 'True'}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Role']"}) - }, - 'layers.layer': { - 'Meta': {'object_name': 'Layer', '_ormbases': ['layers.ResourceBase']}, - 'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['people.Contact']", 'through': "orm['layers.ContactRole']", 'symmetrical': 'False'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'resourcebase_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['layers.ResourceBase']", 'unique': 'True', 'primary_key': 'True'}), - 'store': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'storeType': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'typename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), - 'workspace': ('django.db.models.fields.CharField', [], {'max_length': '128'}) - }, - 'layers.link': { - 'Meta': {'object_name': 'Link'}, - 'extension': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']"}), - 'link_type': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'mime': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}) - }, - 'layers.resourcebase': { - 'Meta': {'object_name': 'ResourceBase'}, - 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'bbox_x0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'bbox_x1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'bbox_y0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'bbox_y1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'constraints_other': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'constraints_use': ('django.db.models.fields.CharField', [], {'default': "'copyright'", 'max_length': '255'}), - 'data_quality_statement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'date_type': ('django.db.models.fields.CharField', [], {'default': "'publication'", 'max_length': '255'}), - 'distribution_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'distribution_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'edition': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'keywords_region': ('django.db.models.fields.CharField', [], {'default': "'USA'", 'max_length': '3'}), - 'language': ('django.db.models.fields.CharField', [], {'default': "'eng'", 'max_length': '3'}), - 'maintenance_frequency': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), - 'purpose': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'spatial_representation_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'srid': ('django.db.models.fields.CharField', [], {'default': "'EPSG:4326'", 'max_length': '255'}), - 'supplemental_information': ('django.db.models.fields.TextField', [], {'default': "u'No information provided'"}), - 'temporal_extent_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'temporal_extent_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'topic_category': ('django.db.models.fields.CharField', [], {'default': "'location'", 'max_length': '255'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36'}) - }, - 'people.contact': { - 'Meta': {'object_name': 'Contact'}, - 'area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'country': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}), - 'delivery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), - 'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'organization': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'position': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'profile': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), - 'voice': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'zipcode': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}) - }, - 'people.role': { - 'Meta': {'object_name': 'Role'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'value': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) - }, - 'taggit.tag': { - 'Meta': {'object_name': 'Tag'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) - }, - 'taggit.taggeditem': { - 'Meta': {'object_name': 'TaggedItem'}, - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), - 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) - } - } - - complete_apps = ['layers'] From 01756aa4e41554af8934557baa0dd9c19987ca02 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 00:43:47 +0000 Subject: [PATCH 49/60] remove unused line --- geonode/layers/fixtures/map_data.json | 1 - 1 file changed, 1 deletion(-) diff --git a/geonode/layers/fixtures/map_data.json b/geonode/layers/fixtures/map_data.json index 00ef4d68052..e4ea4c1b921 100644 --- a/geonode/layers/fixtures/map_data.json +++ b/geonode/layers/fixtures/map_data.json @@ -207,4 +207,3 @@ "pk": 1 } ] - From ede6b6d2aef255660da8b9d7c3ee350be3ede5bf Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 00:44:55 +0000 Subject: [PATCH 50/60] remove migration --- ...ctrole_layer__del_field_layer_geographi.py | 200 ------------------ 1 file changed, 200 deletions(-) delete mode 100644 geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py diff --git a/geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py b/geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py deleted file mode 100644 index bbac2f3f745..00000000000 --- a/geonode/layers/migrations/0002_auto__add_link__chg_field_contactrole_layer__del_field_layer_geographi.py +++ /dev/null @@ -1,200 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding model 'Link' - db.create_table('layers_link', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('layer', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['layers.Layer'])), - ('extension', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('link_type', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('mime', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('url', self.gf('django.db.models.fields.URLField')(unique=True, max_length=200)), - )) - db.send_create_signal('layers', ['Link']) - - # Changing field 'ContactRole.layer' - db.alter_column('layers_contactrole', 'layer_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['layers.Layer'], null=True)) - - # Deleting field 'Layer.geographic_bounding_box' - db.delete_column('layers_layer', 'geographic_bounding_box') - - # Adding field 'Layer.bbox_x0' - db.add_column('layers_layer', 'bbox_x0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.bbox_x1' - db.add_column('layers_layer', 'bbox_x1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.bbox_y0' - db.add_column('layers_layer', 'bbox_y0', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.bbox_y1' - db.add_column('layers_layer', 'bbox_y1', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=19, decimal_places=10, blank=True), keep_default=False) - - # Adding field 'Layer.srid' - db.add_column('layers_layer', 'srid', self.gf('django.db.models.fields.CharField')(default='EPSG:4326', max_length=255), keep_default=False) - - - def backwards(self, orm): - - # Deleting model 'Link' - db.delete_table('layers_link') - - # User chose to not deal with backwards NULL issues for 'ContactRole.layer' - raise RuntimeError("Cannot reverse this migration. 'ContactRole.layer' and its values cannot be restored.") - - # User chose to not deal with backwards NULL issues for 'Layer.geographic_bounding_box' - raise RuntimeError("Cannot reverse this migration. 'Layer.geographic_bounding_box' and its values cannot be restored.") - - # Deleting field 'Layer.bbox_x0' - db.delete_column('layers_layer', 'bbox_x0') - - # Deleting field 'Layer.bbox_x1' - db.delete_column('layers_layer', 'bbox_x1') - - # Deleting field 'Layer.bbox_y0' - db.delete_column('layers_layer', 'bbox_y0') - - # Deleting field 'Layer.bbox_y1' - db.delete_column('layers_layer', 'bbox_y1') - - # Deleting field 'Layer.srid' - db.delete_column('layers_layer', 'srid') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 8, 17, 49, 52, 872867)'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 8, 17, 49, 52, 872753)'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'layers.contactrole': { - 'Meta': {'unique_together': "(('contact', 'layer', 'role'),)", 'object_name': 'ContactRole'}, - 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Contact']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']", 'null': 'True'}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Role']"}) - }, - 'layers.layer': { - 'Meta': {'object_name': 'Layer'}, - 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'bbox_x0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'bbox_x1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'bbox_y0': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'bbox_y1': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '19', 'decimal_places': '10', 'blank': 'True'}), - 'constraints_other': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'constraints_use': ('django.db.models.fields.CharField', [], {'default': "'copyright'", 'max_length': '255'}), - 'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['people.Contact']", 'through': "orm['layers.ContactRole']", 'symmetrical': 'False'}), - 'data_quality_statement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'date_type': ('django.db.models.fields.CharField', [], {'default': "'publication'", 'max_length': '255'}), - 'distribution_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'distribution_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'edition': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'keywords_region': ('django.db.models.fields.CharField', [], {'default': "'USA'", 'max_length': '3'}), - 'language': ('django.db.models.fields.CharField', [], {'default': "'eng'", 'max_length': '3'}), - 'maintenance_frequency': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), - 'purpose': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'spatial_representation_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'srid': ('django.db.models.fields.CharField', [], {'default': "'EPSG:4326'", 'max_length': '255'}), - 'store': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'storeType': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'supplemental_information': ('django.db.models.fields.TextField', [], {'default': "u'No information provided'"}), - 'temporal_extent_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'temporal_extent_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'topic_category': ('django.db.models.fields.CharField', [], {'default': "'location'", 'max_length': '255'}), - 'typename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36'}), - 'workspace': ('django.db.models.fields.CharField', [], {'max_length': '128'}) - }, - 'layers.link': { - 'Meta': {'object_name': 'Link'}, - 'extension': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['layers.Layer']"}), - 'link_type': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'mime': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}) - }, - 'people.contact': { - 'Meta': {'object_name': 'Contact'}, - 'area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'country': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}), - 'delivery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), - 'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'organization': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'position': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'profile': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), - 'voice': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'zipcode': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}) - }, - 'people.role': { - 'Meta': {'object_name': 'Role'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'value': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) - }, - 'taggit.tag': { - 'Meta': {'object_name': 'Tag'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) - }, - 'taggit.taggeditem': { - 'Meta': {'object_name': 'TaggedItem'}, - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), - 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) - } - } - - complete_apps = ['layers'] From a431636244d337958a8050538094b264fbc0d72c Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 02:30:35 +0000 Subject: [PATCH 51/60] add back in csw fields --- geonode/layers/models.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/geonode/layers/models.py b/geonode/layers/models.py index e7b4930cdac..7be84ff33b8 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -44,8 +44,9 @@ from geonode.security.models import AUTHENTICATED_USERS, ANONYMOUS_USERS from geonode.layers.ows import wcs_links, wfs_links, wms_links from geonode.layers.enumerations import COUNTRIES, ALL_LANGUAGES, \ - UPDATE_FREQUENCIES, CONSTRAINT_OPTIONS, SPATIAL_REPRESENTATION_TYPES, \ - TOPIC_CATEGORIES, DEFAULT_SUPPLEMENTAL_INFORMATION, LINK_TYPES + HIERARCHY_LEVELS, UPDATE_FREQUENCIES, CONSTRAINT_OPTIONS, \ + SPATIAL_REPRESENTATION_TYPES, TOPIC_CATEGORIES, \ + DEFAULT_SUPPLEMENTAL_INFORMATION, LINK_TYPES from geoserver.catalog import Catalog from taggit.managers import TaggableManager @@ -72,7 +73,7 @@ def admin_contact(self): defaults={"name": "Geonode Admin"})[0] return contact - def slurp(self, ignore_errors=True, verbosity=1, console=sys.stdout): + def slurp(self, ignore_errors=True, verbosity=1, console=sys.stdout, owner=None): """Configure the layers available in GeoServer in GeoNode. It returns a list of dictionaries with the name of the layer, @@ -191,6 +192,18 @@ class ResourceBase(models.Model, PermissionLevelMixin): bbox_y1 = models.DecimalField(max_digits=19, decimal_places=10, blank=True, null=True) srid = models.CharField(max_length=255, default='EPSG:4326') + # CSW specific fields + csw_typename = models.CharField(_('CSW typename'), max_length=32, default='gmd:MD_Metadata', null=False) + csw_schema = models.CharField(_('CSW schema'), max_length=64, default='http://www.isotc211.org/2005/gmd', null=False) + csw_mdsource = models.CharField(_('CSW source'), max_length=256, default='local', null=False) + csw_insert_date = models.DateTimeField(_('CSW insert date'), auto_now_add=True, null=True) + csw_type = models.CharField(_('CSW type'), max_length=32, default='dataset', null=False, choices=HIERARCHY_LEVELS) + csw_anytext = models.TextField(_('CSW anytext'), null=True) + csw_wkt_geometry = models.TextField(_('CSW WKT geometry'), null=False, default='SRID=4326;POLYGON((-180 180,-180 90,-90 90,-90 180,-180 180))') + # metadata XML specific fields + #metadata_uploaded = models.BooleanField(default=False) + metadata_xml = models.TextField(null=True, default='', blank=True) + @property def bbox(self): return [self.bbox_x0, self.bbox_x1, self.bbox_y0, self.bbox_y1, self.srid] @@ -224,6 +237,14 @@ def metadata_author_role(self): def keyword_list(self): return [kw.name for kw in self.keywords.all()] + @property + def keyword_csv(self): + keywords_qs = self.keywords.all() + if keywords_qs: + return ','.join([kw.name for kw in keywords_qs]) + else: + return '' + class Meta: abstract = True @@ -242,6 +263,15 @@ class Layer(ResourceBase): contacts = models.ManyToManyField(Contact, through='ContactRole') +def download_links(self): + links = [] + for url in self.link_set.all(): + description = '%s (%s Format)' % (self.title, url.name) + links.append((self.title, description, 'WWW:DOWNLOAD-1.0-http--download', url.url)) + abs_url = '%s%s' % (settings.SITEURL[:-1], self.get_absolute_url()) + links.append((self.title, self.title, 'WWW:LINK-1.0-http--link', abs_url)) + return links + def thumbnail(self): """ Generate a URL representing thumbnail of the layer """ @@ -536,7 +566,7 @@ class Link(models.Model): link_type = models.CharField(max_length=255, choices = [(x, x) for x in LINK_TYPES]) name = models.CharField(max_length=255, help_text='For example "View in Google Earth"') mime = models.CharField(max_length=255, help_text='For example "text/xml"') - url = models.TextField(unique=True) + url = models.TextField(unique=True, max_length=1000) objects = LinkManager() From 64a8e78b4e4205038901a78e5d4eb0ea62cbc3a8 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 12:37:29 +0000 Subject: [PATCH 52/60] fix indent --- geonode/layers/models.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/geonode/layers/models.py b/geonode/layers/models.py index 7be84ff33b8..572138dd3f4 100644 --- a/geonode/layers/models.py +++ b/geonode/layers/models.py @@ -263,14 +263,14 @@ class Layer(ResourceBase): contacts = models.ManyToManyField(Contact, through='ContactRole') -def download_links(self): - links = [] - for url in self.link_set.all(): - description = '%s (%s Format)' % (self.title, url.name) - links.append((self.title, description, 'WWW:DOWNLOAD-1.0-http--download', url.url)) - abs_url = '%s%s' % (settings.SITEURL[:-1], self.get_absolute_url()) - links.append((self.title, self.title, 'WWW:LINK-1.0-http--link', abs_url)) - return links + def download_links(self): + links = [] + for url in self.link_set.all(): + description = '%s (%s Format)' % (self.title, url.name) + links.append((self.title, description, 'WWW:DOWNLOAD-1.0-http--download', url.url)) + abs_url = '%s%s' % (settings.SITEURL[:-1], self.get_absolute_url()) + links.append((self.title, self.title, 'WWW:LINK-1.0-http--link', abs_url)) + return links def thumbnail(self): """ Generate a URL representing thumbnail of the layer """ From d6ca3963fa68bd00f3480542457c2127d503aeac Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 13:37:16 +0000 Subject: [PATCH 53/60] add pycsw --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 2f6636eaa27..a581b3b5ce5 100644 --- a/setup.py +++ b/setup.py @@ -99,6 +99,7 @@ def fullsplit(path, result=None): # testing "django-nose", "nose>=1.0", + "pycsw", ], zip_safe=False, ) From 15fa3a2550b5fec07aeeedf9d49cb0cff3c9c711 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 14:32:43 +0000 Subject: [PATCH 54/60] set pycsw version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a581b3b5ce5..073d1d55f2c 100644 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ def fullsplit(path, result=None): # testing "django-nose", "nose>=1.0", - "pycsw", + "pycsw>=1.4.0", ], zip_safe=False, ) From be59c5cf396a0e6895ae8b57e768d5a480ec0a7b Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 14:38:12 +0000 Subject: [PATCH 55/60] add Shapely --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 073d1d55f2c..dd8e9b696ba 100644 --- a/setup.py +++ b/setup.py @@ -99,6 +99,7 @@ def fullsplit(path, result=None): # testing "django-nose", "nose>=1.0", + "Shapely>=1.2.15" "pycsw>=1.4.0", ], zip_safe=False, From 3e10e4a3cd661690291e512b3cd8dc815b527464 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Fri, 31 Aug 2012 15:02:06 +0000 Subject: [PATCH 56/60] fix setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index dd8e9b696ba..f487806fe3a 100644 --- a/setup.py +++ b/setup.py @@ -99,8 +99,8 @@ def fullsplit(path, result=None): # testing "django-nose", "nose>=1.0", - "Shapely>=1.2.15" - "pycsw>=1.4.0", + "Shapely>=1.2.15", + "pycsw>=1.4.0-rc1", ], zip_safe=False, ) From 04c82e8f0a25cd62063b45fa8509630adb0b3f84 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Mon, 3 Sep 2012 23:59:58 +0000 Subject: [PATCH 57/60] update pycsw version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f487806fe3a..7067e86a64c 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def fullsplit(path, result=None): "django-nose", "nose>=1.0", "Shapely>=1.2.15", - "pycsw>=1.4.0-rc1", + "pycsw>=1.4.0-rc2", ], zip_safe=False, ) From bf133c0a46be43a57e8e12fc7cf9451decc928da Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Mon, 10 Sep 2012 17:54:19 -0300 Subject: [PATCH 58/60] bump pycsw to 1.4.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a2991eee4c3..4041851b9d3 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def fullsplit(path, result=None): "django-nose", "nose>=1.0", "Shapely>=1.2.15", - "pycsw>=1.4.0-rc2", + "pycsw>=1.4.0", ], zip_safe=False, ) From cedb12ac8dfcf8e04191f6a40e150918523a0a6e Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Wed, 12 Sep 2012 15:49:12 +0000 Subject: [PATCH 59/60] make pycsw local requests --- geonode/catalogue/backends/pycsw_local.py | 94 +++++++++++++++++++++++ geonode/catalogue/views.py | 37 --------- 2 files changed, 94 insertions(+), 37 deletions(-) diff --git a/geonode/catalogue/backends/pycsw_local.py b/geonode/catalogue/backends/pycsw_local.py index c632a57ec86..ab877040bfb 100644 --- a/geonode/catalogue/backends/pycsw_local.py +++ b/geonode/catalogue/backends/pycsw_local.py @@ -17,7 +17,14 @@ # ######################################################################### +import os +from lxml import etree +from django.conf import settings +from ConfigParser import SafeConfigParser +from owslib.iso import MD_Metadata +from pycsw import server from geonode.catalogue.backends.generic import CatalogueBackend as GenericCatalogueBackend +from geonode.catalogue.backends.generic import METADATA_FORMATS MD_CORE_MODEL = { 'typename': 'pycsw:CoreMetadata', @@ -94,3 +101,90 @@ def remove_record(self, uuid): def create_record(self, item): pass + def get_record(self, uuid): + results = self._csw_local_dispatch(identifier=uuid) + if len(results) < 1: + return None + + result = etree.fromstring(results).find('{http://www.isotc211.org/2005/gmd}MD_Metadata') + + if result is None: + return None + + record = MD_Metadata(result) + record.keywords = [] + if hasattr(record, 'identification') and hasattr(record.identification, 'keywords'): + for kw in record.identification.keywords: + record.keywords.extend(kw['keywords']) + + record.links = {} + record.links['metadata'] = self.catalogue.urls_for_uuid(uuid) + record.links['download'] = self.catalogue.extract_links(record) + return record + + def search_records(self, keywords, start, limit, bbox): + with self.catalogue: + lresults = self._csw_local_dispatch(keywords, keywords, start+1, limit, bbox) + # serialize XML + e = etree.fromstring(lresults) + self.catalogue.records = [MD_Metadata(x) for x in e.findall('//{http://www.isotc211.org/2005/gmd}MD_Metadata')] + + # build results into JSON for API + results = [self.catalogue.metadatarecord2dict(doc) for v, doc in self.catalogue.records.iteritems()] + + result = { + 'rows': results, + 'total': e.find('{http://www.opengis.net/cat/csw/2.0.2}SearchResults').attrib.get('numberOfRecordsMatched'), + 'next_page': e.find('{http://www.opengis.net/cat/csw/2.0.2}SearchResults').attrib.get('nextRecord') + } + + return result + + + def _csw_local_dispatch(self, keywords=None, start=0, limit=10, bbox=None, identifier=None): + """ + HTTP-less CSW + """ + # set up configuration + config = SafeConfigParser() + + for section, options in settings.PYCSW['CONFIGURATION'].iteritems(): + config.add_section(section) + for option, value in options.iteritems(): + config.set(section, option, value) + + # fake HTTP environment variable + os.environ['QUERY_STRING'] = '' + + # init pycsw + csw = server.Csw(config) + + # fake HTTP method + csw.requesttype = 'POST' + + # fake HTTP request parameters + if identifier is None: # it's a GetRecords request + formats = [] + for f in self.catalogue.formats: + formats.append(METADATA_FORMATS[f][0]) + + csw.kvp = { + 'elementsetname': 'full', + 'typenames': formats, + 'resulttype': 'results', + 'constraintlanguage': 'CQL_TEXT', + 'constraint': 'csw:AnyText like "%%%s%%"' % keywords, + 'outputschema': 'http://www.isotc211.org/2005/gmd', + 'constraint': None, + 'startposition': start, + 'maxrecords': limit + } + response = csw.getrecords() + else: # it's a GetRecordById request + csw.kvp = { + 'id': [identifier], + 'outputschema': 'http://www.isotc211.org/2005/gmd', + } + response = csw.getrecordbyid() + + return etree.tostring(response) diff --git a/geonode/catalogue/views.py b/geonode/catalogue/views.py index 38ae9179db7..1bfe0f18846 100644 --- a/geonode/catalogue/views.py +++ b/geonode/catalogue/views.py @@ -61,40 +61,3 @@ def csw_global_dispatch(request): content = csw.dispatch_wsgi() return HttpResponse(content, content_type=csw.contenttype) - -def csw_local_dispatch(request): - """ - HTTP-less CSW - """ - # set up configuration - config = SafeConfigParser() - - for section, options in settings.PYCSW['CONFIGURATION'].iteritems(): - config.add_section(section) - for option, value in options.iteritems(): - config.set(section, option, value) - - # fake HTTP environment variable - os.environ['QUERY_STRING'] = '' - - # init pycsw - csw = server.Csw(config) - - # fake HTTP method - csw.requesttype = request.method.upper() - - # fake HTTP request parameters - csw.kvp = { - 'elementsetname': 'brief', - 'typenames': 'csw:Record', - 'resulttype': 'results', - 'constraintlanguage': 'CQL_TEXT', - #'constraint': 'csw:AnyText like "%iLor%"', - 'constraint': None, - 'maxrecords': '2' - } - - response = csw.getrecords() - response_string = etree.tostring(response) - - return HttpResponse(response, content_type=csw.contenttype) From 0fa4db1d702ed56fddeb4c5321f827c22b392330 Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Wed, 12 Sep 2012 15:49:31 +0000 Subject: [PATCH 60/60] rename pycsw.py to pycsw_http.py; this was tripping up code importing pycsw proper for local dispatching --- geonode/catalogue/backends/{pycsw.py => pycsw_http.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename geonode/catalogue/backends/{pycsw.py => pycsw_http.py} (100%) diff --git a/geonode/catalogue/backends/pycsw.py b/geonode/catalogue/backends/pycsw_http.py similarity index 100% rename from geonode/catalogue/backends/pycsw.py rename to geonode/catalogue/backends/pycsw_http.py