Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pycsw #387

Merged
merged 87 commits into from
Sep 12, 2012
Merged

pycsw #387

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
1d45e68
start pycsw integration
tomkralidis Aug 10, 2012
8543bb5
bring back test_layer_delete_from_catalogue()
tomkralidis Aug 11, 2012
7fafeb0
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 11, 2012
ec6db09
make pycsw default catalogue
tomkralidis Aug 11, 2012
0b8a74e
update testing instructions
tomkralidis Aug 11, 2012
e2e4548
remove line not needed
tomkralidis Aug 11, 2012
baa5bc3
add ref to geonode.catalogue.urls
tomkralidis Aug 11, 2012
8854f66
a./ make FORMATS implementation specific b./ only do CSW-T when CSW i…
tomkralidis Aug 11, 2012
cb45f9b
add Dublin Core for GN
tomkralidis Aug 11, 2012
92cd473
update CSW URL
tomkralidis Aug 11, 2012
805da35
add Atom and ebRIM
tomkralidis Aug 11, 2012
e5fb8ce
add pycsw configuration
tomkralidis Aug 11, 2012
9c69afc
set .local from config, not per backend
tomkralidis Aug 11, 2012
da94b77
move pycsw settings to settings.PYCSW
tomkralidis Aug 11, 2012
a725fcb
refactor checking for local repo
tomkralidis Aug 11, 2012
33ce9b2
s/settings.CATALOGUE/settings.PYCSW/
tomkralidis Aug 11, 2012
beb27d8
fix ref
tomkralidis Aug 11, 2012
ada015c
tighten is_local_repo function
tomkralidis Aug 12, 2012
7bf580b
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 12, 2012
45cfc69
put mappings in backend
tomkralidis Aug 12, 2012
600d159
put mappings in backend
tomkralidis Aug 12, 2012
f71e8e2
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 12, 2012
dbd8212
keep geonode.catalogue optional for now
tomkralidis Aug 12, 2012
23c56a5
undo this until pushing to dev
tomkralidis Aug 12, 2012
8b6b66f
fix ref
tomkralidis Aug 12, 2012
5f404da
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 12, 2012
b6eaa29
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 12, 2012
d85a1ab
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 12, 2012
10666c1
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 12, 2012
001540c
add pycsw to default componenets
tomkralidis Aug 12, 2012
0c4e39a
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 12, 2012
8090af3
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 13, 2012
dcf43d1
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 13, 2012
226f32a
set self.catalogue.type automagically
tomkralidis Aug 13, 2012
d509042
add resource-base branch
tomkralidis Aug 14, 2012
7f69f3c
add hierarchy levels
tomkralidis Aug 14, 2012
def285f
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 14, 2012
c01c50f
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 14, 2012
628d637
add migration
tomkralidis Aug 15, 2012
b9a8435
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 15, 2012
b69bc38
add migration for this branch
tomkralidis Aug 15, 2012
e534bb4
add __init__.py
tomkralidis Aug 15, 2012
bf373f8
fix typo
tomkralidis Aug 15, 2012
78103f0
abstract XML generation
tomkralidis Aug 16, 2012
a3c989b
add initial CSW fields (first pass)
tomkralidis Aug 16, 2012
d700041
add pycsw_local catalogue backend
tomkralidis Aug 16, 2012
0844b54
s/keywords/keywords_csv/
tomkralidis Aug 16, 2012
6194428
break out pycsw as 2 backends (pycsw, pycsw_local)
tomkralidis Aug 16, 2012
93d5ad8
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 16, 2012
7f4c757
remove PYCSW[LOCAL] (implicit to the backend), update backend
tomkralidis Aug 16, 2012
34a01d2
Merge branch 'pycsw' of github.com:tomkralidis/geonode into pycsw
tomkralidis Aug 16, 2012
f287b6f
update mappings
tomkralidis Aug 16, 2012
f4c97e0
add model fields
tomkralidis Aug 16, 2012
53c94e7
update layer metadata editor form enums and presentables
tomkralidis Aug 16, 2012
8f09fa3
add merge
tomkralidis Aug 16, 2012
4796482
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 17, 2012
78391dd
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 17, 2012
77cd521
revert typo
tomkralidis Aug 17, 2012
04320fb
fix ref
tomkralidis Aug 17, 2012
02b8c3b
update tests given pycsw_local backend
tomkralidis Aug 17, 2012
fe3bc4d
tighten CSW endpoint
tomkralidis Aug 18, 2012
d2e79a5
tighten endpoint
tomkralidis Aug 18, 2012
d2d626d
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 18, 2012
21a69d9
remove extra def of CSW URL
tomkralidis Aug 18, 2012
8b91c7e
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 20, 2012
1bf1638
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 24, 2012
5b5c9cc
make URLs longer
tomkralidis Aug 27, 2012
1cd1ff2
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Aug 27, 2012
faf67a8
Merge branch 'dev' of https://github.com/GeoNode/geonode into pycsw
tomkralidis Aug 30, 2012
4ac12a5
per resource-base PR
tomkralidis Aug 31, 2012
018f59b
remove migration
tomkralidis Aug 31, 2012
01756aa
remove unused line
tomkralidis Aug 31, 2012
ede6b6d
remove migration
tomkralidis Aug 31, 2012
a431636
add back in csw fields
tomkralidis Aug 31, 2012
64a8e78
fix indent
tomkralidis Aug 31, 2012
d6ca396
add pycsw
tomkralidis Aug 31, 2012
7f1eaed
update models
tomkralidis Aug 31, 2012
15fa3a2
set pycsw version
tomkralidis Aug 31, 2012
be59c5c
add Shapely
tomkralidis Aug 31, 2012
3e10e4a
fix setup.py
tomkralidis Aug 31, 2012
85cd0dd
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Sep 1, 2012
04c82e8
update pycsw version
tomkralidis Sep 3, 2012
f8eca4e
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Sep 6, 2012
bf133c0
bump pycsw to 1.4.0
tomkralidis Sep 10, 2012
f07a1ea
Merge branch 'dev' of github.com:GeoNode/geonode into pycsw
tomkralidis Sep 12, 2012
cedb12a
make pycsw local requests
tomkralidis Sep 12, 2012
0fa4db1
rename pycsw.py to pycsw_http.py; this was tripping up code importing…
tomkralidis Sep 12, 2012
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion geonode/catalogue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 16 additions & 10 deletions geonode/catalogue/backends/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,21 @@
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'),
}



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.type = kwargs['ENGINE'].split('.')[-1]
self.local = False
self._group_ids = {}
self._operation_ids = {}
self.connected = False
Expand Down Expand Up @@ -122,8 +123,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'
Expand All @@ -136,6 +136,16 @@ def csw_request(self, layer, template):
})
md_doc = tpl.render(ctx)
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)

