Coverage for gws-app/gws/base/search/action.py: 0%
76 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
1"""Search API."""
3from typing import Optional
5import gws
6import gws.base.model
7import gws.base.action
8import gws.base.template
9import gws.base.feature
10import gws.base.shape
11import gws.lib.uom
13gws.ext.new.action('search')
15_DEFAULT_VIEWS = ['title', 'teaser', 'description']
16_DEFAULT_TOLERANCE = 10, gws.Uom.px
19class Config(gws.base.action.Config):
20 """Search action"""
22 limit: int = 1000
23 """Search results limit."""
24 tolerance: Optional[gws.UomValueStr]
25 """Default tolerance."""
26 categories: Optional[list[str]]
27 """Search categories. (added in 8.2)"""
30class Props(gws.base.action.Props):
31 categories: list[str]
34class Request(gws.Request):
35 crs: Optional[gws.CrsName]
36 extent: Optional[gws.Extent]
37 keyword: str = ''
38 layerUids: list[str]
39 limit: Optional[int]
40 resolution: float
41 shapes: Optional[list[gws.base.shape.Props]]
42 tolerance: Optional[str]
43 views: Optional[list[str]]
44 categories: Optional[list[str]]
45 withCategories: bool
48class Response(gws.Response):
49 features: list[gws.FeatureProps]
52class Object(gws.base.action.Object):
53 limit = 0
54 tolerance: gws.UomValue
55 categories: list[str] = []
57 def configure(self):
58 self.limit = self.cfg('limit') or 0
59 self.tolerance = self.cfg('tolerance') or _DEFAULT_TOLERANCE
60 self.categories = self.cfg('categories') or []
62 def props(self, user):
63 return gws.u.merge(super().props(user), categories=self.categories)
65 @gws.ext.command.api('searchFind')
66 def find(self, req: gws.WebRequester, p: Request) -> Response:
67 """Perform a search"""
69 return Response(features=self._get_features(req, p))
71 def _get_features(self, req: gws.WebRequester, p: Request) -> list[gws.FeatureProps]:
72 project = req.user.require_project(p.projectUid)
73 search = gws.SearchQuery(project=project)
75 if p.layerUids:
76 search.layers = gws.u.compact(req.user.acquire(uid, gws.ext.object.layer) for uid in p.layerUids)
78 search.bounds = project.map.bounds
79 if p.extent:
80 search.bounds = gws.Bounds(crs=p.crs or project.map.bounds.crs, extent=p.extent)
82 search.limit = self.limit
83 if p.limit:
84 search.limit = min(int(p.limit), self.limit)
86 if p.shapes:
87 shapes = [gws.base.shape.from_props(s) for s in p.shapes]
88 search.shape = shapes[0] if len(shapes) == 1 else shapes[0].union(shapes[1:])
90 search.tolerance = self.tolerance
91 if p.tolerance:
92 search.tolerance = gws.lib.uom.parse(p.tolerance, gws.Uom.px)
94 if p.resolution:
95 search.resolution = p.resolution
97 if p.keyword.strip():
98 search.keyword = p.keyword.strip()
100 results = self.root.app.searchMgr.run_search(search, req.user)
101 if not results:
102 return []
104 mc = gws.ModelContext(op=gws.ModelOperation.read, target=gws.ModelReadTarget.searchResults, project=project, user=req.user)
106 for res in results:
107 templates = gws.u.compact(
108 self.root.app.templateMgr.find_template(f'feature.{v}', where=[res.finder, res.layer, project], user=req.user)
109 for v in p.views or _DEFAULT_VIEWS
110 )
111 res.feature.render_views(templates, user=req.user, project=project, layer=res.layer)
113 return [res.feature.model.feature_to_view_props(res.feature, mc) for res in results]