source: cows/trunk/cows/pylons/ows_controller.py @ 4008

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/pylons/ows_controller.py@4008
Revision 4008, 8.1 KB checked in by spascoe, 11 years ago (diff)

New COWS distribution. See [4005] for description.

Line 
1"""
2Base controller for OGC Web Services (OWS).
3
4@author: Stephen Pascoe
5@todo: Add pluggable security so replicate what was previously implemented
6    for the NDG discovery portal in ows_server.lib.BaseController.
7"""
8
9
10from pylons import request, response, config, c
11from pylons.controllers import WSGIController
12from pylons.templating import render
13from webhelpers import url_for
14
15from cows import exceptions as OWS_E
16from cows.util import negotiate_version, check_updatesequence
17from cows.builder import loadConfigFile
18from cows import helpers
19
20from cows.model import *
21
22from genshi.template import TemplateLoader
23from pkg_resources import resource_filename
24
25try:
26    from xml.etree import ElementTree as ET
27except ImportError:
28    from elementtree import ElementTree as ET
29
30import logging
31logger = logging.getLogger(__name__)
32
33# Instantiate Genshi template loader
34templateLoader = TemplateLoader(
35    resource_filename('cows.pylons', 'templates'),
36    auto_reload=True,
37    )
38
39# Configure
40#!TODO: rename this configuration object to something non-NDG specific
41#EXCEPTION_TYPE = request.environ['ndgConfig'].get('OWS_SERVER', 'exception_type', 'ogc').lower()
42EXCEPTION_TYPE = config.get('ows_server.exception_type', 'ogc')
43
44
45
46class OWSControllerBase(WSGIController):
47    """
48    @ivar owsParams: A dictionary of parameters passed to the service.
49        Initially these comes from the query string but could come from
50        a HTTP POST in future.
51    @cvar owsOperations: A list of operation names
52   
53    """
54
55    owsOperations = []
56   
57    def __call__(self, environ, start_response):
58
59        self._loadOwsParams()
60
61        # If the EXCEPTION_TYPE is 'pylons' let Pylons catch any exceptions.
62        # Otherwise send an OGC exception report for any OWS_E.OwsError
63        if 'pylons' in EXCEPTION_TYPE:
64            self._fixOwsAction(environ)
65            return super(OWSControllerBase, self).__call__(environ, start_response)
66        else:
67            try:
68                self._fixOwsAction(environ)
69                return super(OWSControllerBase, self).__call__(environ, start_response)
70            except OWS_E.OwsError, e:
71                logger.exception(e)
72
73                tmpl = templateLoader.load('exception_report.xml')
74                response.write(tmpl.generate(report=e.report).render('xml'))
75                response.headers['content-type'] = 'text/xml'
76                return response
77
78    def _loadOwsParams(self):
79        # All OWS parameter names are case insensitive.
80        self._owsParams = {}
81        print request
82        #if request.has_key('REQUEST'):
83        #print request['REQUEST']
84        for k in request.params:
85            if k.lower() == 'x':
86                self._owsParams['i'] = request.params[k]
87            elif k.lower() == 'y':
88                self._owsParams['j'] = request.params[k]
89            else:
90                self._owsParams[k.lower()] = request.params[k]
91        if 'info_format' not in request.params:
92                self._owsParams['info_format'] = 'text/html'
93            #print [k, request.params[k]]
94
95    def _fixOwsAction(self, environ):
96        rdict = environ['pylons.routes_dict']
97       
98        # Override the Routes action from the request query parameter
99        action = self.getOwsParam('request')
100
101        # Check action is a method in self and is defined as an OWS operation
102        if action not in self.owsOperations:
103            raise OWS_E.InvalidParameterValue('request=%s not supported' % action,
104                                              'REQUEST')
105        rdict['action'] = action
106
107    def getOwsParam(self, param, **kwargs):
108        """
109        Returns the value of a OWS parameter passed to the operation.
110        If argv['default'] is given it is taken to be the default
111        value otherwise the parameter is treated as manditory and an
112        exception is raised if the parameter is not present.
113
114        """
115        try:
116            return self._owsParams[param.lower()]
117        except KeyError:
118            if 'default' in kwargs:
119                return kwargs['default']
120            else:
121                raise OWS_E.MissingParameterValue('%s parameter is not specified' % param,
122                                                  param)
123
124#-----------------------------------------------------------------------------
125# Functions that populate c.capabilities
126
127def addOperation(opName, formats=[]):
128    ops = c.capabilities.operationsMetadata.operationDict
129    ops[opName] = helpers.operation(url_for(qualified=True, action="index")+'?', formats=formats)
130
131def addLayer(name, title, abstract, srss, bbox, dimensions={}):
132    """
133    @param dimensions: Dictionary of dictionaries D[k1][k2]=val where
134        k1 is dimension name, k2 is a keyword parameter to send to
135        helpers.wms_dimension and val is it's value.
136
137    @todo: The helpers interface is leaking through.  Could make cleaner.
138
139    """
140       
141    if c.capabilities.contents is None:
142        c.capabilities.contents = Contents()
143
144    layer = helpers.wms_layer(name, title, srss, bbox, abstract)
145
146    for k1, kwargs in dimensions.items():
147        dim = helpers.wms_dimension(**kwargs)
148        layer.dimensions[k1] = dim
149
150    c.capabilities.contents.datasetSummaries.append(layer)
151
152def initCapabilities():
153    """
154    Initialise the capabilities object c.capabilities.
155
156    By default the server-wide configuration file is loaded and
157    used to populate some standard metadata.  The GetCapabilites
158    operation is added.
159
160    """
161    # Load the basic ServiceMetadata from a config file
162    configFile = config.get('ows_server.capabilities_config')
163    if configFile is None:
164        raise RuntimeError('No OWS configuration file')
165   
166    c.capabilities = loadConfigFile(configFile)
167
168    om = OperationsMetadata(operationDict={})
169    c.capabilities.operationsMetadata = om
170
171    addOperation('GetCapabilities', formats=['text/xml'])
172
173#-----------------------------------------------------------------------------
174
175class OWSController(OWSControllerBase):
176    """
177    Adds basic GetCapabilities response to OWSControllerBase.
178
179    @cvar service: If None does not enforce the SERVICE parameter.  Otherwise
180        raises exception if SERVICE is not correct on GetCapabilities request.
181    @cvar validVersions: A list of supported version numbers.  Automatic
182        version negotiation is performed according to this attribute.
183   
184    @ivar updateSequence: None if cache-control is not supported or an
185        updateSequence identifier.  This attribute should be set in the
186        controller's __before__() method.
187    """
188
189    owsOperations = ['GetCapabilities']
190
191    # Override these attributes to control how OWSController responds to
192    # GetCapabilities
193    service = None
194    validVersions = NotImplemented
195
196    # To enable cache control set this instance attribute in self.__before__().
197    updateSequence = None
198   
199    def GetCapabilities(self):
200
201        # Retrieve Operation parameters
202        service = self.getOwsParam('service')
203        version = self.getOwsParam('version', default=None)
204        format = self.getOwsParam('format', default='text/xml')
205        updateSequence = self.getOwsParam('updatesequence', default=None)
206
207        # Check update sequence
208        check_updatesequence(self.updateSequence, updateSequence)
209
210        # Do version negotiation
211        version = negotiate_version(self.validVersions, version)
212
213        # Get information required for the capabilities document
214        initCapabilities()
215        self._loadCapabilities()
216       
217        # Render the capabilities document       
218        response.headers['content-type'] = format
219        return self._renderCapabilities(version, format)
220
221
222    def _loadCapabilities(self):
223        """
224        Override in subclasses to populate c.capabilities with
225        operation and contents metadata.
226
227        """
228        pass
229
230    def _renderCapabilities(self, version, format):
231        """
232        Override in subclases to render the capabilities document.
233        The response mime-type will already have been set.  Raise an
234        OWS exception if the format is not supported.
235
236        @param version: the version as a string
237        @param format: the format as a string
238       
239        @return: a template as expected by pylons.render()
240        """
241        raise NotImplementedError
242
243   
244
Note: See TracBrowser for help on using the repository browser.