if self.type == 'geonetwork':
headers = {
Expand Down Expand Up @@ -356,8 +366,6 @@ def extract_links(self, rec):
pass
return links



class CatalogueBackend(BaseCatalogueBackend):
def __init__(self, *args, **kwargs):
self.catalogue = Catalogue(*args, **kwargs)
Expand All @@ -371,7 +379,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)
Expand All @@ -388,13 +395,12 @@ 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
Expand Down
2 changes: 1 addition & 1 deletion geonode/catalogue/backends/geonetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +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']
26 changes: 26 additions & 0 deletions geonode/catalogue/backends/pycsw_http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#########################################################################
#
# 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 <http://www.gnu.org/licenses/>.
#
#########################################################################

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.formats = ['Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'TC211']
190 changes: 190 additions & 0 deletions geonode/catalogue/backends/pycsw_local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#########################################################################
#
# 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 <http://www.gnu.org/licenses/>.
#
#########################################################################

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',
'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': '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': 'csw_wkt_geometry',
'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']
self.catalogue.local = True

def remove_record(self, uuid):
pass

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)
11 changes: 11 additions & 0 deletions geonode/catalogue/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions geonode/catalogue/urls.py
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
#
#########################################################################

from django.conf.urls.defaults import patterns, url

urlpatterns = patterns('geonode.catalogue.views',
url(r'^csw$', 'csw_global_dispatch', name='csw_global_dispatch'),
)
Loading