# -*- coding: utf-8 -*- from collections import OrderedDict from gluon import current, URL from gluon.storage import Storage def config(settings): """ Template settings for SaFiRe: Sahana First Response http://eden.sahanafoundation.org/wiki/BluePrint/SAFIRE """ T = current.T settings.base.system_name = T("Sahana First Response") settings.base.system_name_short = T("SAFIRE") # PrePopulate data settings.base.prepopulate.append("SAFIRE") settings.base.prepopulate_demo.append("SAFIRE/Demo") # Theme (folder to use for views/layout.html) #settings.base.theme = "SAFIRE" # Authentication settings # Should users be allowed to register themselves? #settings.security.self_registration = False # Do new users need to verify their email address? #settings.auth.registration_requires_verification = True # Do new users need to be approved by an administrator prior to being able to login? #settings.auth.registration_requires_approval = True settings.auth.registration_requests_organisation = True # Approval emails get sent to all admins settings.mail.approver = "ADMIN" settings.auth.registration_link_user_to = {"staff": T("Staff"), } settings.auth.registration_link_user_to_default = ["staff"] # Uncomment to display the Map Legend as a floating DIV settings.gis.legend = "float" # Uncomment to Disable the Postcode selector in the LocationSelector #settings.gis.postcode_selector = False # @ToDo: Vary by country (include in the gis_config!) # Uncomment to show the Print control: # http://eden.sahanafoundation.org/wiki/UserGuidelines/Admin/MapPrinting #settings.gis.print_button = True # GeoNames username settings.gis.geonames_username = "trendspotter" settings.gis.simplify_tolerance = 0 # L10n settings # Number formats (defaults to ISO 31-0) # Decimal separator for numbers (defaults to ,) settings.L10n.decimal_separator = "." # Thousands separator for numbers (defaults to space) settings.L10n.thousands_separator = "," # Security Policy # http://eden.sahanafoundation.org/wiki/S3AAA#System-widePolicy # 1: Simple (default): Global as Reader, Authenticated as Editor # 2: Editor role required for Update/Delete, unless record owned by session # 3: Apply Controller ACLs # 4: Apply both Controller & Function ACLs # 5: Apply Controller, Function & Table ACLs # 6: Apply Controller, Function, Table ACLs and Entity Realm # 7: Apply Controller, Function, Table ACLs and Entity Realm + Hierarchy # 8: Apply Controller, Function, Table ACLs, Entity Realm + Hierarchy and Delegations settings.security.policy = 5 # Controller, Function & Table ACLs # ------------------------------------------------------------------------- # Comment/uncomment modules here to disable/enable them # Modules menu is defined in modules/eden/menu.py settings.modules = OrderedDict([ # Core modules which shouldn't be disabled ("default", Storage( name_nice = "Home", restricted = False, # Use ACLs to control access to this module #access = None, # All Users (inc Anonymous) can see this module in the default menu & access the controller module_type = None # This item is not shown in the menu )), ("admin", Storage( name_nice = "Administration", #description = "Site Administration", access = "|1|", # Only Administrators can see this module in the default menu & access the controller module_type = None # This item is handled separately for the menu )), ("appadmin", Storage( name_nice = "Administration", #description = "Site Administration", module_type = None # No Menu )), ("errors", Storage( name_nice = "Ticket Viewer", #description = "Needed for Breadcrumbs", restricted = False, module_type = None # No Menu )), ("sync", Storage( name_nice = "Synchronization", #description = "Synchronization", access = "|1|", # Only Administrators can see this module in the default menu & access the controller module_type = None # This item is handled separately for the menu )), #("tour", Storage( # name_nice = T("Guided Tour Functionality"), # module_type = None, #)), #("translate", Storage( # name_nice = T("Translation Functionality"), # #description = "Selective translation of strings based on module.", # module_type = None, #)), ("gis", Storage( name_nice = "Map", #description = "Situation Awareness & Geospatial Analysis", module_type = 6, # 6th item in the menu )), ("pr", Storage( name_nice = "Person Registry", #description = "Central point to record details on People", access = "|1|", # Only Administrators can see this module in the default menu (access to controller is possible to all still) module_type = 10 )), ("org", Storage( name_nice = "Organizations", #description = 'Lists "who is doing what & where". Allows relief agencies to coordinate their activities', module_type = 1 )), ("hrm", Storage( name_nice = "Staff", #description = "Human Resources Management", module_type = 2, )), ("vol", Storage( name_nice = T("Volunteers"), #description = "Human Resources Management", module_type = 2, )), ("cms", Storage( name_nice = "Content Management", #description = "Content Management System", module_type = 10, )), ("doc", Storage( name_nice = "Documents", #description = "A library of digital resources, such as photos, documents and reports", module_type = 10, )), ("msg", Storage( name_nice = "Messaging", #description = "Sends & Receives Alerts via Email & SMS", # The user-visible functionality of this module isn't normally required. Rather it's main purpose is to be accessed from other modules. module_type = None, )), ("supply", Storage( name_nice = "Supply Chain Management", #description = "Used within Inventory Management, Request Management and Asset Management", module_type = None, # Not displayed )), ("inv", Storage( name_nice = T("Warehouses"), #description = "Receiving and Sending Items", module_type = 4 )), ("asset", Storage( name_nice = "Assets", #description = "Recording and Assigning Assets", module_type = 5, )), # Vehicle depends on Assets ("vehicle", Storage( name_nice = "Vehicles", #description = "Manage Vehicles", module_type = 10, )), #("budget", Storage( # name_nice = T("Budgets"), # #description = "Tracks the location, capacity and breakdown of victims in Shelters", # module_type = 10 #)), ("fin", Storage( name_nice = T("Finance"), module_type = 10 )), ("cr", Storage( name_nice = T("Shelters"), #description = "Tracks the location, capacity and breakdown of victims in Shelters", module_type = 10 )), ("project", Storage( name_nice = "Tasks", #description = "Tracking of Projects, Activities and Tasks", module_type = 2 )), ("req", Storage( name_nice = "Requests", #description = "Manage requests for supplies, assets, staff or other resources. Matches against Inventories where supplies are requested.", module_type = 10, )), ("hms", Storage( name_nice = T("Hospitals"), #description = "Helps to monitor status of hospitals", module_type = 10 )), #("dvr", Storage( # name_nice = T("Disaster Victim Registry"), # #description = "Allow affected individuals & households to register to receive compensation and distributions", # module_type = 10, #)), ("event", Storage( name_nice = "Events", #description = "Activate Events (e.g. from Scenario templates) for allocation of appropriate Resources (Human, Assets & Facilities).", module_type = 10, )), #("transport", Storage( # name_nice = T("Transport"), # module_type = 10, #)), #("stats", Storage( # name_nice = T("Statistics"), # #description = "Manages statistics", # module_type = None, #)), ]) # ------------------------------------------------------------------------- # CMS # ------------------------------------------------------------------------- settings.cms.richtext = True # ------------------------------------------------------------------------- # Organisations # ------------------------------------------------------------------------- settings.org.documents_tab = True settings.org.projects_tab = False # ------------------------------------------------------------------------- # Shelters # ------------------------------------------------------------------------- settings.cr.people_registration = False # ------------------------------------------------------------------------- def customise_cr_shelter_resource(r, tablename): #table = current.s3db.cr_shelter f = current.s3db.cr_shelter.shelter_service_id f.readable = f.writable = False settings.customise_cr_shelter_resource = customise_cr_shelter_resource # ------------------------------------------------------------------------- # Events # ------------------------------------------------------------------------- def event_rheader(r): rheader = None record = r.record if record and r.representation == "html": from gluon import A, DIV, TABLE, TR, TH from s3 import s3_rheader_tabs name = r.name if name == "incident": if settings.get_incident_label(): # == "Ticket" label = T("Ticket Details") else: label = T("Incident Details") tabs = [(label, None), #(T("Tasks"), "task"), #(T("Human Resources"), "human_resource"), #(T("Equipment"), "asset"), (T("Action Plan"), "plan"), (T("Incident Reports"), "incident_report"), (T("Logs"), "log"), (T("Expenses"), "expense"), (T("Situation Reports"), "sitrep"), ] rheader_tabs = s3_rheader_tabs(r, tabs) record_id = r.id incident_type_id = record.incident_type_id editable = current.auth.s3_has_permission("UPDATE", "event_incident", record_id) if editable: # Dropdown of Scenarios to select stable = current.s3db.event_scenario query = (stable.incident_type_id == incident_type_id) & \ (stable.deleted == False) scenarios = current.db(query).select(stable.id, stable.name, ) if len(scenarios) and r.method != "event": from gluon import SELECT, OPTION dropdown = SELECT(_id="scenarios") dropdown["_data-incident_id"] = record_id dappend = dropdown.append dappend(OPTION(T("Select Scenario"))) for s in scenarios: dappend(OPTION(s.name, _value=s.id)) scenarios = TR(TH("%s: " % T("Scenario")), dropdown, ) s3 = current.response.s3 script = "/%s/static/themes/SAFIRE/js/incident_profile.js" % r.application if script not in s3.scripts: s3.scripts.append(script) s3.js_global.append('''i18n.scenarioConfirm="%s"''' % T("Populate Incident with Tasks, Organizations, Positions and Equipment from the Scenario?")) else: scenarios = "" else: scenarios = "" if record.exercise: exercise = TH(T("EXERCISE")) else: exercise = TH() if record.closed: closed = TH(T("CLOSED")) else: closed = TH() if record.event_id or r.method == "event" or not editable: event = "" else: if settings.get_event_label(): # == "Disaster" label = T("Assign to Disaster") else: label = T("Assign to Event") event = A(label, _href = URL(c = "event", f = "incident", args = [record_id, "event"], ), _class = "action-btn" ) table = r.table rheader = DIV(TABLE(TR(exercise), TR(TH("%s: " % table.name.label), record.name, ), TR(TH("%s: " % table.incident_type_id.label), table.incident_type_id.represent(incident_type_id), ), TR(TH("%s: " % table.location_id.label), table.location_id.represent(record.location_id), ), # @ToDo: Add Zone TR(TH("%s: " % table.severity.label), table.severity.represent(record.severity), ), TR(TH("%s: " % table.level.label), table.level.represent(record.level), ), TR(TH("%s: " % table.organisation_id.label), table.organisation_id.represent(record.organisation_id), ), TR(TH("%s: " % table.person_id.label), table.person_id.represent(record.person_id), ), scenarios, TR(TH("%s: " % table.comments.label), record.comments, ), TR(TH("%s: " % table.date.label), table.date.represent(record.date), ), TR(closed), event, ), rheader_tabs) elif name == "incident_report": record_id = r.id ltable = current.s3db.event_incident_report_incident query = (ltable.incident_report_id == record_id) link = current.db(query).select(ltable.incident_id, limitby = (0, 1) ).first() if link: from s3 import S3Represent represent = S3Represent(lookup="event_incident", show_link=True) rheader = DIV(TABLE(TR(TH("%s: " % ltable.incident_id.label), represent(link.incident_id), ), )) else: if settings.get_incident_label(): # == "Ticket" label = T("Assign to Ticket") else: label = T("Assign to Incident") rheader = DIV(A(label, _href = URL(c = "event", f = "incident_report", args = [record_id, "assign"], ), _class = "action-btn" )) elif name == "event": if settings.get_event_label(): # == "Disaster" label = T("Disaster Details") else: label = T("Event Details") if settings.get_incident_label(): # == "Ticket" INCIDENTS = T("Tickets") else: INCIDENTS = T("Incidents") tabs = [(label, None), (INCIDENTS, "incident"), (T("Documents"), "document"), (T("Photos"), "image"), ] rheader_tabs = s3_rheader_tabs(r, tabs) table = r.table rheader = DIV(TABLE(TR(TH("%s: " % table.event_type_id.label), table.event_type_id.represent(record.event_type_id), ), TR(TH("%s: " % table.name.label), record.name, ), TR(TH("%s: " % table.start_date.label), table.start_date.represent(record.start_date), ), TR(TH("%s: " % table.comments.label), record.comments, ), ), rheader_tabs) elif name == "scenario": tabs = [(T("Scenario Details"), None), #(T("Tasks"), "task"), #(T("Human Resources"), "human_resource"), #(T("Equipment"), "asset"), (T("Action Plan"), "plan"), (T("Incident Reports"), "incident_report"), ] rheader_tabs = s3_rheader_tabs(r, tabs) table = r.table rheader = DIV(TABLE(TR(TH("%s: " % table.incident_type_id.label), table.incident_type_id.represent(record.incident_type_id), ), TR(TH("%s: " % table.organisation_id.label), table.organisation_id.represent(record.organisation_id), ), TR(TH("%s: " % table.location_id.label), table.location_id.represent(record.location_id), ), TR(TH("%s: " % table.name.label), record.name, ), TR(TH("%s: " % table.comments.label), record.comments, ), ), rheader_tabs) return rheader # ------------------------------------------------------------------------- def customise_event_event_controller(**attr): #s3 = current.response.s3 # No sidebar menu #current.menu.options = None attr["rheader"] = event_rheader return attr settings.customise_event_event_controller = customise_event_event_controller # ------------------------------------------------------------------------- def customise_event_incident_report_resource(r, tablename): current.response.s3.crud_strings[tablename] = Storage( label_create = T("Log Call"), title_display = T("Call Log Details"), title_list = T("Call Logs"), title_update = T("Edit Call Log"), label_list_button = T("List Call Logs"), label_delete_button = T("Delete Call Log"), msg_record_created = T("Call Log added"), msg_record_modified = T("Call Log updated"), msg_record_deleted = T("Call Log removed"), msg_list_empty = T("No Calls currently logged"), ) settings.customise_event_incident_report_resource = customise_event_incident_report_resource # ------------------------------------------------------------------------- def customise_event_incident_report_controller(**attr): from gluon import A s3 = current.response.s3 # Custom prep standard_prep = s3.prep def custom_prep(r): # Call standard postp if callable(standard_prep): result = standard_prep(r) if not result: return False method = r.method if method in (None, "create"): current.s3db.gis_location.addr_street.label = T("Street Address or Location Details") from s3 import S3SQLCustomForm crud_form = S3SQLCustomForm((T("What is it?"), "name"), "incident_type_id", (T("Who am I speaking with?"), "reported_by"), (T("How can we contact you?"), "contact"), (T("Where did this Incident take place?"), "location_id"), (T("Explain the Situation?"), "description"), (T("What are your immediate needs?"), "needs"), ) r.resource.configure(create_next = URL(args=["[id]", "assign"]), crud_form = crud_form, ) return True s3.prep = custom_prep # No sidebar menu current.menu.options = None req_args = current.request.args if len(req_args) > 1 and req_args[1] == "assign": if settings.get_incident_label(): # == "Ticket" label = T("New Ticket") else: label = T("New Incident") attr["rheader"] = A(label, _class = "action-btn", _href = URL(c="event", f="incident", args = ["create"], vars = {"incident_report_id": req_args[0]}, ), ) else: attr["rheader"] = event_rheader return attr settings.customise_event_incident_report_controller = customise_event_incident_report_controller # ------------------------------------------------------------------------- def event_incident_create_onaccept(form): """ Automate Level based on Type, Zone (intersect from Location) & Severity @ToDo: Move this to SAFIRE/SC """ db = current.db s3db = current.s3db form_vars_get = form.vars.get incident_id = form_vars_get("id") # If Incident Type is Chemical then level must be > 2 level = form_vars_get("level") if level and int(level) < 3: incident_type_id = form_vars_get("incident_type_id") ittable = s3db.event_incident_type incident_type = db(ittable.id == incident_type_id).select(ittable.name, limitby = (0,1) ).first().name if incident_type == "Chemical Hazard": itable = s3db.event_incident db(itable.id == incident_id).update(level = 3) current.response.warning = T("Chemical Hazard Incident so Level raised to 3") # Alert Lead Agency organisation_id = form_vars_get("organisation_id") if organisation_id: otable = s3db.org_organisation_tag query = (otable.organisation_id == organisation_id) & \ (otable.tag == "duty") duty = db(query).select(otable.value, limitby = (0, 1) ).first() if duty: current.msg.send_sms_via_api(duty.value, "You have been assigned an Incident: %s%s" % (settings.get_base_public_url(), URL(c="event", f= "incident", args = incident_id), )) # ------------------------------------------------------------------------- def customise_event_incident_resource(r, tablename): from s3 import S3LocationSelector s3db = current.s3db table = s3db.event_incident f = table.severity f.readable = f.writable = True f = table.level f.readable = f.writable = True table.location_id.widget = S3LocationSelector(polygons = True, show_address = True, ) f = table.organisation_id f.readable = f.writable = True f.label = T("Lead Response Organization") if r.method == "plan": table.action_plan.label = T("Event Action Plan") else: f = table.action_plan f.readable = f.writable = False if r.interactive: s3db.add_custom_callback(tablename, "create_onaccept", event_incident_create_onaccept, ) settings.customise_event_incident_resource = customise_event_incident_resource # ------------------------------------------------------------------------- def customise_event_incident_controller(**attr): s3db = current.s3db s3 = current.response.s3 # Custom prep standard_prep = s3.prep def custom_prep(r): # Call standard postp if callable(standard_prep): result = standard_prep(r) if not result: return False resource = r.resource # Redirect to action plan after create resource.configure(create_next = URL(c="event", f="incident", args = ["[id]", "plan"]), ) method = r.method if method == "create": incident_report_id = r.get_vars.get("incident_report_id") if incident_report_id: # Got here from incident report assign => "New Incident" # - prepopulate incident name from report title # - copy incident type and location from report # - onaccept: link the incident report to the incident if r.http == "GET": from s3 import s3_truncate rtable = s3db.event_incident_report incident_report = current.db(rtable.id == incident_report_id).select(rtable.name, rtable.incident_type_id, rtable.location_id, limitby = (0, 1), ).first() table = r.table table.name.default = s3_truncate(incident_report.name, 64) table.incident_type_id.default = incident_report.incident_type_id table.location_id.default = incident_report.location_id elif r.http == "POST": def create_onaccept(form): s3db.event_incident_report_incident.insert(incident_id = form.vars.id, incident_report_id = incident_report_id, ) s3db.add_custom_callback("event_incident", "create_onaccept", create_onaccept, ) elif method == "plan" and settings.get_incident_label(): # == "Ticket" s3db.event_task s3db.event_organisation crud_strings = s3.crud_strings crud_strings.event_task.msg_list_empty = T("No Tasks currently registered for this ticket") crud_strings.event_organisation.msg_list_empty = T("No Organizations currently registered in this ticket") return True s3.prep = custom_prep # No sidebar menu current.menu.options = None attr["rheader"] = event_rheader return attr settings.customise_event_incident_controller = customise_event_incident_controller # ------------------------------------------------------------------------- def customise_event_asset_resource(r, tablename): table = current.s3db.event_asset table.item_id.label = T("Item Type") table.asset_id.label = T("Specific Item") # DateTime from gluon import IS_EMPTY_OR from s3 import IS_UTC_DATETIME, S3CalendarWidget, S3DateTime for f in (table.start_date, table.end_date): f.requires = IS_EMPTY_OR(IS_UTC_DATETIME()) f.represent = lambda dt: S3DateTime.datetime_represent(dt, utc=True) f.widget = S3CalendarWidget(timepicker = True) if settings.get_incident_label(): # == "Ticket" current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Equipment"), title_display = T("Equipment Details"), title_list = T("Equipment"), title_update = T("Edit Equipment"), label_list_button = T("List Equipment"), label_delete_button = T("Remove Equipment from this ticket"), msg_record_created = T("Equipment added"), msg_record_modified = T("Equipment updated"), msg_record_deleted = T("Equipment removed"), msg_list_empty = T("No Equipment currently registered for this ticket")) else: current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Equipment"), title_display = T("Equipment Details"), title_list = T("Equipment"), title_update = T("Edit Equipment"), label_list_button = T("List Equipment"), label_delete_button = T("Remove Equipment from this incident"), msg_record_created = T("Equipment added"), msg_record_modified = T("Equipment updated"), msg_record_deleted = T("Equipment removed"), msg_list_empty = T("No Equipment currently registered for this incident")) settings.customise_event_asset_resource = customise_event_asset_resource # ------------------------------------------------------------------------- def event_human_resource_onaccept(form, create=True): """ When a Position is assigned to an Incident: - set_event_from_incident - add Log Entry - send Notification """ db = current.db s3db = current.s3db s3db.event_set_event_from_incident(form, "event_human_resource") table = s3db.event_human_resource form_vars = form.vars form_vars_get = form_vars.get link_id = form_vars_get("id") incident_id = form_vars_get("incident_id") if not incident_id: link = db(table.id == link_id).select(table.incident_id, limitby = (0, 1) ).first() incident_id = link.incident_id pe_id = None if create: person_id = form_vars_get("person_id") if person_id: ptable = s3db.pr_person person = db(ptable.id == person_id).select(ptable.pe_id, limitby = (0, 1) ).first() pe_id = person.pe_id job_title_id = form_vars_get("job_title_id") if job_title_id: s3db.event_incident_log.insert(incident_id = incident_id, name = "Person Requested", comments = s3db.event_human_resource.job_title_id.represent(job_title_id), ) else: # Update record = form.record if record: # Not True for a record merger from s3dal import Field changed = {} for var in form_vars: vvar = form_vars[var] if isinstance(vvar, Field): # modified_by/modified_on continue rvar = record.get(var, "NOT_PRESENT") if rvar != "NOT_PRESENT" and vvar != rvar: f = table[var] if var == "pe_id": pe_id = vvar type_ = f.type if type_ == "integer" or \ type_.startswith("reference"): if vvar: vvar = int(vvar) if vvar == rvar: continue represent = table[var].represent if represent: if hasattr(represent, "show_link"): represent.show_link = False else: represent = lambda o: o if rvar: changed[var] = "%s changed from %s to %s" % \ (f.label, represent(rvar), represent(vvar)) else: changed[var] = "%s changed to %s" % \ (f.label, represent(vvar)) if changed: table = s3db.event_incident_log text = [] for var in changed: text.append(changed[var]) text = "\n".join(text) table.insert(incident_id = incident_id, #name = "Person Assigned", name = "Person Request Updated", comments = text, ) if pe_id: # Notify Assignee if settings.get_incident_label(): # == "Ticket" label = T("Ticket") else: label = T("Incident") current.msg.send_by_pe_id(pe_id, subject = "", message = "You have been assigned to an %s: %s%s" % \ (label, settings.get_base_public_url(), URL(c="event", f= "incident", args = [incident_id, "human_resource", link_id]), ), contact_method = "SMS") # ------------------------------------------------------------------------- def customise_event_human_resource_resource(r, tablename): s3db = current.s3db table = s3db.event_human_resource # DateTime from gluon import IS_EMPTY_OR from s3 import IS_UTC_DATETIME, S3CalendarWidget, S3DateTime for f in (table.start_date, table.end_date): f.requires = IS_EMPTY_OR(IS_UTC_DATETIME()) f.represent = lambda dt: S3DateTime.datetime_represent(dt, utc=True) f.widget = S3CalendarWidget(timepicker = True) if settings.get_incident_label(): # == "Ticket" current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Person"), title_display = T("Person Details"), title_list = T("Personnel"), title_update = T("Edit Person"), label_list_button = T("List Personnel"), label_delete_button = T("Remove Person from this ticket"), msg_record_created = T("Person added"), msg_record_modified = T("Person updated"), msg_record_deleted = T("Person removed"), msg_list_empty = T("No Persons currently registered for this ticket")) else: current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Person"), title_display = T("Person Details"), title_list = T("Personnel"), title_update = T("Edit Person"), label_list_button = T("List Personnel"), label_delete_button = T("Remove Person from this incident"), msg_record_created = T("Person added"), msg_record_modified = T("Person updated"), msg_record_deleted = T("Person removed"), msg_list_empty = T("No Persons currently registered for this incident")) s3db.configure(tablename, # Deliberately over-rides create_onaccept = event_human_resource_onaccept, update_onaccept = lambda form: event_human_resource_onaccept(form, create=False), ) settings.customise_event_human_resource_resource = customise_event_human_resource_resource # ------------------------------------------------------------------------- def customise_event_scenario_controller(**attr): s3 = current.response.s3 # Custom prep standard_prep = s3.prep def custom_prep(r): # Call standard postp if callable(standard_prep): result = standard_prep(r) if not result: return False if r.method != "plan": f = r.table.action_plan f.readable = f.writable = False if r.method == "create"and r.http == "POST": r.resource.configure(create_next = URL(c="event", f="scenario", args = ["[id]", "plan"]), ) return True s3.prep = custom_prep # No sidebar menu current.menu.options = None attr["rheader"] = event_rheader return attr settings.customise_event_scenario_controller = customise_event_scenario_controller # ------------------------------------------------------------------------- def customise_event_scenario_asset_resource(r, tablename): table = current.s3db.event_scenario_asset table.item_id.label = T("Item Type") table.asset_id.label = T("Specific Item") if settings.get_incident_label(): # == "Ticket" current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Equipment"), title_display = T("Equipment Details"), title_list = T("Equipment"), title_update = T("Edit Equipment"), label_list_button = T("List Equipment"), label_delete_button = T("Remove Equipment from this ticket"), msg_record_created = T("Equipment added"), msg_record_modified = T("Equipment updated"), msg_record_deleted = T("Equipment removed"), msg_list_empty = T("No Equipment currently registered for this ticket")) else: current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Equipment"), title_display = T("Equipment Details"), title_list = T("Equipment"), title_update = T("Edit Equipment"), label_list_button = T("List Equipment"), label_delete_button = T("Remove Equipment from this incident"), msg_record_created = T("Equipment added"), msg_record_modified = T("Equipment updated"), msg_record_deleted = T("Equipment removed"), msg_list_empty = T("No Equipment currently registered for this incident")) settings.customise_event_scenario_asset_resource = customise_event_scenario_asset_resource # ------------------------------------------------------------------------- def customise_event_scenario_human_resource_resource(r, tablename): if settings.get_incident_label(): # == "Ticket" current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Person"), title_display = T("Person Details"), title_list = T("Personnel"), title_update = T("Edit Person"), label_list_button = T("List Personnel"), label_delete_button = T("Remove Person from this ticket"), msg_record_created = T("Person added"), msg_record_modified = T("Person updated"), msg_record_deleted = T("Person removed"), msg_list_empty = T("No Persons currently registered for this ticket")) else: current.response.s3.crud_strings[tablename] = Storage( label_create = T("Add Person"), title_display = T("Person Details"), title_list = T("Personnel"), title_update = T("Edit Person"), label_list_button = T("List Personnel"), label_delete_button = T("Remove Person from this incident"), msg_record_created = T("Person added"), msg_record_modified = T("Person updated"), msg_record_deleted = T("Person removed"), msg_list_empty = T("No Persons currently registered for this incident")) settings.customise_event_scenario_human_resource_resource = customise_event_scenario_human_resource_resource # ------------------------------------------------------------------------- # HRM # ------------------------------------------------------------------------- settings.hrm.job_title_deploy = True settings.hrm.org_dependent_job_titles = True # ------------------------------------------------------------------------- # Organisations # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- def customise_org_organisation_resource(r, tablename): s3db = current.s3db # Custom Components s3db.add_components(tablename, org_organisation_tag = (# On-call Duty Number {"name": "duty", "joinby": "organisation_id", "filterby": {"tag": "duty", }, "multiple": False, }, ), ) from s3 import S3SQLCustomForm, S3SQLInlineComponent, S3SQLInlineLink, \ IS_EMPTY_OR, IS_PHONE_NUMBER_MULTI, S3PhoneWidget, s3_phone_represent # Individual settings for specific tag components components_get = s3db.resource(tablename).components.get duty = components_get("duty") f = duty.table.value f.represent = s3_phone_represent, f.requires = IS_EMPTY_OR(IS_PHONE_NUMBER_MULTI()) f.widget = S3PhoneWidget() crud_form = S3SQLCustomForm("name", "acronym", S3SQLInlineLink("organisation_type", field = "organisation_type_id", # Default 10 options just triggers which adds unnecessary complexity to a commonly-used form & commonly an early one (create Org when registering) search = False, label = T("Type"), multiple = False, widget = "multiselect", ), "country", (T("Reception Phone #"), "phone"), S3SQLInlineComponent("duty", label = T("On-call Duty Number"), fields = [("", "value")], multiple = False, ), "website", "logo", "comments", ) s3db.configure(tablename, crud_form = crud_form, ) settings.customise_org_organisation_resource = customise_org_organisation_resource # ------------------------------------------------------------------------- # Projects # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- def project_task_onaccept(form, create=True): """ Send Person a Notification when they are assigned to a Task Log changes in Incident Log """ if current.request.function == "scenario": # Must be a Scenario # - don't Log # - don't send Notification return db = current.db s3db = current.s3db ltable = s3db.event_task form_vars = form.vars form_vars_get = form_vars.get task_id = form_vars_get("id") link = db(ltable.task_id == task_id).select(ltable.incident_id, limitby = (0, 1) ).first() if not link: # Not attached to an Incident # - don't Log # - don't send Notification return incident_id = link.incident_id if create: pe_id = form_vars_get("pe_id") # Log name = form_vars_get("name") if name: s3db.event_incident_log.insert(incident_id = incident_id, name = "Task Created", comments = name, ) else: # Update pe_id = None record = form.record if record: # Not True for a record merger from s3dal import Field table = s3db.project_task changed = {} for var in form_vars: vvar = form_vars[var] if isinstance(vvar, Field): # modified_by/modified_on continue if var == "pe_id": pe_id = vvar rvar = record.get(var, "NOT_PRESENT") if rvar != "NOT_PRESENT" and vvar != rvar: f = table[var] type_ = f.type if type_ == "integer" or \ type_.startswith("reference"): if vvar: vvar = int(vvar) if vvar == rvar: continue represent = table[var].represent if represent: if hasattr(represent, "show_link"): represent.show_link = False else: represent = lambda o: o if rvar: changed[var] = "%s changed from %s to %s" % \ (f.label, represent(rvar), represent(vvar)) else: changed[var] = "%s changed to %s" % \ (f.label, represent(vvar)) if changed: table = s3db.event_incident_log text = [] for var in changed: text.append(changed[var]) text = "\n".join(text) table.insert(incident_id = incident_id, name = "Task Updated", comments = text, ) if pe_id: # Notify Assignee message = "You have been assigned a Task: %s%s" % \ (settings.get_base_public_url(), URL(c="event", f= "incident", args = [incident_id, "task", task_id]), ) instance_type = s3db.pr_instance_type(pe_id) if instance_type == "org_organisation": # Notify the Duty Number for the Organisation, not everyone in the Organisation! otable = s3db.org_organisation ottable = s3db.org_organisation_tag query = (otable.pe_id == pe_id) & \ (ottable.organisation_id == otable.id) & \ (ottable.tag == "duty") duty = db(query).select(ottable.value, limitby = (0, 1) ).first() if duty: current.msg.send_sms_via_api(duty.value, message) else: task_notification = settings.get_event_task_notification() if task_notification: current.msg.send_by_pe_id(pe_id, subject = "%s: Task assigned to you" % settings.get_system_name_short(), message = message, contact_method = task_notification) # ------------------------------------------------------------------------- def customise_project_task_resource(r, tablename): s3db = current.s3db f = s3db.project_task.source f.readable = f.writable = False s3db.configure(tablename, # No need to see time log: KISS crud_form = None, # NB We deliberatly over-ride the default one create_onaccept = project_task_onaccept, # In event_ActionPlan() #list_fields = ["priority", # "name", # "pe_id", # "status_id", # "date_due", # ], update_onaccept = lambda form: project_task_onaccept(form, create=False), ) settings.customise_project_task_resource = customise_project_task_resource # END =========================================================================