Coverage for gws-app/gws/base/ows/client/operation.py: 98%
62 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +0200
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +0200
1"""OWS operations of remote services."""
3from typing import Optional
5import gws
6import gws.lib.mime
9class Config(gws.Config):
10 """OWS operation that overrides or adds to those in the service capabilities."""
12 verb: gws.OwsVerb
13 """OWS request type."""
14 formats: Optional[list[str]]
15 """MIME types supported by the operation."""
16 params: Optional[dict]
17 """Extra parameters added to GET requests for this operation."""
18 postUrl: Optional[gws.Url]
19 """URL for POST requests."""
20 url: Optional[gws.Url]
21 """URL for GET requests."""
24def create(
25 verb: gws.OwsVerb,
26 op_from_caps: Optional[gws.OwsOperation] = None,
27 cfgs: Optional[list[Config]] = None,
28) -> gws.OwsOperation:
29 """Create an operation from the capabilities and the configuration.
31 All fields are initialized. Attributes of the operations with the same verb in the
32 configuration replace those of ``op_from_caps``. The default MIME type is the first
33 configured one, if ``formats`` are configured, otherwise selected from the supported ones.
35 Args:
36 verb: Request type.
37 op_from_caps: Operation read from the capabilities, with MIME types as spelled by the service.
38 cfgs: Operation configurations.
40 Returns:
41 The operation.
42 """
44 co = op_from_caps or gws.OwsOperation()
45 op = gws.OwsOperation(
46 allowedParameters=co.allowedParameters or {},
47 constraints=co.constraints or {},
48 mimeTypeMap=dict(co.mimeTypeMap or {}),
49 mimeTypes=list(co.mimeTypes or []),
50 params=co.params or {},
51 postUrl=co.postUrl or '',
52 url=co.url or '',
53 verb=verb,
54 )
56 if op.mimeTypes:
57 _set_mime_types(op, op.mimeTypes)
59 has_formats_config = False
61 if cfgs:
62 for c in cfgs:
63 if c.get('verb') != verb:
64 continue
65 p = c.get('params')
66 if p is not None:
67 op.params = p
68 p = c.get('postUrl')
69 if p is not None:
70 op.postUrl = p
71 p = c.get('url')
72 if p is not None:
73 op.url = p
74 p = c.get('formats')
75 if p is not None:
76 _set_mime_types(op, p)
77 has_formats_config = True
79 if has_formats_config and op.mimeTypes:
80 op.defaultMimeType = op.mimeTypes[0]
81 else:
82 op.defaultMimeType = _default_mime_type(op)
84 return op
87def _set_mime_types(op: gws.OwsOperation, values: list[str]):
88 """Set the MIME types of an operation from the values a service offers.
90 ``mimeTypes`` gets the values normalized with ``gws.lib.mime.get`` (aliases resolved), ``mimeTypeMap``
91 maps those spelled differently by the service to the service spelling. Unknown MIME types are ignored.
93 Args:
94 op: Operation.
95 values: MIME types as spelled by the service.
96 """
98 op.mimeTypes = []
99 op.mimeTypeMap = {}
100 for s in values:
101 mime = gws.lib.mime.get(s)
102 if not mime:
103 gws.log.warning(f'{op.verb}: unknown MIME type {s!r} ignored')
104 continue
105 if mime in op.mimeTypes:
106 continue
107 op.mimeTypes.append(mime)
108 if s != mime:
109 op.mimeTypeMap[mime] = s
112_PREFERRED_MIME_TYPES = {
113 'image': [
114 gws.lib.mime.PNG8,
115 gws.lib.mime.PNG24,
116 gws.lib.mime.PNG,
117 gws.lib.mime.JPEG,
118 ],
119 'text': [
120 gws.lib.mime.GML3,
121 gws.lib.mime.GML32,
122 gws.lib.mime.GML2,
123 gws.lib.mime.GML,
124 gws.lib.mime.XML,
125 ],
126}
128_IMAGE_VERBS = {
129 gws.OwsVerb.GetLegendGraphic,
130 gws.OwsVerb.GetMap,
131 gws.OwsVerb.GetTile,
132}
135def _default_mime_type(op: gws.OwsOperation) -> str:
136 """Pick the default MIME type of an operation.
138 The first preferred MIME type the operation supports is selected. Otherwise, the first supported
139 MIME type with the same base type as a preferred one (``image/png; mode=16bit`` for ``image/png``),
140 otherwise the first supported one. Without supported MIME types, the first preferred base type is used.
141 """
143 best_mimes = _PREFERRED_MIME_TYPES['image' if op.verb in _IMAGE_VERBS else 'text']
145 if not op.mimeTypes:
146 return gws.lib.mime.get_base(best_mimes[0])
148 for mime in best_mimes:
149 if mime in op.mimeTypes:
150 return mime
152 for mime in op.mimeTypes:
153 if gws.lib.mime.get_base(mime) in best_mimes:
154 return mime
156 return op.mimeTypes[0]