def _groups(): """ Committees/Mechanisms/Forums & Networks - Filtered subset of Organisations """ from s3 import S3FieldSelector, s3_request T = current.T s3db = current.s3db table = s3db.org_organisation table.address = Field.Lazy(s3db.org_organisation_address) s3request = s3_request("org", "organisation", extension="aadata") #(S3FieldSelector("project.id") != None) & \ f = (S3FieldSelector("organisation_type_id$name").anyof(["Committees/Mechanism/Forum", "Network"])) s3request.resource.add_filter(f) field_list = [ "id", "name", "acronym", (T("Type"), "organisation_type_id"), "year", (T("Address"), "address"), (T("Notes"), "comments"), ] return (s3request, field_list)
def _groups(): """ Committees/Mechanisms/Forums & Networks - Filtered subset of Organisations """ from s3 import S3FieldSelector, s3_request T = current.T s3db = current.s3db table = s3db.org_organisation table.virtualfields.append(s3db.org_organisation_address_virtual_field()) s3request = s3_request("org", "organisation", extension="aadata") #(S3FieldSelector("project.id") != None) & \ f = (S3FieldSelector("organisation_type_id$name").anyof(["Committees/Mechanism/Forum", "Network"])) s3request.resource.add_filter(f) field_list = [ "id", "name", "acronym", (T("Type"), "organisation_type_id"), "year", (T("Address"), "address"), (T("Notes"), "comments"), ] return (s3request, field_list)
def parameter(): """ REST controller for budget parameters - should always be 1 & only 1 record """ s3db.configure( "budget_parameter", deletable=False, ) table = s3db.budget_parameter record = db().select(table.id, limitby=(0, 1)).first() if not record: record_id = table.insert() else: record_id = record.id def postp(r, output): if isinstance(output, dict) and "buttons" in output: output["buttons"].pop("list_btn", None) return output s3.postp = postp from s3 import s3_request r = s3_request(args=[str(record_id)]) return r()
def __call__(self): auth = current.auth if not auth.s3_logged_in(): auth.permission.fail() # Use custom method current.s3db.set_method( "pr", "person", method="dashboard", action=PersonalDashboard, ) # Call for currently logged-in person r = s3_request( "pr", "person", args=[ str(auth.s3_logged_in_person()), "dashboard.%s" % auth.permission.format, ], r=current.request, ) return r()
def config(): """ Synchronization Settings Controller """ # Get the record ID of the first and only record table = s3db.sync_config record = db().select(table.id, limitby = (0, 1) ).first() if not record: record_id = table.insert() else: record_id = record.id def postp(r, output): if isinstance(output, dict) and "buttons" in output: output["buttons"].pop("list_btn", None) return output s3.postp = postp # Can't do anything else than update here from s3 import s3_request r = s3_request(args = [str(record_id), "update"], extension = "html", ) return r()
def _regional(): """ """ from s3 import S3FieldSelector, s3_request T = current.T s3request = s3_request("org", "organisation", extension="aadata") # (S3FieldSelector("project.id") != None) & \ f = (S3FieldSelector("organisation_type_id$name").anyof(["Regional Organisation", "Regional Office", "Regional Center"])) s3request.resource.add_filter(f) field_list = [ "id", "name", "acronym", (T("Type"), "organisation_type_id"), "website", "region", "year", (T("Notes"), "comments") ] return (s3request, field_list)
def _regional(): """ Regional Organisations - Filtered subset of Organisations """ from s3 import S3FieldSelector, s3_request T = current.T s3request = s3_request("org", "organisation", extension="aadata") # (S3FieldSelector("project.id") != None) & \ f = (S3FieldSelector("organisation_type_id$name").anyof(["Regional Organisation", "Regional Office", "Regional Center"])) s3request.resource.add_filter(f) field_list = [ "id", "name", "acronym", (T("Type"), "organisation_type_id"), "website", "region", "year", (T("Notes"), "comments"), ] return (s3request, field_list)
def forms(): """ Controller to download form list and individual forms """ from s3 import S3XForms if request.env.request_method != "GET": raise HTTP(405, current.ERROR.BAD_METHOD) args = request.args if len(args): # Individual form tablename = args[0] if "." in tablename: tablename, extension = tablename.split(".", 1) else: extension = "xhtml" try: prefix, name = tablename.split("_", 1) except ValueError: # Invalid tablename raise HTTP(404, current.error.BAD_RESOURCE) method = ["xform.%s" % extension] if len(args) > 1: method.insert(0, args[1]) from s3 import s3_request r = s3_request(prefix, name, args = method, extension = None, ) r.set_handler("xform", S3XForms) output = r() else: # Form list xforms = S3XForms.formlist() if not xforms: raise HTTP(404, current.T("No XForms configured on this server")) formlist = TAG.forms() for url, title in xforms: formlist.append(TAG.form(title, _url=url)) response.headers["Content-Type"] = "text/xml" response.view = "xforms/formlist.xml" output = {"formlist": formlist} return output
def _groups(): """ """ from s3 import S3FieldSelector, s3_request T = current.T s3request = s3_request("org", "organisation", extension="aadata") #(S3FieldSelector("project.id") != None) & \ f = (S3FieldSelector("organisation_type_id$name").anyof( ["Committees/Mechanism/Forum", "Network"])) s3request.resource.add_filter(f) field_list = [ "id", "name", "acronym", (T("Type"), "organisation_type_id"), "year", "address", (T("Notes"), "comments") ] return (s3request, field_list)
def adjust_pools(rules): # Override auth (disables all permission checks) current.auth.override = True db = current.db s3db = current.s3db # Customise Resources (doesn't happen automatically when running from CLI) from s3 import s3_request r = s3_request("pr", "person") r.customise_resource("pr_person") r.customise_resource("pr_group_membership") # Get all volunteer records htable = s3db.hrm_human_resource query = (htable.type == 2) & (htable.deleted == False) rows = db(query).select(htable.person_id) if rows: info("Checking %s volunteer records" % len(rows)) updated = 0 for row in rows: person_id = row.person_id if not person_id: continue new_pool_id = rules(person_id) if not new_pool_id: info(".") continue else: if update_pool(person_id, new_pool_id): updated += 1 info("+") else: info(".") infoln("...done (%s volunteers re-assigned)" % updated) else: infoln("No volunteer records to check") return True
def template_read(): """ Show the details of all the questions of a particular template """ if len(get_vars) > 0: dummy, template_id = get_vars.viewing.split(".") else: template_id = request.args[0] def postp(r, output): if r.interactive: template_id = r.id form = s3db.survey_buildQuestionnaireFromTemplate(template_id) output["items"] = None output["form"] = None output["item"] = form output["title"] = s3.crud_strings[ "survey_template"].title_question_details return output s3.postp = postp # remove CRUD generated buttons in the tabs s3db.configure( "survey_template", listadd=False, editable=False, deletable=False, ) from s3 import s3_request r = s3_request( "survey", "template", args=[template_id], ) return r( method="read", rheader=s3db.survey_template_rheader, )
def __call__(self): auth = current.auth if not auth.s3_logged_in(): auth.permission.fail() # Use custom method current.s3db.set_method("pr", "person", method = "dashboard", action = PersonalDashboard, ) # Call for currently logged-in person r = s3_request("pr", "person", args=[str(auth.s3_logged_in_person()), "dashboard.%s" % auth.permission.format, ], r = current.request, ) return r()
def _groups(): """ """ from s3 import S3FieldSelector, s3_request T = current.T s3request = s3_request("org", "organisation", extension="aadata") #(S3FieldSelector("project.id") != None) & \ f = (S3FieldSelector("organisation_type_id$name").anyof(["Committees/Mechanism/Forum", "Network"])) s3request.resource.add_filter(f) field_list = [ "id", "name", "acronym", (T("Type"), "organisation_type_id"), "year", "address", (T("Notes"), "comments") ] return (s3request, field_list)
def sync(): """ Synchronization """ tablename = get_vars.get("resource") if not tablename or tablename == "mixed": # Sync adapter to determine/negotiate the resource(s) mixed = True tablename = "sync_repository" else: # Resource determined by request mixed = False if tablename and "_" in tablename: get_vars_new = Storage(include_deleted=True) # Copy URL variables from peer: # repository ID, msince and sync filters for k, v in get_vars.items(): if k in ("repository", "msince") or \ k[0] == "[" and "]" in k: get_vars_new[k] = v # Request prefix, name = tablename.split("_", 1) from s3 import s3_request r = s3_request(prefix = prefix, name = name, args = ["sync"], get_vars = get_vars_new, ) # Response return r(mixed = mixed) raise HTTP(400, body=current.ERROR.BAD_REQUEST)
def __call__(self): """ The userstats controller """ # Require ORG_GROUP_ADMIN auth = current.auth if not auth.s3_has_role("ORG_GROUP_ADMIN"): auth.permission.fail() from s3 import S3CRUD, s3_get_extension, s3_request request = current.request args = request.args # Create an S3Request r = s3_request("org", "organisation", c = "default", f = "index/%s" % args[0], args = args[1:], extension = s3_get_extension(request), ) # Filter to root organisations resource = r.resource resource.add_filter(FS("id").belongs(self.root_orgs)) # Configure field methods from gluon import Field table = resource.table table.total_accounts = Field.Method("total_accounts", self.total_accounts) table.active_accounts = Field.Method("active_accounts", self.active_accounts) table.disabled_accounts = Field.Method("disabled_accounts", self.disabled_accounts) table.active30 = Field.Method("active30", self.active30) # Labels for field methods T = current.T TOTAL = T("Total User Accounts") ACTIVE = T("Active") DISABLED = T("Inactive") ACTIVE30 = T("Logged-in Last 30 Days") # Configure list_fields list_fields = ("id", "name", (TOTAL, "total_accounts"), (ACTIVE, "active_accounts"), (DISABLED, "disabled_accounts"), (ACTIVE30, "active30"), ) # Configure form from s3 import S3SQLCustomForm, S3SQLVirtualField crud_form = S3SQLCustomForm("name", S3SQLVirtualField("total_accounts", label = TOTAL, ), S3SQLVirtualField("active_accounts", label = ACTIVE, ), S3SQLVirtualField("disabled_accounts", label = DISABLED, ), S3SQLVirtualField("active30", label = ACTIVE30, ), ) # Configure read-only resource.configure(insertable = False, editable = False, deletable = False, crud_form = crud_form, filter_widgets = None, list_fields = list_fields, ) output = r(rheader=self.rheader) if isinstance(output, dict): output["title"] = T("User Statistics") # URL to open the resource open_url = resource.crud._linkto(r, update=False)("[id]") # Add action button for open action_buttons = S3CRUD.action_buttons action_buttons(r, deletable = False, copyable = False, editable = False, read_url = open_url, ) return output
def prep(r): resource = r.resource table = resource.table table.pe_label.label = T("ID") get_vars = r.get_vars if "viewing" in get_vars: try: vtablename, record_id = get_vars["viewing"].split(".") except ValueError: return False if vtablename == "disease_case": # Get the person_id from the case ctable = s3db[vtablename] query = (ctable.id == record_id) row = db(query).select( ctable.person_id, limitby=(0, 1), ).first() if not row: r.error(404, current.ERROR.BAD_RECORD) # Update the request from s3 import s3_request, S3SQLInlineComponent, S3SQLCustomForm request = s3_request( "pr", "person", args=[str(row.person_id)], vars={}, ) r.resource = resource = request.resource r.record = request.record r.id = request.id # Name fields in name-format order NAMES = ("first_name", "middle_name", "last_name") keys = s3base.StringTemplateParser.keys( settings.get_pr_name_format()) name_fields = [fn for fn in keys if fn in NAMES] # Fields in form crud_fields = name_fields + \ ["gender", "date_of_birth", S3SQLInlineComponent( "contact", fields = [("", "value")], filterby = {"field": "contact_method", "options": "SMS", }, label = settings.get_ui_label_mobile_phone(), multiple = False, name = "phone", ), S3SQLInlineComponent( "contact", fields = [("", "value")], filterby = {"field": "contact_method", "options": "EMAIL", }, label = T("Email"), multiple = False, name = "email", ), ] resource.configure( crud_form=S3SQLCustomForm(*crud_fields), deletable=False, ) return True else: return False
def __call__(self): """ The userstats controller """ # Require ORG_GROUP_ADMIN auth = current.auth if not auth.s3_has_role("ORG_GROUP_ADMIN"): auth.permission.fail() from s3 import S3CRUD, s3_get_extension, s3_request request = current.request args = request.args # Create an S3Request r = s3_request( "org", "organisation", c="default", f="index/%s" % args[0], args=args[1:], extension=s3_get_extension(request), ) # Filter to root organisations resource = r.resource resource.add_filter(FS("id").belongs(self.root_orgs)) # Configure field methods from gluon import Field table = resource.table table.total_accounts = Field.Method("total_accounts", self.total_accounts) table.active_accounts = Field.Method("active_accounts", self.active_accounts) table.disabled_accounts = Field.Method("disabled_accounts", self.disabled_accounts) table.active30 = Field.Method("active30", self.active30) # Labels for field methods T = current.T TOTAL = T("Total User Accounts") ACTIVE = T("Active") DISABLED = T("Inactive") ACTIVE30 = T("Logged-in Last 30 Days") # Configure list_fields list_fields = ( "id", "name", (TOTAL, "total_accounts"), (ACTIVE, "active_accounts"), (DISABLED, "disabled_accounts"), (ACTIVE30, "active30"), ) # Configure form from s3 import S3SQLCustomForm, S3SQLVirtualField crud_form = S3SQLCustomForm( "name", S3SQLVirtualField( "total_accounts", label=TOTAL, ), S3SQLVirtualField( "active_accounts", label=ACTIVE, ), S3SQLVirtualField( "disabled_accounts", label=DISABLED, ), S3SQLVirtualField( "active30", label=ACTIVE30, ), ) # Configure read-only resource.configure( insertable=False, editable=False, deletable=False, crud_form=crud_form, filter_widgets=None, list_fields=list_fields, ) output = r(rheader=self.rheader) if isinstance(output, dict): output["title"] = T("User Statistics") # URL to open the resource open_url = resource.crud._linkto(r, update=False)("[id]") # Add action button for open action_buttons = S3CRUD.action_buttons action_buttons( r, deletable=False, copyable=False, editable=False, read_url=open_url, ) return output
def __call__(self): from gluon import IS_INT_IN_RANGE from s3 import FS, \ s3_request, \ S3CRUD, \ S3TextFilter, \ S3DateFilter, \ S3SQLCustomForm s3 = current.response.s3 controller = self.__class__.__name__ def prep(r): SURPLUS_MEALS = "SURPLUS-MEALS" T = current.T db = current.db s3db = current.s3db resource = r.resource # Set default SURPLUS_MEALS event type ttable = s3db.dvr_case_event_type query = (ttable.code == SURPLUS_MEALS) & \ (ttable.deleted != True) event_type = db(query).select( ttable.id, limitby=(0, 1), ).first() if not event_type: r.error(400, "No event type with code %s defined" % SURPLUS_MEALS) event_type_id = event_type.id # Filter to SURPLUS_MEALS events without person_id query = (FS("type_id") == event_type_id) & \ (FS("person_id") == None) resource.add_filter(query) # Configure fields table = resource.table field = table.person_id field.default = None field.readable = field.writable = False field = table.type_id field.default = event_type_id field.readable = field.writable = False field = table.date field.readable = field.writable = True field = table.quantity field.default = 0 # Override IS_EMPTY_OR field.requires = IS_INT_IN_RANGE(0, None) field.readable = field.writable = True field = table.modified_by field.readable = True registered_by = (T("Registered by"), "modified_by") if r.interactive: # Custom CRUD form crud_form = S3SQLCustomForm( "date", "quantity", registered_by, "comments", ) # Custom filter widgets filter_widgets = [ S3TextFilter( [ "created_by$email", "comments", ], label=T("Search"), ), S3DateFilter("date"), ] resource.configure( crud_form=crud_form, filter_widgets=filter_widgets, ) # Turn off filter manager current.deployment_settings.search.filter_manager = False # Custom list fields list_fields = [ "date", "quantity", registered_by, "comments", ] # URL of the list view list_url = URL(args=[controller], vars={}) s3.datatable_ajax_source = list_url resource.configure( insertable=True, list_fields=list_fields, # Fix redirects: create_next=list_url, update_next=list_url, delete_next=list_url, ) # Custom CRUD strings T = current.T s3.crud_strings["dvr_case_event"] = Storage( label_create=T("Register Surplus Meals Quantity"), title_display=T("Surplus Meals Quantity"), title_list=T("Surplus Meals"), title_update=T("Edit Surplus Meals Quantity"), label_list_button=T("List Surplus Meals"), label_delete_button=T("Delete Entry"), msg_record_created=T("Entry added"), msg_record_modified=T("Entry updated"), msg_record_deleted=T("Entry deleted"), msg_list_empty=T("No Surplus Meals currently registered"), ) return True s3.prep = prep def postp(r, output): if isinstance(output, dict): # Inject controller name in dt action buttons if r.component: action_args = [controller, r.id, r.component.alias, '[id]'] else: action_args = [controller, '[id]'] action_url = lambda action: URL(args=action_args + [action], vars={}) S3CRUD.action_buttons( r, read_url=action_url('read'), update_url=action_url('update'), delete_url=action_url('delete'), ) # Inject controller name in CRUD buttons buttons = output.get("buttons") if buttons: path = "%s/%s" % (r.controller, r.function) full = "%s/%s" % (path, controller) for element in buttons.values(): if not hasattr(element, "attributes"): continue url = element.attributes.get("_href") if url: element["_href"] = url.replace(path, full) return output s3.postp = postp # Custom REST request request_args = current.request.args[1:] r = s3_request( "dvr", "case_event", args=request_args, extension=current.auth.permission.format, ) return r()
def s3_rest_controller(prefix=None, resourcename=None, **attr): """ Helper function to apply the S3Resource REST interface Args: prefix: the application prefix resourcename: the resource name (without prefix) attr: additional keyword parameters Any keyword parameters will be copied into the output dict (provided that the output is a dict). If a keyword parameter is callable, then it will be invoked, and its return value will be added to the output dict instead. The callable receives the S3Request as its first and only parameter. CRUD can be configured per table using: s3db.configure(tablename, **attr) *** Redirection: create_next URL to redirect to after a record has been created update_next URL to redirect to after a record has been updated delete_next URL to redirect to after a record has been deleted *** Form configuration: list_fields list of names of fields to include into list views subheadings Sub-headings (see separate documentation) listadd Enable/Disable add-form in list views *** CRUD configuration: editable Allow/Deny record updates in this table deletable Allow/Deny record deletions in this table insertable Allow/Deny record insertions into this table copyable Allow/Deny record copying within this table *** Callbacks: create_onvalidation Function for additional record validation on create create_onaccept Function after successful record insertion update_onvalidation Function for additional record validation on update update_onaccept Function after successful record update onvalidation Fallback for both create_onvalidation and update_onvalidation onaccept Fallback for both create_onaccept and update_onaccept ondelete Function after record deletion """ # Parse the request dynamic = attr.get("dynamic") if dynamic: # Dynamic table controller c = request.controller f = request.function attr = settings.customise_controller("%s_%s" % (c, f), **attr) from s3 import DYNAMIC_PREFIX, s3_get_extension, s3_request r = s3_request( DYNAMIC_PREFIX, dynamic, f="%s/%s" % (f, dynamic), args=request.args[1:], extension=s3_get_extension(request), ) else: # Customise Controller from Template attr = settings.customise_controller( "%s_%s" % ( prefix or request.controller, resourcename or request.function, ), **attr) from s3 import s3_request r = s3_request(prefix, resourcename) # Customize target resource(s) from Template r.customise_resource() # Configure standard method handlers from s3 import S3Compose, S3Filter, S3GroupedItemsReport, S3HierarchyCRUD, \ S3Importer, S3Map, S3Merge, S3MobileCRUD, S3Organizer, \ S3OrgRoleManager, S3Profile, S3Report, S3Summary, \ S3TimePlot, S3XForms, S3Wizard, search_ac from s3db.cms import S3CMS set_handler = r.set_handler set_handler("cms", S3CMS) set_handler("compose", S3Compose) # @ToDo: Make work in Component Tabs: set_handler("copy", lambda r, **attr: \ redirect(URL(args = "create", vars = {"from_record":r.id}, ))) set_handler("deduplicate", S3Merge) set_handler("filter", S3Filter) set_handler("grouped", S3GroupedItemsReport) set_handler("hierarchy", S3HierarchyCRUD) set_handler("import", S3Importer) set_handler("map", S3Map) set_handler("mform", S3MobileCRUD, representation="json") set_handler("organize", S3Organizer) set_handler("profile", S3Profile) set_handler("report", S3Report) # For HTML, JSON set_handler("report", S3Report, transform=True) # For GeoJSON set_handler("search_ac", search_ac) set_handler("summary", S3Summary) set_handler("timeplot", S3TimePlot) set_handler("xform", S3XForms) set_handler("wizard", S3Wizard) method = r.method # Don't load S3PDF unless needed (very slow import with Reportlab) #if method == "import" and r.representation == "pdf": # from s3.s3pdf import S3PDF # set_handler("import", S3PDF(), # http = ("GET", "POST"), # representation = "pdf" # ) # Plugin OrgRoleManager when appropriate S3OrgRoleManager.set_method(r) # List of methods which can have custom action buttons # (defining here allows postp to add a custom method to the list) s3.action_methods = ( "import", "review", "approve", "reject", "deduplicate", "wizard", ) # Execute the request output = r(**attr) if isinstance(output, dict) and \ method in (None, "report", "search", "datatable", "datatable_f", "summary", ): if s3.actions is None: # Add default action buttons prefix, name, table, tablename = r.target() authorised = auth.s3_has_permission("update", tablename) # If a component has components itself, then action buttons # can be forwarded to the native controller by setting native=True if r.component and s3db.has_components(table): native = output.get("native", False) else: native = False # Get table config get_config = s3db.get_config listadd = get_config(tablename, "listadd", True) # Which is the standard open-action? if settings.get_ui_open_read_first(): # Always read, irrespective permissions editable = False else: editable = get_config(tablename, "editable", True) if editable and \ auth.permission.ownership_required("update", table): # User cannot edit all records in the table if settings.get_ui_auto_open_update(): # Decide automatically per-record (implicit method) editable = "auto" else: # Always open read first (explicit read) editable = False deletable = get_config(tablename, "deletable", True) copyable = get_config(tablename, "copyable", False) # URL to open the resource from s3 import S3CRUD open_url = S3CRUD._linkto( r, authorised=authorised, update=editable, native=native, )("[id]") # Add action buttons for Open/Delete/Copy as appropriate s3_action_buttons( r, deletable=deletable, copyable=copyable, editable=editable, read_url=open_url, update_url=open_url # To use modals #update_url = "%s.popup?refresh=list" % open_url ) # Override Add-button, link to native controller and put # the primary key into get_vars for automatic linking if native and not listadd and \ auth.s3_has_permission("create", tablename): label = S3CRUD.crud_string(tablename, "label_create") component = r.resource.components[name] fkey = "%s.%s" % (name, component.fkey) get_vars_copy = get_vars.copy() get_vars_copy.update({fkey: r.record[component.fkey]}) url = URL( prefix, name, args=["create"], vars=get_vars_copy, ) add_btn = A( label, _href=url, _class="action-btn", ) output.update(add_btn=add_btn) elif method not in s3.action_methods: s3.actions = None return output
def __call__(self): T = current.T db = current.db s3db = current.s3db user = current.auth.user user_id = user.id # Huuricane Season lasts from 1/6 to 30/11 now = current.request.utcnow if 5 < now.month < 12: SEASON = T("this Season") SEASON_START = datetime.date(now.year, 6, 1) SEASON_END = None else: SEASON = T("last Season") last_year = now.year - 1 SEASON_START = datetime.date(last_year, 6, 1) SEASON_END = datetime.date(last_year, 12, 1) # Shipments stable = s3db.inv_send fields = [ "id", "send_ref", "date", "site_id", "to_site_id", "transport_type", "status", "filing_status", ] sresource = s3db.resource( "inv_send", filter=(stable.date != None), # Don't include Unsent Shipments ) srows = sresource.select( fields, as_rows=True, limit=5, orderby=~stable.date, ) rtable = s3db.inv_recv fields = [ "id", "recv_ref", "date", "site_id", "from_site_id", "transport_type", "status", "filing_status", ] rresource = s3db.resource( "inv_recv", filter=(rtable.date != None), # Don't include Unreceived Shipments ) rrows = rresource.select( fields, as_rows=True, limit=5, orderby=~rtable.date, ) rtotal = len(rrows) # Find the most recent 5 from both lists shipments = [] sappend = shipments.append rindex = 0 stotal = 0 for srow in srows: if rindex < rtotal: rrow = rrows[rindex] else: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue send_date = srow.date recv_date = rrow.date if send_date > recv_date: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue rrow.type = "recv" sappend(rrow) if stotal == 4: break stotal += 1 rindex += 1 if rindex < rtotal: rrow = rrows[rindex] else: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue recv_date = rrow.date if send_date > recv_date: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue rrow.type = "recv" sappend(rrow) if stotal == 4: break stotal += 1 rindex += 1 if rindex < rtotal: rrow = rrows[rindex] else: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue recv_date = rrow.date if send_date > recv_date: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue rrow.type = "recv" sappend(rrow) if stotal == 4: break stotal += 1 rindex += 1 if rindex < rtotal: rrow = rrows[rindex] else: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue recv_date = rrow.date if send_date > recv_date: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue rrow.type = "recv" sappend(rrow) if stotal == 4: break stotal += 1 rindex += 1 if rindex < rtotal: rrow = rrows[rindex] else: srow.type = "send" sappend(srow) if stotal == 4: break stotal += 1 continue recv_date = rrow.date if send_date > recv_date: srow.type = "send" sappend(srow) stotal += 1 break rrow.type = "recv" sappend(rrow) stotal += 1 break while stotal < 5: if rindex < rtotal: rrow = rrows[rindex] else: break rrow.type = "recv" sappend(rrow) rindex += 1 stotal += 1 date_represent = stable.date.represent status_represent = stable.status.represent site_ids = [] for row in shipments: if row.type == "send": site_ids += [ row.site_id, row.to_site_id, ] else: site_ids += [ row.site_id, row.from_site_id, ] #sites = org_SiteRepresent(show_type = False).bulk(list(set(site_ids))) sites = S3Represent(lookup="org_site").bulk(list(set(site_ids))) sites_get = sites.get transport_opts = { "Air": ICON("plane"), "Sea": ICON("ship"), "Road": ICON("truck"), "Hand": ICON("hand-grab"), } transport_opts_get = transport_opts.get filing_opts = { SHIP_DOC_PENDING: ICON("close"), SHIP_DOC_COMPLETE: ICON("check"), } filing_opts_get = filing_opts.get shipment_rows = [ DIV( DIV( T("Date"), _class="columns medium-2", ), DIV( T("in/out"), _class="columns medium-1", ), DIV( T("From"), _class="columns medium-1", ), DIV( T("To"), _class="columns medium-1", ), DIV( T("WB/GRN"), _class="columns medium-4", ), DIV( T("Trans."), _class="columns medium-1", ), DIV( T("Status"), _class="columns medium-1", ), DIV( ICON("briefcase"), _class="columns medium-1", ), _class="ship-card row", ), ] sappend = shipment_rows.append for row in shipments: if row.type == "send": in_out = ICON("arrow-right") from_site_id = row.site_id to_site_id = row.to_site_id shipment_ref = row.send_ref url = URL( c="inv", f="send", args=[row["inv_send.id"]], ) else: in_out = ICON("arrow-left") from_site_id = row.from_site_id to_site_id = row.site_id shipment_ref = row.recv_ref url = URL( c="inv", f="recv", args=[row.id], ) sappend( DIV( DIV( date_represent(row.date), _class="columns medium-2", ), DIV( in_out, _class="columns medium-1", ), DIV( sites_get(from_site_id), _class="columns medium-1", ), DIV( sites_get(to_site_id), _class="columns medium-1", ), DIV( A( shipment_ref, _href=url, ), _class="columns medium-4", ), DIV( transport_opts_get(row.transport_type), _class="columns medium-1", ), DIV( status_represent(row.status), _class="columns medium-1", ), DIV( filing_opts_get(row.filing_status), _class="columns medium-1", ), _class="ship-card row", )) shipments = DIV(*shipment_rows) # Alerts table = s3db.auth_user_notification query = (table.user_id == user_id) & \ (table.deleted == False) rows = db(query).select( table.name, table.url, orderby=~table.created_on, ) alert_rows = [] for row in rows: alert_rows.append( DIV( A( DIV(ICON("bell-o"), _class="columns medium-1"), DIV(row.name, _class="columns medium-11"), _href=row.url, _target="_blank", ), _class="alert-card row", )) alerts = DIV(*alert_rows) # Capacity # Define the Pivot Table r = s3_request("inv", "inv_item") r.customise_resource() resource = s3db.resource("inv_inv_item") report = S3Report() report.resource = resource capacity = report.widget( r, widget_id="capacity", ajaxurl=URL(c="inv", f="inv_item", args="report.json"), ) # KPI # Which Warehouses are we responsible for? wtable = s3db.inv_warehouse gtable = db.auth_group mtable = db.auth_membership query = (mtable.user_id == user_id) & \ (mtable.deleted == False) & \ (mtable.group_id == gtable.id) & \ (gtable.uuid.belongs("ORG_ADMIN", "logs_manager", "wh_operator", )) realms = db(query).select(mtable.pe_id) realms = list(set([row.pe_id for row in realms])) if None in realms: realms.remove(None) # Lookup Default Realm from s3db.pr import pr_default_realms default_realms = pr_default_realms(user.pe_id) realms = realms + default_realms from s3db.pr import pr_get_descendants child_pe_ids = pr_get_descendants(realms, entity_types=["inv_warehouse"]) warehouses = db(wtable.pe_id.belongs(realms + child_pe_ids)).select( wtable.site_id, wtable.name, wtable.free_capacity, ) wh_site_ids = [row.site_id for row in warehouses] itable = s3db.inv_inv_item fields = [ "site_id", "total_weight", "total_volume", "quantity", # extra_fields "item_pack_id$quantity", # extra_fields "item_id.weight", # extra_fields "item_id.volume", # extra_fields ] iresource = s3db.resource( "inv_inv_item", filter=(itable.site_id.belongs(wh_site_ids)), ) rows = iresource.select(fields, as_rows=True) stockpile_weight = 0 stockpile_volume = 0 for row in rows: stockpile_weight += row["inv_inv_item.total_weight"]() stockpile_volume += row["inv_inv_item.total_volume"]() fields = [ "id", "track_item.total_weight", "track_item.total_volume", "track_item.quantity", # extra_fields "track_item.item_pack_id$quantity", # extra_fields "track_item.item_id$weight", # extra_fields "track_item.item_id$volume", # extra_fields ] query = (stable.status.belongs([SHIP_STATUS_SENT, SHIP_STATUS_RECEIVED, SHIP_STATUS_RETURNING, ])) & \ (stable.date > SEASON_START) if SEASON_END: query &= (stable.date > SEASON_END) sresource = s3db.resource("inv_send", filter=query) srows = sresource.select(fields, as_rows=True) num_shipments = len(set([row["inv_send.id"] for row in srows])) shipments_weight = 0 shipments_volume = 0 for row in srows: weight = row["inv_track_item.total_weight"]() try: shipments_weight += weight except TypeError: # NONE returned: ignore pass volume = row["inv_track_item.total_volume"]() try: shipments_volume += volume except TypeError: # NONE returned: ignore pass float_represent = IS_FLOAT_AMOUNT.represent free_capacities = UL() for row in warehouses: free_capacities.append( LI("%s: %s m3" % (row.name, float_represent(row.free_capacity, precision=1)))) kpi = UL( LI("%s: %s kg" % (T("Total weight stockpiled"), float_represent(stockpile_weight, precision=1))), LI("%s: %s m3" % (T("Total volume stockpiled"), float_represent(stockpile_volume, precision=1))), LI("%s %s: %s" % (T("Number of Shipments sent"), SEASON, num_shipments)), LI("%s %s: %s kg" % (T("Total weight sent"), SEASON, float_represent(shipments_weight, precision=1))), LI("%s %s: %s m3" % (T("Total volume sent"), SEASON, float_represent(shipments_volume, precision=3))), LI("%s: %s" % (T("Number of warehouses"), len(warehouses))), LI("%s:" % T("Remaining stockpile capacities available"), free_capacities), ) # Preparedness Checklist #checklist = UL() output = { "title": T("Dashboard"), "shipments": shipments, "alerts": alerts, "capacity": capacity, "kpi": kpi, #"checklist": checklist, } # Custom view self._view(THEME, "inv_dashboard.html") return output
def __call__(self): from gluon import IS_INT_IN_RANGE from s3 import FS, \ s3_request, \ S3CRUD, \ S3TextFilter, \ S3DateFilter, \ S3SQLCustomForm s3 = current.response.s3 controller = self.__class__.__name__ def prep(r): FOOD = "FOOD" T = current.T db = current.db s3db = current.s3db resource = r.resource # Set default FOOD event type ttable = s3db.dvr_case_event_type query = (ttable.code == FOOD) & \ (ttable.deleted != True) event_type = db(query).select(ttable.id, limitby = (0, 1), ).first() if not event_type: r.error("No event type with code %s defined" % FOOD) event_type_id = event_type.id # Filter to FOOD events without person_id query = (FS("type_id") == event_type_id) & \ (FS("person_id") == None) resource.add_filter(query) # Configure fields table = resource.table field = table.person_id field.default = None field.readable = field.writable = False field = table.type_id field.default = event_type_id field.readable = field.writable = False field = table.date field.readable = field.writable = True field = table.quantity field.default = 0 # Override IS_EMPTY_OR field.requires = IS_INT_IN_RANGE(0, None) field.readable = field.writable = True field = table.modified_by field.readable = True registered_by = (T("Registered by"), "modified_by") if r.interactive: # Custom CRUD form crud_form = S3SQLCustomForm("date", "quantity", registered_by, "comments", ) # Custom filter widgets filter_widgets = [S3TextFilter(["created_by$email", "comments", ], label = T("Search"), ), S3DateFilter("date"), ] resource.configure(crud_form = crud_form, filter_widgets = filter_widgets, ) # Turn off filter manager current.deployment_settings.search.filter_manager = False # Custom list fields list_fields = ["date", "quantity", registered_by, "comments", ] # URL of the list view list_url = URL(args=[controller], vars={}) s3.datatable_ajax_source = list_url resource.configure(insertable = True, list_fields = list_fields, # Fix redirects: create_next = list_url, update_next = list_url, delete_next = list_url, ) # Custom CRUD strings T = current.T s3.crud_strings["dvr_case_event"] = Storage( label_create = T("Register Surplus Meals Quantity"), title_display = T("Surplus Meals Quantity"), title_list = T("Surplus Meals"), title_update = T("Edit Surplus Meals Quantity"), label_list_button = T("List Surplus Meals"), label_delete_button = T("Delete Entry"), msg_record_created = T("Entry added"), msg_record_modified = T("Entry updated"), msg_record_deleted = T("Entry deleted"), msg_list_empty = T("No Surplus Meals currently registered"), ) return True s3.prep = prep def postp(r, output): if isinstance(output, dict): # Inject controller name in dt action buttons if r.component: action_args = [controller, r.id, r.component.alias, '[id]'] else: action_args = [controller, '[id]'] action_url = lambda action: URL(args=action_args + [action], vars={}) S3CRUD.action_buttons(r, read_url = action_url('read'), update_url = action_url('update'), delete_url = action_url('delete'), ) # Inject controller name in CRUD buttons buttons = output.get("buttons") if buttons: path = "%s/%s" % (r.controller, r.function) full = "%s/%s" % (path, controller) for element in buttons.values(): if not hasattr(element, "attributes"): continue url = element.attributes.get("_href") if url: element["_href"] = url.replace(path, full) return output s3.postp = postp # Custom REST request request_args = current.request.args[1:] r = s3_request("dvr", "case_event", args = request_args, extension = current.auth.permission.format, ) return r()