1214 lines
54 KiB
Python
1214 lines
54 KiB
Python
# -*- 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 =========================================================================
|