Package s3 :: Module s3summary
[frames] | no frames]

Source Code for Module s3.s3summary

  1  # -*- coding: utf-8 -*- 
  2   
  3  """ Resource Summary Pages 
  4   
  5      @copyright: 2013-2019 (c) Sahana Software Foundation 
  6      @license: MIT 
  7   
  8      @requires: U{B{I{gluon}} <http://web2py.com>} 
  9   
 10      Permission is hereby granted, free of charge, to any person 
 11      obtaining a copy of this software and associated documentation 
 12      files (the "Software"), to deal in the Software without 
 13      restriction, including without limitation the rights to use, 
 14      copy, modify, merge, publish, distribute, sublicense, and/or sell 
 15      copies of the Software, and to permit persons to whom the 
 16      Software is furnished to do so, subject to the following 
 17      conditions: 
 18   
 19      The above copyright notice and this permission notice shall be 
 20      included in all copies or substantial portions of the Software. 
 21   
 22      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 23      EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 24      OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 25      NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 26      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 27      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 28      FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 29      OTHER DEALINGS IN THE SOFTWARE. 
 30  """ 
 31   
 32  from gluon import current, A, DIV, LI, UL 
 33   
 34  from s3filter import S3FilterForm 
 35  from s3gis import MAP 
 36  from s3rest import S3Method 
