Coverage for gws-app/gws/base/application/core.py: 91%
147 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Application object and root configuration."""
3from typing import Optional
5import gws
6import gws.base.action
7import gws.base.application.middleware
8import gws.base.auth
9import gws.base.client
10import gws.base.database
11import gws.base.exporter
12import gws.base.job
13import gws.base.metadata
14import gws.base.model
15import gws.base.printer
16import gws.base.search
17import gws.base.storage
18import gws.base.template
19import gws.base.web
20import gws.config
21import gws.gis.cache
22import gws.lib.font
23import gws.server.manager
24import gws.server.monitor
26DEFAULT_LOCALE = ['en_CA']
28DEFAULT_TEMPLATE_SUBJECTS = [
29 'application.home',
30 'application.error',
31 'project.home',
32 'project.description',
33 'layer.description',
34 'feature.description',
35 'feature.title',
36 'feature.label',
37]
39DEFAULT_TEMPLATES = [
40 gws.Config(
41 type='html',
42 path=f'{gws.c.APP_DIR}/gws/base/application/templates/{_s.replace(".", "_")}.cx.html',
43 subject=_s,
44 access=gws.c.PUBLIC,
45 uid=f'gws.base.application.default_template.{_s}',
46 )
47 for _s in DEFAULT_TEMPLATE_SUBJECTS
48]
50DEFAULT_PRINTER = gws.Config(
51 uid='gws.base.application.default_printer',
52 access=gws.c.PUBLIC,
53 template=gws.Config(
54 type='html',
55 path=f'{gws.c.APP_DIR}/gws/base/application/templates/project_print.cx.html',
56 mapSize=(200, 180, gws.Uom.mm),
57 ),
58 qualityLevels=[gws.TemplateQualityLevel(dpi=72)],
59)
61DEFAULT_CSS_FILENAME = 'style.css'
63class Config(gws.ConfigWithAccess):
64 """Root configuration of the application."""
66 actions: Optional[list[gws.ext.config.action]]
67 """Server actions available in all projects."""
68 auth: Optional[gws.base.auth.manager.Config]
69 """Authentication methods, providers and session options."""
70 cache: Optional[gws.gis.cache.GlobalConfig]
71 """Options for tile cache seeding."""
72 client: Optional[gws.base.client.Config]
73 """Default client UI elements and options for all projects."""
74 database: Optional[gws.base.database.manager.Config]
75 """Database connections."""
76 developer: Optional[dict]
77 """Options for development and debugging, not for production use."""
78 exporters: Optional[list[gws.ext.config.exporter]]
79 """Formats for exporting features."""
80 finders: Optional[list[gws.ext.config.finder]]
81 """Search providers available in all projects."""
82 fonts: Optional[gws.lib.font.Config]
83 """Directory with additional fonts for rendering and printing."""
84 helpers: Optional[list[gws.ext.config.helper]]
85 """Auxiliary objects, used throughout the application."""
86 locales: Optional[list[gws.LocaleUid]]
87 """Default locales for all projects."""
88 metadata: Optional[gws.base.metadata.Config]
89 """Application metadata."""
90 models: Optional[list[gws.ext.config.model]]
91 """Data models available in all projects."""
92 owsServices: Optional[list[gws.ext.config.owsService]]
93 """OWS services provided by the server."""
94 projectDirs: Optional[list[gws.DirPath]]
95 """Directories searched recursively for project configuration files."""
96 projectPaths: Optional[list[gws.FilePath]]
97 """Additional project configuration files."""
98 printers: Optional[list[gws.ext.config.printer]]
99 """Configuration for print templates and quality levels."""
100 projects: Optional[list[gws.ext.config.project]]
101 """Projects, each with its own map, client and actions."""
102 server: Optional[gws.server.Config]
103 """Server processes, logging and monitoring options."""
104 storage: Optional[gws.base.storage.manager.Config]
105 """Storage providers for data that users save in the client."""
106 templates: Optional[list[gws.ext.config.template]]
107 """Templates available in all projects."""
108 templateOptions: Optional[gws.TemplateOptions]
109 """Login form, footer and extra resources of the built-in home pages."""
110 title: Optional[str]
111 """Application title."""
112 vars: Optional[dict]
113 """Custom variables."""
114 web: Optional[gws.base.web.manager.Config]
115 """Web site and SSL options."""
116 lastUid: int = 1
117 """Last used UID for auto-generated UIDs."""
120class Object(gws.Application):
121 """Application object."""
123 _helperMap: dict[str, gws.Node]
125 _developerOptions: dict
127 _supportedCrs: list[gws.Crs]
128 _registeredCrs: set[gws.Crs]
130 def configure(self):
131 self._registeredCrs = set()
133 self.version = self.root.specs.version
134 self.versionString = f'GWS version {self.version}'
136 if gws.u.is_file('/GWS_REVISION'):
137 self.versionString += ' (rev. ' + gws.u.read_file('/GWS_REVISION') + ')'
139 if not gws.env.GWS_IN_TEST:
140 gws.log.info('*' * 80)
141 gws.log.info(self.versionString)
142 gws.log.info('*' * 80)
144 self.vars = self.cfg('vars') or {}
146 self.title = self.cfg('title') or ''
148 self.serverMgr = self.create_child(gws.server.manager.Object, self.cfg('server'))
149 # NB need defaults from the server
150 self.config.server = self.serverMgr.config
152 gws.log.set_level(self.cfg('server.log.level'))
154 self._developerOptions = self.cfg('developer') or {}
155 if self._developerOptions:
156 gws.log.warning(f'developer mode enabled: {self._developerOptions}')
158 self.monitor = self.create_child(gws.server.monitor.Object, self.serverMgr.cfg('monitor'))
160 self.localeUids = self.cfg('locales') or DEFAULT_LOCALE
161 self.metadata = gws.base.metadata.from_config(self.cfg('metadata'))
163 self.middlewareMgr = self.create_child(gws.base.application.middleware.Object)
165 p = self.cfg('fonts')
166 if p:
167 gws.lib.font.configure(p)
169 # NB the order of initialization is important
170 # - db
171 # - helpers
172 # - auth providers
173 # - actions, client, web
174 # - finally, projects
176 self.databaseMgr = self.create_child(gws.base.database.manager.Object, self.cfg('database'))
178 helpers = self.create_children(gws.ext.object.helper, self.cfg('helpers'))
179 self._helperMap = {p.extType: p for p in helpers}
181 self.storageMgr = self.create_child(gws.base.storage.manager.Object, self.cfg('storage'))
182 self.authMgr = self.create_child(gws.base.auth.manager.Object, self.cfg('auth'))
184 # @TODO default API
185 self.actionMgr = self.create_child(gws.base.action.manager.Object)
186 self.actions = self.create_children(gws.ext.object.action, self.cfg('actions'))
188 self.webMgr = self.create_child(gws.base.web.manager.Object, self.cfg('web'))
190 self.searchMgr = self.create_child(gws.base.search.manager.Object)
191 self.finders = self.create_children(gws.ext.object.finder, self.cfg('finders'))
193 self.modelMgr = self.create_child(gws.base.model.manager.Object)
194 self.models = self.create_children(gws.ext.object.model, self.cfg('models'))
196 self.templateMgr = self.create_child(gws.base.template.manager.Object)
197 self.templates = self.create_children(gws.ext.object.template, self.cfg('templates'))
198 for cfg in DEFAULT_TEMPLATES:
199 self.templates.append(self.root.create_shared(gws.ext.object.template, cfg))
201 p = self.cfg('templateOptions')
202 defaults = gws.TemplateOptions(
203 withLogin = any(a.extType == 'auth' for a in self.actions),
204 homeResources = [],
205 projectResources = [],
206 )
207 if gws.u.is_file(self.webMgr.site.staticRoot.dir + f'/{DEFAULT_CSS_FILENAME}'):
208 defaults.homeResources = [f'/{DEFAULT_CSS_FILENAME}']
209 defaults.projectResources = [f'/{DEFAULT_CSS_FILENAME}']
211 self.templateOptions = gws.u.merge(defaults, self.cfg('templateOptions'))
213 self.exporterMgr = self.create_child(gws.base.exporter.manager.Object)
214 self.exporters = self.create_children(gws.ext.object.exporter, self.cfg('exporters'))
216 self.jobMgr = self.create_child(gws.base.job.manager.Object)
218 self.printerMgr = self.create_child(gws.base.printer.manager.Object)
219 self.printers = self.create_children(gws.ext.object.printer, self.cfg('printers'))
220 self.defaultPrinter = self.root.create_shared(gws.ext.object.printer, DEFAULT_PRINTER)
222 self.owsServices = self.create_children(gws.ext.object.owsService, self.cfg('owsServices'))
224 self.client = self.create_child(gws.base.client.Object, self.cfg('client'))
226 self.projects = self.create_children(gws.ext.object.project, self.cfg('projects'))
228 def activate(self):
229 gws.log.set_level(self.cfg('server.log.level'))
231 for p in self.root.configPaths:
232 if p.startswith(gws.c.CONFIG_DIR):
233 # do not watch derived config paths
234 continue
235 self.monitor.watch_file(p)
236 for d in self.config.get('projectDirs') or []:
237 self.monitor.watch_directory(d, gws.config.CONFIG_PATH_PATTERN)
239 def project(self, uid):
240 for p in self.projects:
241 if p.uid == uid:
242 return p
244 def register_supported_crs(self, crs):
245 self._registeredCrs.add(crs)
246 if hasattr(self, '_supportedCrs'):
247 del self._supportedCrs
249 def supported_crs(self):
250 if not hasattr(self, '_supportedCrs'):
251 crs_set = set(self._registeredCrs)
252 for m in self.root.find_all(gws.ext.object.map):
253 crs_set.add(m.bounds.crs)
254 for s in self.root.find_all(gws.ext.object.owsService):
255 for b in s.supportedBounds:
256 crs_set.add(b.crs)
257 self._supportedCrs = sorted(crs_set, key=lambda c: c.srid)
258 return self._supportedCrs
260 def helper(self, ext_type):
261 if ext_type not in self._helperMap:
262 p = self.create_child(gws.ext.object.helper, type=ext_type)
263 if not p:
264 raise gws.Error(f'helper {ext_type!r} not found')
265 gws.log.info(f'created helper {ext_type!r}')
266 self._helperMap[ext_type] = p
267 return self._helperMap.get(ext_type)
269 def developer_option(self, key):
270 return self._developerOptions.get(key)