Coverage for gws-app/gws/base/search/finder.py: 78%

77 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-16 22:59 +0200

1from typing import Optional, cast 

2 

3import gws 

4import gws.base.model 

5import gws.base.template 

6import gws.base.shape 

7import gws.config.util 

8import gws.lib.uom 

9 

10 

11class SpatialContext(gws.Enum): 

12 """Spatial context for keyword searches.""" 

13 

14 map = 'map' 

15 """Search in the map extent.""" 

16 view = 'view' 

17 """Search in the client view extent.""" 

18 

19 

20class Config(gws.ConfigWithAccess): 

21 """Finder configuration.""" 

22 

23 models: Optional[list[gws.ext.config.model]] 

24 """Data models for features.""" 

25 spatialContext: Optional[SpatialContext] = SpatialContext.map 

26 """Spatial context for keyword searches.""" 

27 templates: Optional[list[gws.ext.config.template]] 

28 """Feature formatting templates.""" 

29 title: Optional[str] 

30 """Finder title.""" 

31 category: Optional[str] 

32 """Category for the finder. (added in 8.2)""" 

33 withGeometry: bool = True 

34 """Enable geometry search.""" 

35 withKeyword: bool = True 

36 """Enable keyword search.""" 

37 withFilter: bool = True 

38 """Enable filter search.""" 

39 

40 

41class Object(gws.Finder): 

42 spatialContext: SpatialContext 

43 

44 def configure(self): 

45 self.templates = [] 

46 self.models = [] 

47 

48 self.withKeyword = self.cfg('withKeyword', default=True) 

49 self.withGeometry = self.cfg('withGeometry', default=True) 

50 self.withFilter = self.cfg('withFilter', default=True) 

51 

52 self.spatialContext = self.cfg('spatialContext', default=SpatialContext.map) 

53 self.title = self.cfg('title', default='') 

54 self.category = self.cfg('category', default='') 

55 

56 ## 

57 

58 def configure_models(self): 

59 return gws.config.util.configure_models_for(self) 

60 

61 def configure_templates(self): 

62 return gws.config.util.configure_templates_for(self) 

63 

64 ## 

65 

66 def can_run(self, search, user): 

67 has_param = False 

68 

69 if search.keyword: 

70 if not self.supportsKeywordSearch or not self.withKeyword: 

71 gws.log.debug(f'can run: False: {self} {search.keyword=} {self.supportsKeywordSearch=} {self.withKeyword=}') 

72 return False 

73 has_param = True 

74 

75 if search.shape: 

76 if not self.supportsGeometrySearch or not self.withGeometry: 

77 gws.log.debug(f'can run: False: {self} <shape> {self.supportsGeometrySearch=} {self.withGeometry=}') 

78 return False 

79 has_param = True 

80 

81 if search.filter: 

82 if not self.supportsFilterSearch or not self.withFilter: 

83 gws.log.debug(f'can run: False: {self} {search.filter=} {self.supportsFilterSearch=} {self.withFilter=}') 

84 return False 

85 has_param = True 

86 

87 return has_param 

88 

89 def context_shape(self, search: gws.SearchQuery) -> gws.Shape: 

90 if search.shape: 

91 return search.shape 

92 if self.spatialContext == SpatialContext.view and search.bounds: 

93 return gws.base.shape.from_bounds(search.bounds) 

94 if search.project: 

95 return gws.base.shape.from_bounds(search.project.map.bounds) 

96 

97 def run(self, search, user, layer=None): 

98 model = self.root.app.modelMgr.find_model(self, layer, user=user, access=gws.Access.read) 

99 if not model: 

100 gws.log.debug(f'no model for {user.uid=} in finder {self.uid!r}') 

101 return [] 

102 search = cast(gws.SearchQuery, gws.u.merge(search, shape=self.context_shape(search))) 

103 mc = gws.ModelContext(op=gws.ModelOperation.read, target=gws.ModelReadTarget.searchResults, user=user) 

104 return model.find_features(search, mc)