37 38 # ============================================================================= 39 -class S3Summary(S3Method):
40 """ Resource Summary Pages """ 41 42 # -------------------------------------------------------------------------
43 - def apply_method(self, r, **attr):
44 """ 45 Entry point for REST interface 46 47 @param r: the S3Request 48 @param attr: controller attributes 49 """ 50 51 if "w" in r.get_vars: 52 # Ajax-request for a specific widget 53 return self.ajax(r, **attr) 54 else: 55 # Full page request 56 return self.summary(r, **attr)
57 58 # -------------------------------------------------------------------------
59 - def summary(self, r, **attr):
60 """ 61 Render the summary page 62 63 @param r: the S3Request 64 @param attr: controller attributes 65 """ 66 67 output = {} 68 response = current.response 69 resource = self.resource 70 get_config = resource.get_config 71 72 # Get Summary Page Configuration 73 config = self._get_config(resource) 74 75 # Page title 76 crud_string = self.crud_string 77 title = crud_string(self.tablename, "title_list") 78 output["title"] = title 79 80 # Tabs 81 tablist = UL() 82 sections = [] 83 commons = [] 84 85 # Active tab 86 if "t" in r.get_vars: 87 active_tab = int(r.get_vars["t"]) 88 else: 89 active_tab = 0 90 active_map = None 91 92 show_filter_form = False 93 filter_widgets = get_config("filter_widgets") 94 if filter_widgets and not self.hide_filter: 95 # Apply filter defaults (before rendering the data!) 96 show_filter_form = True 97 S3FilterForm.apply_filter_defaults(r, resource) 98 99 # Render sections 100 tab_idx = 0 101 widget_idx = 0 102 targets = [] 103 pending = [] 104 105 # Dynamic filtering (e.g. plot-click in report widget) 106 attr["filter_form"] = form_id = "summary-filter-form" 107 108 for section in config: 109 110 common = section.get("common") 111 112 # Section container 113 section_id = section["name"] 114 s = DIV(_class="section-container", _id=section_id) 115 116 if not common: 117 # Label 118 label = section["label"] 119 translate = section.get("translate", True) 120 if isinstance(label, basestring) and translate: 121 label = current.T(label) 122 123 # Add tab 124 tablist.append(LI(A(label, _href="#%s" % section_id))) 125 126 if common or active_tab == tab_idx: 127 visible = True 128 else: 129 visible = False 130 131 # Widgets 132 widgets = section.get("widgets", []) 133 for widget in widgets: 134 135 # Widget ID 136 widget_id = "summary-%s" % widget_idx 137 138 # Make sure widgets include the widget ID when 139 # generating Ajax URLs: 140 r.get_vars["w"] = r.vars["w"] = widget_id 141 142 # Append to filter targets 143 filterable = widget.get("filterable", True) 144 if filterable: 145 targets.append(widget_id) 146 if not visible and widget.get("ajax_init"): 147 pending.append(widget_id) 148 149 # Apply method 150 method = widget.get("method") 151 if callable(method): 152 content = method(r, 153 widget_id=widget_id, 154 visible=visible, 155 **attr) 156 else: 157 handler = r.get_widget_handler(method) 158 if handler is None: 159 # Fall back to CRUD 160 handler = resource.crud 161 if handler is not None: 162 if method == "datatable": 163 # Assume that we have a FilterForm, so disable Quick Search 164 dtargs = attr.get("dtargs", {}) 165 dtargs["dt_searching"] = "false" 166 attr["dtargs"] = dtargs 167 content = handler(r, 168 method=method, 169 widget_id=widget_id, 170 visible=visible, 171 **attr) 172 else: 173 r.error(405, current.ERROR.BAD_METHOD) 174 175 # Add content to section 176 if isinstance(content, dict): 177 if r.http == "POST" and content.get("success"): 178 # Form successfully processed: behave like the 179 # primary method handler and redirect to next 180 next_url = content.get("next") 181 if next_url: 182 self.next = next_url 183 return content 184 for k, v in content.items(): 185 if k not in ("tabs", "sections", "widget"): 186 output[k] = v 187 content = content.get("widget", "EMPTY") 188 elif isinstance(content, MAP) and (common or active_tab == tab_idx): 189 active_map = content 190 s.append(DIV(content, 191 _id="%s-container" % widget_id, 192 _class="widget-container", 193 )) 194 widget_idx += 1 195 196 if common: 197 commons.append(s) 198 else: 199 sections.append(s) 200 tab_idx += 1 201 202 # Remove widget ID 203 r.get_vars.pop("w", None) 204 205 # Add tabs + sections to output 206 if len(sections) > 1: 207 output["tabs"] = tablist 208 # Hide tabbed sections initially to avoid visible artifacts 209 # in slow page loads (S3.search.summary_tabs will un-hide the active one): 210 for s in sections: 211 s.add_class("hide") 212 else: 213 # Hide tabs if there's only one section 214 # - and don't hide the section 215 output["tabs"] = "" 216 output["sections"] = sections 217 218 # Add common sections to output 219 output["common"] = commons 220 221 # Filter targets 222 target = " ".join(targets) 223 224 # Filter form 225 filter_ajax = True 226 if show_filter_form: 227 228 # Where to retrieve filtered data from: 229 if active_tab != 0: 230 submit_url_vars = {"t": active_tab} 231 else: 232 submit_url_vars = {} 233 filter_submit_url = attr.get("filter_submit_url") 234 if not filter_submit_url: 235 _vars = self._remove_filters(r.get_vars) 236 _vars.update(submit_url_vars) 237 filter_submit_url = r.url(vars=_vars) 238 239 # Where to retrieve updated filter options from: 240 filter_ajax_url = attr.get("filter_ajax_url", 241 r.url(method="filter", 242 vars={}, 243 representation="options")) 244 245 filter_clear = get_config("filter_clear", 246 current.deployment_settings.get_ui_filter_clear()) 247 filter_formstyle = get_config("filter_formstyle") 248 filter_submit = get_config("filter_submit", True) 249 filter_form = S3FilterForm(filter_widgets, 250 clear=filter_clear, 251 formstyle=filter_formstyle, 252 submit=filter_submit, 253 ajax=filter_ajax, 254 url=filter_submit_url, 255 ajaxurl=filter_ajax_url, 256 _class="filter-form", 257 _id=form_id) 258 fresource = current.s3db.resource(resource.tablename) 259 260 alias = resource.alias if r.component else None 261 output["filter_form"] = filter_form.html(fresource, 262 r.get_vars, 263 target=target, 264 alias=alias) 265 else: 266 # Render as empty string to avoid the exception in the view 267 output["filter_form"] = "" 268 269 # View 270 response.view = self._view(r, "summary.html") 271 272 if len(sections) > 1: 273 # Provide a comma-separated list of initially hidden widgets 274 # which are rendered empty and need a trigger to Ajax-load 275 # their data layer (e.g. maps, reports): 276 pending = ",".join(pending) if pending else "null" 277 278 # Render the Sections as Tabs 279 script = '''S3.search.summary_tabs("%s",%s,"%s")''' % \ 280 (form_id, active_tab, pending) 281 else: 282 # Unhide initial section 283 script = '''S3.search.summary_tabs("%s")''' % form_id 284 285 response.s3.jquery_ready.append(script) 286 287 if active_map: 288 # If there is a map on the active tab then we need to add 289 # a callback to the Map JS Loader 290 active_map.callback = '''S3.search.summary_maps("%s")''' % form_id 291 292 return output
293 294 # -------------------------------------------------------------------------
295 - def ajax(self, r, **attr):
296 """ 297 Render a specific widget for pulling-in via AJAX 298 299 @param r: the S3Request 300 @param attr: controller attributes 301 """ 302 303 # Get Summary Page Configuration 304 config = self._get_config(self.resource) 305 306 widget_id = r.get_vars.get("w") 307 i = 0 308 for section in config: 309 widgets = section.get("widgets", []) 310 for widget in widgets: 311 if widget_id == "summary-%s" % i: 312 method = widget.get("method", None) 313 output = None 314 if callable(method): 315 output = method(r, widget_id=widget_id, **attr) 316 else: 317 handler = r.get_widget_handler(method) 318 if handler is not None: 319 output = handler(r, 320 method=method, 321 widget_id=widget_id, 322 **attr) 323 else: 324 r.error(405, current.ERROR.BAD_METHOD) 325 return output 326 i += 1 327 328 # Not found? 329 return None
330 331 # ------------------------------------------------------------------------- 332 @staticmethod
333 - def _get_config(resource):
334 """ 335 Get the summary page configuration 336 337 @param resource: the target S3Resource 338 """ 339 340 get_config = resource.get_config 341 config = get_config("summary", 342 current.deployment_settings.get_ui_summary()) 343 if not config: 344 config = [{"name": "table", 345 "label": "Table", 346 "widgets": [{"name": "datatable", 347 "method": "datatable", 348 }] 349 }] 350 return config
351 352 # END ========================================================================= 353