diff --git a/sahana-spotter/Dockerfile b/sahana-spotter/Dockerfile new file mode 100644 index 0000000..68741b1 --- /dev/null +++ b/sahana-spotter/Dockerfile @@ -0,0 +1,9 @@ +FROM sahana:latest + +COPY image.d / + +RUN \ + # Change ownership of the newly copied files + find /srv/web2py ! -user sahana -exec chown -R sahana:sahana {} + + +VOLUME ["/srv/web2py/applications/eden/models"] diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/models/000_config.py b/sahana-spotter/image.d/srv/web2py/applications/eden/models/000_config.py new file mode 100644 index 0000000..01e2430 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/models/000_config.py @@ -0,0 +1,250 @@ +# -*- coding: utf-8 -*- + +""" + Machine-specific settings + All settings which are typically edited for a specific machine should be done here + + Deployers should ideally not need to edit any other files outside of their template folder + + Note for Developers: + /models/000_config.py is NOT in the Git repository, to avoid leaking of + sensitive or irrelevant information into the repository. + For changes to be committed, please also edit: + modules/templates/000_config.py +""" + +import os + +# Remove this line when you have edited this file sufficiently to proceed to the web interface +FINISHED_EDITING_CONFIG_FILE = True + +# Select the Template +# - which Modules are enabled +# - PrePopulate data +# - Security Policy +# - Workflows +# - Theme +# - note that you should restart your web2py after changing this setting +settings.base.template = "Spotter" + +# Database settings +# Uncomment to use a different database, other than sqlite +settings.database.db_type = "postgres" +#settings.database.db_type = "mysql" +# Uncomment to use a different host +settings.database.host = os.getenv("POSTGRES_HOST", "localhost") +# Uncomment to use a different port +#settings.database.port = 3306 +#settings.database.port = 5432 +# Uncomment to select a different name for your database +settings.database.database = os.getenv("POSTGRES_DB", "sahana") +# Uncomment to select a different username for your database +settings.database.username = os.getenv("POSTGRES_USER", "sahana") +# Uncomment to set the password +# NB Web2Py doesn't like passwords with an @ in them +settings.database.password = os.getenv("POSTGRES_PASSWORD", "password") +# Uncomment to use a different pool size +#settings.database.pool_size = 30 +# Do we have a spatial DB available? (currently supports PostGIS. Spatialite to come.) +settings.gis.spatialdb = True + +# Base settings +settings.base.system_name = T("Spotter Cluster") +settings.base.system_name_short = T("Spotter") +# Set this to the Public URL of the instance +settings.base.public_url = os.getenv("SAHANA_PUBLIC_URL", "http://127.0.0.1:8080") + +# Switch to "False" in Production for a Performance gain +# (need to set to "True" again when Table definitions are changed) +settings.base.migrate = True +# To just create the .table files (also requires migrate=True): +#settings.base.fake_migrate = True + +# Set this to True to switch to Debug mode +# Debug mode means that uncompressed CSS/JS files are loaded +# JS Debug messages are also available in the Console +# can also load an individual page in debug mode by appending URL with +# ?debug=1 +settings.base.debug = True + +# Uncomment this to prevent automated test runs from remote +# settings.base.allow_testing = False + +# Configure the log level ("DEBUG", "INFO", "WARNING", "ERROR" or "CRITICAL"), None = turn off logging (default) +#settings.log.level = "ERROR" # DEBUG set automatically when base.debug is True +# Uncomment to prevent writing log messages to the console (sys.stderr) +#settings.log.console = False +# Configure a log file (file name) +#settings.log.logfile = None +# Uncomment to get detailed caller information +#settings.log.caller_info = True + +# Uncomment to use Content Delivery Networks to speed up Internet-facing sites +#settings.base.cdn = True + +# Allow language files to be updated automatically +#settings.L10n.languages_readonly = False + +# This setting should be changed _before_ registering the 1st user +# - should happen automatically if installing using supported scripts +settings.auth.hmac_key = os.getenv("SAHANA_HMAC_KEY", "akeytochange") + +# If using Masterkey Authentication, then set this to a deployment-specific 32 char string: +#settings.auth.masterkey_app_key = "randomstringrandomstringrandomstring" + +# Minimum Password Length +#settings.auth.password_min_length = 8 + +# Email settings +# Outbound server +settings.mail.server = os.getenv("SAHANA_MAIL_HOST", "127.0.0.1:25") +#settings.mail.tls = True +# Useful for Windows Laptops: +# https://www.google.com/settings/security/lesssecureapps +#settings.mail.server = "smtp.gmail.com:587" +#settings.mail.tls = True +#settings.mail.login = "username:password" +# From Address - until this is set, no mails can be sent +settings.mail.sender = os.getenv("SAHANA_MAIL_SENDER", "'Sahana' ") +# Default email address to which requests to approve new user accounts gets sent +# This can be overridden for specific domains/organisations via the auth_domain table +settings.mail.approver = os.getenv("SAHANA_MAIL_APPROVER", "useradmin@example.org") +# Daily Limit on Sending of emails +#settings.mail.limit = 1000 + +# Uncomment to restrict to specific country/countries +#settings.gis.countries= ("LK",) + +# Bing API Key (for Map layers) +# http://www.microsoft.com/maps/create-a-bing-maps-key.aspx +#settings.gis.api_bing = "" +# GetAddress API Key (for GetAddress.io Postcode to Address lookup) +#settings.gis.api_getaddress = "" +# Google API Key (for Google Maps Layers) +settings.gis.api_google = os.getenv("SAHANA_API_KEY_GOOGLE", "") +# OpenWeatherMap API Key (for OpenWeatherMap Layers) +settings.gis.api_openweathermap = os.getenv("SAHANA_API_KEY_OPENWEATHERMAP", "") + +# GeoNames username +#settings.gis.geonames_username = "" + +# Fill this in to get a Facebook Pixel for your site +#settings.base.facebook_pixel_id = "" +# Fill this in to get Google Analytics for your site +#settings.base.google_analytics_tracking_id = "" + +# Chat server, see: http://eden.sahanafoundation.org/wiki/InstallationGuidelines/Chat +#settings.base.chat_server = { +# "ip": "127.0.0.1", +# "port": 7070, +# "name": "servername", +# # Default group everyone is added to +# "groupname" : "everyone", +# "server_db" : "openfire", +# # These settings fallback to main DB settings if not specified +# # Only mysql/postgres supported +# #"server_db_type" : "mysql", +# #"server_db_username" : "", +# #"server_db_password": "", +# #"server_db_port" : 3306, +# #"server_db_ip" : "127.0.0.1", +# } + +# GeoServer (Currently used by GeoExplorer. Will allow REST control of GeoServer.) +# NB Needs to be publically-accessible URL for querying via client JS +#settings.gis.geoserver_url = "http://localhost/geoserver" +#settings.gis.geoserver_username = "admin" +#settings.gis.geoserver_password = "" +# Print Service URL: http://eden.sahanafoundation.org/wiki/BluePrintGISPrinting +#settings.gis.print_service = "/geoserver/pdf/" + +# Google OAuth (to allow users to login using Google) +# https://code.google.com/apis/console/ +#settings.auth.google_id = "" +#settings.auth.google_secret = "" + +# Pootle server +# settings.L10n.pootle_url = "http://pootle.sahanafoundation.org/" +# settings.L10n.pootle_username = "username" +# settings.L10n.pootle_password = "*****" + +# SOLR server for Full-Text Search +#settings.base.solr_url = "http://127.0.0.1:8983/solr/" + +# Memcache server to allow sharing of sessions across instances +#settings.base.session_memcache = '127.0.0.1:11211' + +settings.base.session_db = True + +# UI options +# Should user be prompted to save before navigating away? +#settings.ui.navigate_away_confirm = False +# Should user be prompted to confirm actions? +#settings.ui.confirm = False +# Should potentially large dropdowns be turned into autocompletes? +# (unused currently) +#settings.ui.autocomplete = True +#settings.ui.read_label = "Details" +#settings.ui.update_label = "Edit" + +# Audit settings +# - can be a callable for custom hooks (return True to also perform normal logging, or False otherwise) +# NB Auditing (especially Reads) slows system down & consumes diskspace +#settings.security.audit_write = False +#settings.security.audit_read = False + +# Performance Options +# Maximum number of search results for an Autocomplete Widget +#settings.search.max_results = 200 +# Maximum number of features for a Map Layer +#settings.gis.max_features = 1000 + +# CAP Settings +# Change for different authority and organisations +# See http://alerting.worldweather.org/ for oid +# Country root oid. The oid for the organisation includes this base +#settings.cap.identifier_oid = "2.49.0.0.608.0" +# Set the period (in days) after which alert info segments expire (default=2) +#settings.cap.info_effective_period = 2 + +# ============================================================================= +# Import the settings from the Template +# - note: invalid settings are ignored +# +settings.import_template() + +# ============================================================================= +# Over-rides to the Template may be done here +# + +# e.g. +#settings.security.self_registration = True +#settings.base.system_name = T("Sahana TEST") +#settings.base.prepopulate = ("MY_TEMPLATE_ONLY") +#settings.base.prepopulate += ("default", "default/users") +#settings.base.theme = "default" +settings.L10n.default_language = "cs" +#settings.security.policy = 7 # Organisation-ACLs +# Enable Additional Module(s) +#from gluon.storage import Storage +#settings.modules["delphi"] = Storage( +# name_nice = T("Delphi Decision Maker"), +# restricted = False, +# module_type = 10, +# ) +# Disable a module which is normally used by the template +# - NB Only templates with adaptive menus will work nicely with this! +#del settings.modules["irs"] + +# Production instances should set this before prepopulate is run +settings.base.prepopulate_demo = 0 + +# After 1st_run, set this for Production to save 1x DAL hit/request +#settings.base.prepopulate = 0 + +# ============================================================================= +# A version number to tell update_check if there is a need to refresh the +# running copy of this file +VERSION = 1 + +# END ========================================================================= diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/models/00_settings.py b/sahana-spotter/image.d/srv/web2py/applications/eden/models/00_settings.py new file mode 100644 index 0000000..ffbfdb0 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/models/00_settings.py @@ -0,0 +1,309 @@ +# -*- coding: utf-8 -*- + +""" + Global settings: + + Those which are typically edited during a deployment are in + 000_config.py & their results parsed into here. Deployers + shouldn't typically need to edit any settings here. +""" + +# Keep all our configuration options off the main global variables + +# Use response.s3 for one-off variables which are visible in views without explicit passing +s3.formats = Storage() + +# Workaround for this Bug in Selenium with FF4: +# http://code.google.com/p/selenium/issues/detail?id=1604 +s3.interactive = settings.get_ui_confirm() + +s3.base_url = "%s/%s" % (settings.get_base_public_url(), + appname, + ) +s3.download_url = "%s/default/download" % s3.base_url + +# ----------------------------------------------------------------------------- +# Client tests + +# Check whether browser is Mobile & store result in session +# - commented-out until we make use of it +#if session.s3.mobile is None: +# from s3 import s3_is_mobile_client +# session.s3.mobile = s3_is_mobile_client(request) +#if session.s3.browser is None: +# from s3 import s3_populate_browser_compatibility +# session.s3.browser = s3_populate_browser_compatibility(request) + +# ----------------------------------------------------------------------------- +# Global variables + +# Strings to i18n +# Common Labels +#messages["BREADCRUMB"] = ">> " +messages["UNKNOWN_OPT"] = "" +messages["NONE"] = "" +messages["OBSOLETE"] = "Obsolete" +messages["READ"] = settings.get_ui_label_read() +messages["UPDATE"] = settings.get_ui_label_update() +messages["DELETE"] = "Delete" +messages["COPY"] = "Copy" +messages["NOT_APPLICABLE"] = "N/A" +messages["ADD_PERSON"] = "Create a Person" +messages["ADD_LOCATION"] = "Create Location" +messages["SELECT_LOCATION"] = "Select a location" +messages["COUNTRY"] = "Country" +messages["ORGANISATION"] = "Organization" +messages["AUTOCOMPLETE_HELP"] = "Enter some characters to bring up a list of possible matches" + +for u in messages: + if isinstance(messages[u], str): + globals()[u] = T(messages[u]) + +# CRUD Labels +s3.crud_labels = Storage(READ = READ, + UPDATE = UPDATE, + DELETE = DELETE, + COPY = COPY, + NONE = NONE, + ) + +# Error Messages +ERROR["BAD_RECORD"] = "Record not found!" +ERROR["BAD_METHOD"] = "Unsupported method!" +ERROR["BAD_FORMAT"] = "Unsupported data format!" +ERROR["BAD_REQUEST"] = "Invalid request" +ERROR["BAD_SOURCE"] = "Invalid source" +ERROR["BAD_TEMPLATE"] = "XSLT stylesheet not found" +ERROR["BAD_RESOURCE"] = "Nonexistent or invalid resource" +ERROR["DATA_IMPORT_ERROR"] = "Data import error" +ERROR["INTEGRITY_ERROR"] = "Integrity error: record can not be deleted while it is referenced by other records" +ERROR["METHOD_DISABLED"] = "Method disabled" +ERROR["NO_MATCH"] = "No matching element found in the data source" +ERROR["NOT_IMPLEMENTED"] = "Not implemented" +ERROR["NOT_PERMITTED"] = "Operation not permitted" +ERROR["PARSE_ERROR"] = "XML parse error" +ERROR["TRANSFORMATION_ERROR"] = "XSLT transformation error" +ERROR["UNAUTHORISED"] = "Not Authorized" +ERROR["VALIDATION_ERROR"] = "Validation error" + +# To get included in +s3.stylesheets = [] +s3.external_stylesheets = [] +# To get included at the end of +s3.scripts = [] +s3.scripts_modules = [] +s3.js_global = [] +s3.jquery_ready = [] + +# ----------------------------------------------------------------------------- +# Languages + +s3.l10n_languages = settings.get_L10n_languages() + +# Default strings are in US English +T.current_languages = ("en", "en-us") +# Check if user has selected a specific language +if get_vars._language: + language = get_vars._language + session.s3.language = language +elif session.s3.language: + # Use the last-selected language + language = session.s3.language +elif auth.is_logged_in(): + # Use user preference + language = auth.user.language +else: + # Use system default + language = settings.get_L10n_default_language() +#else: +# # Use what browser requests (default web2py behaviour) +# T.force(T.http_accept_language) + +# IE doesn't set request.env.http_accept_language +#if language != "en": +T.force(language) + +# Store for views (e.g. Ext) +if language.find("-") == -1: + # Ext peculiarities + if language == "vi": + s3.language = "vn" + elif language == "el": + s3.language = "el_GR" + else: + s3.language = language +else: + lang_parts = language.split("-") + s3.language = "%s_%s" % (lang_parts[0], lang_parts[1].upper()) + +# List of Languages which use a Right-to-Left script (Arabic, Hebrew, Farsi, Urdu) +if language in ("ar", "prs", "ps", "ur"): + s3.rtl = True +else: + s3.rtl = False + +# ----------------------------------------------------------------------------- +# Auth + +_settings = auth.settings +_settings.lock_keys = False + +_settings.expiration = 28800 # seconds + +if settings.get_auth_openid(): + # Requires http://pypi.python.org/pypi/python-openid/ + try: + from gluon.contrib.login_methods.openid_auth import OpenIDAuth + openid_login_form = OpenIDAuth(auth) + from gluon.contrib.login_methods.extended_login_form import ExtendedLoginForm + _settings.login_form = ExtendedLoginForm(auth, openid_login_form, + signals = ["oid", "janrain_nonce"], + ) + except ImportError: + session.warning = "Library support not available for OpenID" + +# Allow use of LDAP accounts for login +# NB Currently this means that change password should be disabled: +#_settings.actions_disabled.append("change_password") +# (NB These are not automatically added to PR or to Authenticated role since they enter via the login() method not register()) +#from gluon.contrib.login_methods.ldap_auth import ldap_auth +# Require even alternate login methods to register users 1st +#_settings.alternate_requires_registration = True +# Active Directory +#_settings.login_methods.append(ldap_auth(mode="ad", server="dc.domain.org", base_dn="ou=Users,dc=domain,dc=org")) +# or if not wanting local users at all (no passwords saved within DB): +#_settings.login_methods = [ldap_auth(mode="ad", server="dc.domain.org", base_dn="ou=Users,dc=domain,dc=org")] +# Domino +#_settings.login_methods.append(ldap_auth(mode="domino", server="domino.domain.org")) +# OpenLDAP +#_settings.login_methods.append(ldap_auth(server="directory.sahanafoundation.org", base_dn="ou=users,dc=sahanafoundation,dc=org")) +# Allow use of Email accounts for login +#_settings.login_methods.append(email_auth("smtp.gmail.com:587", "@gmail.com")) + +# Require captcha verification for registration +#auth.settings.captcha = RECAPTCHA(request, public_key="PUBLIC_KEY", private_key="PRIVATE_KEY") +# Require Email Verification +_settings.registration_requires_verification = settings.get_auth_registration_requires_verification() +_settings.on_failed_authorization = URL(c="default", f="user", + args = "not_authorized", + ) +_settings.reset_password_requires_verification = True +_settings.verify_email_next = URL(c="default", f="index") + +# Require Admin approval for self-registered users +_settings.registration_requires_approval = settings.get_auth_registration_requires_approval() + +# We don't wish to clutter the groups list with 1 per user. +_settings.create_user_groups = False +# We need to allow basic logins for Webservices +_settings.allow_basic_login = True + +_settings.logout_onlogout = s3_auth_on_logout +_settings.login_onaccept = s3_auth_on_login +# Now read in auth.login() to avoid setting unneccesarily in every request +#_settings.login_next = settings.get_auth_login_next() +if settings.has_module("vol") and \ + settings.get_auth_registration_volunteer(): + _settings.register_next = URL(c="vol", f="person") + +# Languages available in User Profiles +#if len(s3.l10n_languages) > 1: +# from s3 import IS_ISO639_2_LANGUAGE_CODE +# _settings.table_user.language.requires = IS_ISO639_2_LANGUAGE_CODE(sort = True, +# translate = True, +# zero = None, +# ) +#else: +# field = _settings.table_user.language +# field.default = s3.l10n_languages.keys()[0] +# field.readable = False +# field.writable = False + +_settings.lock_keys = True + +# ----------------------------------------------------------------------------- +# Mail + +# These settings could be made configurable as part of the Messaging Module +# - however also need to be used by Auth (order issues) +sender = settings.get_mail_sender() +if sender: + mail.settings.sender = sender + mail.settings.server = settings.get_mail_server() + mail.settings.tls = settings.get_mail_server_tls() + mail_server_login = settings.get_mail_server_login() + if mail_server_login: + mail.settings.login = mail_server_login + # Email settings for registration verification and approval + _settings.mailer = mail + +# ----------------------------------------------------------------------------- +# Session + +# Custom Notifications +response.error = session.error +response.confirmation = session.confirmation +response.information = session.information +response.warning = session.warning +session.error = [] +session.confirmation = [] +session.information = [] +session.warning = [] + +# Shortcuts for system role IDs, see modules/s3aaa.py/AuthS3 +#system_roles = auth.get_system_roles() +#ADMIN = system_roles.ADMIN +#AUTHENTICATED = system_roles.AUTHENTICATED +#ANONYMOUS = system_roles.ANONYMOUS +#EDITOR = system_roles.EDITOR +#MAP_ADMIN = system_roles.MAP_ADMIN +#ORG_ADMIN = system_roles.ORG_ADMIN +#ORG_GROUP_ADMIN = system_roles.ORG_GROUP_ADMIN + +# ----------------------------------------------------------------------------- +# CRUD + +s3_formstyle = settings.get_ui_formstyle() +s3_formstyle_read = settings.get_ui_formstyle_read() +s3_formstyle_mobile = s3_formstyle +submit_button = T("Save") +s3_crud = s3.crud +s3_crud.formstyle = s3_formstyle +s3_crud.formstyle_read = s3_formstyle_read +s3_crud.submit_button = submit_button +# Optional class for Submit buttons +#s3_crud.submit_style = "submit-button" +s3_crud.confirm_delete = T("Do you really want to delete these records?") +s3_crud.archive_not_delete = settings.get_security_archive_not_delete() +s3_crud.navigate_away_confirm = settings.get_ui_navigate_away_confirm() + +# JSON Formats +s3.json_formats = ("geojson", "s3json") + +# CSV Formats +s3.csv_formats = ("hrf", "s3csv") + +# Datatables default number of rows per page +s3.ROWSPERPAGE = 20 + +# Valid Extensions for Image Upload fields +s3.IMAGE_EXTENSIONS = ("png", "PNG", "jpg", "JPG", "jpeg", "JPEG") + +# Default CRUD strings +s3.crud_strings = Storage(label_create = T("Add Record"), + title_display = T("Record Details"), + title_list = T("Records"), + title_update = T("Edit Record"), + title_map = T("Map"), + title_report = T("Report"), + label_list_button = T("List Records"), + label_delete_button = T("Delete Record"), + msg_record_created = T("Record added"), + msg_record_modified = T("Record updated"), + msg_record_deleted = T("Record deleted"), + msg_list_empty = T("No Records currently available"), + msg_match = T("Matching Records"), + msg_no_match = T("No Matching Records"), + ) + +# END ========================================================================= diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/SAMBRO/config.py b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/SAMBRO/config.py new file mode 100644 index 0000000..36ebaa8 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/SAMBRO/config.py @@ -0,0 +1,1595 @@ +# -*- coding: utf-8 -*- + +import json +import os + +from collections import OrderedDict +from io import BytesIO + +from gluon import current +from gluon.html import * +from gluon.storage import Storage +from gluon.languages import lazyT + +from s3 import FS, s3_str, s3_truncate, s3_utc + +def config(settings): + """ + Template settings for CAP: Common Alerting Protocol + """ + + T = current.T + +## Deprecated because such edits to the title should happen in the 000_config.py file +## specific to the demo deployment and not here because if you change here it affect any +## developments and commits to git etc - nuwan at sahanafoundation dot org + #settings.base.system_name = T("Sahana Alerting and Messaging Broker") + #settings.base.system_name_short = T("SAMBRO") +## + # Pre-Populate + settings.base.prepopulate.append("SAMBRO") + settings.base.prepopulate_demo += ("SAMBRO/Demo",) + + # Theme (folder to use for views/layout.html) + #settings.base.theme = "SAMBRO" + + # The Registration functionality shouldn't be visible to the Public + #settings.security.registration_visible = True + + settings.auth.registration_requires_approval = True + + # Link Users to Organisations + settings.auth.registration_requests_organisation = True + + # GeoNames username + settings.gis.geonames_username = "trendspotter" + settings.gis.simplify_tolerance = 0 + + # ========================================================================= + # System Settings + # ------------------------------------------------------------------------- + # Security Policy + # http://eden.sahanafoundation.org/wiki/S3/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 + settings.security.policy = 4 # Controller-Function ACLs + + # Record Approval + settings.auth.record_approval = True + # cap_alert record requires approval before sending + settings.auth.record_approval_required_for = ("cap_alert",) + # Don't auto-approve so that can save draft + settings.auth.record_approval_manual = ("cap_alert",) + + # ========================================================================= + # Module Settings + # ------------------------------------------------------------------------- + # CAP Settings + # Uncomment this according to country profile + #settings.cap.restrict_fields = True + + # ------------------------------------------------------------------------- + # Notifications + + # Template for the subject line in update notifications + #settings.msg.notify_subject = "%s $s %s" % (T("SAHANA"), T("Alert Notification")) + + # Notifications format + settings.msg.notify_email_format = "html" + + # Filename for FTP + # Characters not allowed are [\ / : * ? " < > | % .] + # https://en.wikipedia.org/wiki/Filename + # http://docs.attachmate.com/reflection/ftp/15.6/guide/en/index.htm?toc.htm?6503.htm + settings.sync.upload_filename = "$s-%s" % ("recent_alert") + + # Whether to tweet alerts + settings.cap.post_to_twitter = True + + # Whether to post alerts in facebook? + settings.cap.post_to_facebook = True + + # ALlow RSS to use links of entry if link fails + settings.cap.rss_use_links = True + + # SAMBRO supports ack workflow + settings.cap.use_ack = True + + # ------------------------------------------------------------------------- + # L10n (Localization) settings + languages = OrderedDict([ + ("cs", "Czech"), + ("en-US", "English"), + ]) + settings.cap.languages = languages + settings.L10n.languages = languages + # Translate the cap_area name + settings.L10n.translate_cap_area = True + + # Date Format + #settings.L10n.date_format = "%a, %d %B %Y" + + # Time Format + settings.L10n.time_format = "%H:%M:%S" + + # PDF font + settings.L10n.pdf_export_font = ['Helvetica', 'Helvetica-Bold'] + + # ------------------------------------------------------------------------- + # Messaging + # Parser + settings.msg.parser = "SAMBRO" + # Subscriptions + settings.msg.notify_check_subscriptions = True + + # ------------------------------------------------------------------------- + # Organisations + # Enable the use of Organisation Branches + settings.org.branches = True + + # ------------------------------------------------------------------------- + def customise_msg_rss_channel_resource(r, tablename): + + # @ToDo: We won't be able to automate this as we have 2 sorts, so will need the user to select manually + # Can we add a component for the parser for S3CSV imports? + + s3db = current.s3db + def onaccept(form): + # Normal onaccept + s3db.msg_channel_onaccept(form) + db = current.db + table = db.msg_rss_channel + form_vars = form.vars + record_id = form_vars.get("id", None) + form_type = form_vars.get("type", None) + type = current.request.get_vars.get("type", None) + query = (table.id == record_id) + if type == "cap" or form_type == "cap": + fn = "parse_rss_2_cap" + db(query).update(type = "cap") + else: + fn = "parse_rss_2_cms" + db(query).update(type = "cms") + channel_id = db(query).select(table.channel_id, + limitby=(0, 1)).first().channel_id + # Link to Parser + table = s3db.msg_parser + parser_id = table.insert(channel_id=channel_id, function_name=fn, enabled=True) + s3db.msg_parser_enable(parser_id) + + run_async = current.s3task.run_async + # Poll + run_async("msg_poll", args=["msg_rss_channel", channel_id]) + + # Parse + run_async("msg_parse", args=[channel_id, fn]) + + s3db.configure(tablename, + create_onaccept = onaccept, + ) + + settings.customise_msg_rss_channel_resource = customise_msg_rss_channel_resource + + # ------------------------------------------------------------------------- + def customise_msg_rss_channel_controller(**attr): + + s3 = current.response.s3 + channel_type = current.request.get_vars.get("type", None) + if channel_type == "cap": + # CAP RSS Channel + s3.filter = (FS("type") == "cap") + s3.crud_strings["msg_rss_channel"] = Storage( + label_create = T("Add CAP Feed"), + title_display = T("CAP Feed"), + title_list = T("CAP Feeds"), + title_update = T("Edit CAP Feed"), + label_list_button = T("List CAP Feeds"), + label_delete_button = T("Delete CAP Feed"), + msg_record_created = T("CAP Feed created"), + msg_record_modified = T("CAP Feed modified"), + msg_record_deleted = T("CAP Feed deleted"), + msg_list_empty = T("No CAP Feed to show")) + else: + # CMS RSS Channel + s3.filter = (FS("type") == "cms") + + # Custom postp + standard_postp = s3.postp + def custom_postp(r, output): + # Call standard postp + if callable(standard_postp): + output = standard_postp(r, output) + + if r.interactive and isinstance(output, dict): + # Modify Open Button + if channel_type == "cap": + # CAP RSS Channel + table = r.table + query = (table.deleted == False) + rows = current.db(query).select(table.id, + table.enabled, + ) + restrict_e = [str(row.id) for row in rows if not row.enabled] + restrict_d = [str(row.id) for row in rows if row.enabled] + + s3.actions = [{"label": s3_str(T("Open")), + "_class": "action-btn edit", + "url": URL(args = ["[id]", "update"], + vars = {"type": "cap"}, + ), + }, + {"label": s3_str(T("Delete")), + "_class": "delete-btn", + "url": URL(args = ["[id]", "delete"], + vars = {"type": "cap"}, + ), + }, + {"label": s3_str(T("Subscribe")), + "_class": "action-btn", + "url": URL(args = ["[id]", "enable"], + vars = {"type": "cap"}, + ), + "restrict": restrict_e, + }, + {"label": s3_str(T("Unsubscribe")), + "_class": "action-btn", + "url": URL(args = ["[id]", "disable"], + vars = {"type": "cap"}, + ), + "restrict": restrict_d + }, + ] + + if not current.s3task._is_alive(): + # No Scheduler Running + s3.actions.append({"label": s3_str(T("Poll")), + "_class": "action-btn", + "url": URL(args = ["[id]", "poll"], + vars = {"type": "cap"}, + ), + "restrict": restrict_d, + }) + + if "form" in output and current.auth.s3_has_role("ADMIN"): + # Modify Add Button + add_btn = A(T("Add CAP Feed"), + _class = "action-btn", + _href = URL(args = ["create"], + vars = {"type": "cap"}, + ), + ) + output["showadd_btn"] = add_btn + + return output + s3.postp = custom_postp + + return attr + + settings.customise_msg_rss_channel_controller = customise_msg_rss_channel_controller + + # ------------------------------------------------------------------------- + def customise_msg_twitter_channel_resource(r, tablename): + + s3db = current.s3db + def onaccept(form): + # Normal onaccept + s3db.msg_channel_onaccept(form) + _id = form.vars.id + db = current.db + table = db.msg_twitter_channel + channel_id = db(table.id == _id).select(table.channel_id, + limitby=(0, 1)).first().channel_id + # Link to Parser + table = s3db.msg_parser + _id = table.insert(channel_id=channel_id, function_name="parse_tweet", enabled=True) + s3db.msg_parser_enable(_id) + + run_async = current.s3task.run_async + # Poll + run_async("msg_poll", args=["msg_twitter_channel", channel_id]) + + # Parse + run_async("msg_parse", args=[channel_id, "parse_tweet"]) + + s3db.configure(tablename, + create_onaccept = onaccept, + ) + + settings.customise_msg_twitter_channel_resource = customise_msg_twitter_channel_resource + + # ------------------------------------------------------------------------- + def customise_org_organisation_resource(r, tablename): + + s3 = current.response.s3 + + crud_strings_branch = Storage( + label_create = T("Add Branch"), + title_display = T("Branch Details"), + title_list = T("Branches"), + title_update = T("Edit Branch"), + title_upload = T("Import Branches"), + label_list_button = T("List Branches"), + label_delete_button = T("Delete Branch"), + msg_record_created = T("Branch added"), + msg_record_modified = T("Branch updated"), + msg_record_deleted = T("Branch deleted"), + msg_list_empty = T("No Branches currently registered")) + + if r.component_name == "branch": + # Make sure branch uses same form as organisation because we need CAP OID + r.component.actuate = "replace" + s3.crud_strings[tablename] = crud_strings_branch + + if r.method == "hierarchy": + s3.crud_strings[tablename] = crud_strings_branch + + from s3 import S3SQLCustomForm, S3SQLInlineComponent, S3SQLInlineLink + crud_form = S3SQLCustomForm("name", + "acronym", + S3SQLInlineLink("organisation_type", + field = "organisation_type_id", + label = T("Type"), + multiple = False, + #widget = "hierarchy", + ), + S3SQLInlineComponent( + "tag", + label = T("CAP OID"), + multiple = False, + fields = [("", "value")], + filterby = {"field": "tag", + "options": "cap_oid", + }, + ), + "website", + "comments", + ) + + current.s3db.configure("org_organisation", + crud_form = crud_form, + ) + + settings.customise_org_organisation_resource = customise_org_organisation_resource + + # ------------------------------------------------------------------------- + def customise_pr_person_resource(r, tablename): + + # On-delete option + current.s3db.pr_person_id.attr.ondelete = "SET NULL" + + settings.customise_pr_person_resource = customise_pr_person_resource + + # ------------------------------------------------------------------------- + def customise_pr_contact_controller(**attr): + + s3 = current.response.s3 + + # Custom prep + standard_prep = s3.prep + def custom_prep(r): + # Call standard prep + if callable(standard_prep): + result = standard_prep(r) + else: + result = True + + table = r.table + table.priority.writable = False + table.priority.readable = False + table.comments.writable = False + table.comments.readable = False + + return result + s3.prep = custom_prep + + return attr + + settings.customise_pr_contact_controller = customise_pr_contact_controller + + # ------------------------------------------------------------------------- + def customise_cap_alert_resource(r, tablename): + + T = current.T + db = current.db + s3db = current.s3db + def onapprove(record): + # Normal onapprove + s3db.cap_alert_onapprove(record) + + run_async = current.s3task.run_async + + # Sync FTP Repository + run_async("cap_ftp_sync") + + # @ToDo: Check for LEFT join when required + # this is ok for now since every Alert should have an Info & an Area + alert_id = int(record["id"]) + table = s3db.cap_alert + itable = s3db.cap_info + atable = s3db.cap_area + query = (table.id == alert_id) & \ + (table.deleted != True) & \ + (itable.alert_id == table.id) & \ + (itable.deleted != True) & \ + (atable.alert_id == table.id) & \ + (atable.deleted != True) + resource = s3db.resource("cap_alert", filter=query) + # Fields to extract + fields = resource.list_fields(key="notify_fields") + # Extract the data + data = resource.select(fields, + raw_data=True) + # Single row as we are filtering for particular alert_id + arow = data["rows"][0] + + # Create attachment + cap_document_id = _get_or_create_attachment(alert_id) + + if record["scope"] != "Private" and data["numrows"] > 0: + # Google Cloud Messaging + stable = s3db.pr_subscription + ctable = s3db.pr_contact + + query = (stable.pe_id == ctable.pe_id) & \ + (ctable.contact_method == "GCM") & \ + (ctable.value != None) & \ + (ctable.deleted != True) & \ + (stable.deleted != True) & \ + (stable.method.like("%GCM%")) + rows = db(query).select(ctable.value) + if len(rows): + registration_ids = [s3_str(row.value) for row in rows] + title = get_email_subject(arow, system=False) + run_async("msg_gcm", args=[title, + "%s/%s" % (s3_str(arow["cap_info.web"]), "profile"), + s3_str(get_formatted_value(arow["cap_info.headline"], + system=False)), + json.dumps(registration_ids), + ]) + # Twitter Post + if settings.get_cap_post_to_twitter(): + try: + import tweepy + except ImportError: + current.log.debug("tweepy module needed for sending tweets") + else: + url = "%s/%s" % (arow["cap_info.web"], "profile") + try: + from pyshorteners import Shortener + except ImportError: + pass + else: + try: + url = Shortener('Tinyurl', timeout=3).short(url) + except: + pass + twitter_text = \ +("""%(status)s Alert: %(headline)s +%(sender)s: %(sender_name)s +%(website)s: %(Website)s""") % {"status": s3_str(T(arow["cap_alert.status"])), + "headline": s3_str(get_formatted_value(arow["cap_info.headline"], + system=False)), + "sender": s3_str(T("Sender")), + "sender_name": s3_str(get_formatted_value(arow["cap_info.sender_name"], + system=False)), + "website": s3_str(T("Website")), + "Website": s3_str(url), + } + try: + # @ToDo: Handle the multi-message nicely? + # @ToDo: Send resource url with tweet + current.msg.send_tweet(text=s3_str(twitter_text), + alert_id=alert_id, + ) + except tweepy.error.TweepError as e: + current.log.debug("Sending tweets failed: %s" % e) + + # Facebook Post + if settings.get_cap_post_to_facebook(): + # @ToDo: post resources too? + content = get_facebook_content(arow) + try: + current.msg.post_to_facebook(text=content, + alert_id=alert_id, + ) + except Exception as e: + current.log.debug("Posting Alert to Facebook failed: %s" % e) + + addresses = record["addresses"] + if len(addresses): + # First Responders + gtable = s3db.pr_group + mtable = s3db.pr_group_membership + ptable = s3db.pr_person + send_by_pe_id = current.msg.send_by_pe_id + get_user_id = current.auth.s3_get_user_id + query_ = (gtable.id == mtable.group_id) & \ + (mtable.person_id == ptable.id) & \ + (gtable.deleted != True) & \ + (mtable.deleted != True) & \ + (ptable.deleted != True) + count = len(addresses) + if count == 1: + query = query_ & (gtable.id == addresses[0]) + else: + query = query_ & (gtable.id.belongs(addresses)) + rows = db(query).select(ptable.pe_id) + subject = get_email_subject(arow, system=False) + if settings.get_cap_use_ack(): + for row in rows: + ack_id = create_ack(alert_id, get_user_id(pe_id=row.pe_id)) + email_content = "%s%s%s" % ("", + XML(get_html_email_content(arow, + ack_id=ack_id, + system=False)), + "") + sms_content = get_sms_content(arow, ack_id=ack_id, system=False) + send_by_pe_id(row.pe_id, + subject, + email_content, + document_ids=cap_document_id, + alert_id=alert_id, + ) + try: + send_by_pe_id(row.pe_id, + subject, + sms_content, + contact_method="SMS", + alert_id=alert_id, + ) + except ValueError: + current.log.error("No SMS Handler defined!") + else: + html_content = get_html_email_content(arow, system=False) + email_content = "%s%s%s" % ("", + XML(html_content), + "") + sms_content = get_sms_content(arow, system=False) + for row in rows: + send_by_pe_id(row.pe_id, + subject, + email_content, + document_ids = cap_document_id, + alert_id = alert_id, + ) + try: + send_by_pe_id(row.pe_id, + subject, + sms_content, + contact_method = "SMS", + alert_id = alert_id, + ) + except ValueError: + current.log.error("No SMS Handler defined!") + + s3db.configure(tablename, + onapprove = onapprove, + ) + + settings.customise_cap_alert_resource = customise_cap_alert_resource + + # ------------------------------------------------------------------------- + def customise_cap_alert_controller(**attr): + + s3 = current.response.s3 + auth = current.auth + if not auth.user: + # For notifications for group + r = current.request + if not r.function == "public": + if r.get_vars.format == "msg": + # This is called by notification + # The request from web looks like r.extension + s3.filter = (FS("scope") != "Private") + else: + auth.permission.fail() + + # Custom prep + standard_prep = s3.prep + def custom_prep(r): + # Call standard prep + if callable(standard_prep): + result = standard_prep(r) + else: + result = True + + if r.representation == "msg": + # Notification + table = r.table + table.scope.represent = None + table.status.represent = None + table.msg_type.represent = None + + itable = current.s3db.cap_info + itable.severity.represent = None + itable.urgency.represent = None + itable.certainty.represent = None + + return result + s3.prep = custom_prep + + return attr + + settings.customise_cap_alert_controller = customise_cap_alert_controller + + # ------------------------------------------------------------------------- + def customise_sync_repository_controller(**attr): + + s3 = current.response.s3 + + # Custom prep + standard_prep = s3.prep + def custom_prep(r): + # Call standard prep + if callable(standard_prep): + result = standard_prep(r) + else: + result = True + + if r.representation == "popup": + table = r.table + table.apitype.default = "ftp" + table.apitype.readable = table.apitype.writable = False + table.synchronise_uuids.readable = \ + table.synchronise_uuids.writable = False + table.uuid.readable = table.uuid.writable = False + + return result + s3.prep = custom_prep + + return attr + + settings.customise_sync_repository_controller = customise_sync_repository_controller + + # ------------------------------------------------------------------------- + def customise_pr_subscription_controller(**attr): + + from s3 import s3_action_buttons + s3 = current.response.s3 + s3db = current.s3db + auth = current.auth + stable = s3db.pr_subscription + has_role = auth.s3_has_role + + list_fields = [(T("Filters"), "filter_id"), + (T("Methods"), "method"), + ] + manage_recipient = current.request.get_vars["option"] == "manage_recipient" + role_check = has_role("ADMIN") + + if manage_recipient and role_check: + # Admin based subscription + s3.filter = (stable.deleted != True) & \ + (stable.owned_by_group != None) + list_fields.insert(0, (T("People/Groups"), "pe_id")) + s3.crud_strings["pr_subscription"].title_list = T("Admin Controlled Subscriptions") + else: + # Self Subscription + s3.filter = (stable.deleted != True) & \ + (stable.owned_by_group == None) & \ + (stable.owned_by_user == auth.user.id) + s3.crud_strings["pr_subscription"].title_list = T("Your Subscriptions") + + # Custom prep + standard_prep = s3.prep + def custom_prep(r): + from s3 import S3Represent + table = r.table + # Call standard prep + if callable(standard_prep): + result = standard_prep(r) + else: + result = True + MSG_CONTACT_OPTS = {"EMAIL": T("EMAIL"), + "SMS" : T("SMS"), + "FTP" : T("FTP"), + } + table.method.represent = S3Represent(options = MSG_CONTACT_OPTS, + multiple = True, + ), + if r.representation == "html": + table.filter_id.represent = S3Represent(\ + options=pr_subscription_filter_row_options()) + s3db.configure("pr_subscription", + list_fields = list_fields, + list_orderby = "pe_id desc", + orderby = "pr_subscription.pe_id desc", + ) + + return result + s3.prep = custom_prep + + # Custom postp + standard_postp = s3.postp + def custom_postp(r, output): + # Call standard postp + if callable(standard_postp): + output = standard_postp(r, output) + + if r.interactive and isinstance(output, dict): + # Modify Open Button + if manage_recipient and role_check: + # Admin based subscription + s3_action_buttons(r, + update_url = URL(c="default", f="index", + args = ["subscriptions"], + vars = {"option": "manage_recipient", + "subscription_id": "[id]", + }, + ), + delete_url = URL(c="pr", f="subscription", + args = ["[id]", "delete"], + vars = {"option": "manage_recipient"}, + ) + ) + else: + # self subscription + url = URL(c="default", f="index", + args = ["subscriptions"], + vars = {"subscription_id": "[id]"}, + ) + s3_action_buttons(r, update_url=url, read_url=url) + + if "form" in output: + # Modify Add Button + if manage_recipient and role_check: + # Admin based subscription + add_btn = A(T("Add Recipient to List"), + _class="action-btn", + _href=URL(c="default", f="index", + args=["subscriptions"], + vars={"option": "manage_recipient"} + ) + ) + else: + # self subscription + add_btn = A(T("Create Subscription"), + _class="action-btn", + _href=URL(c="default", f="index", args=["subscriptions"]) + ) + output["showadd_btn"] = add_btn + + return output + s3.postp = custom_postp + + return attr + + settings.customise_pr_subscription_controller = customise_pr_subscription_controller + + # ----------------------------------------------------------------------------- + def custom_msg_render(resource, data, meta_data, format=None): + """ + Custom Method to pre-render the contents for the message template + + Args: + resource: the S3Resource + data: the data returned from S3Resource.select + meta_data: the meta data for the notification + format: the contents format ("text" or "html") + """ + + notify_on = meta_data["notify_on"] + last_check_time = meta_data["last_check_time"] + rows = data["rows"] + output = {} + upd = [] # upd as the created alerts might be approved after some time, check is also done + + db = current.db + atable = current.s3db.cap_alert + if format == "text": + # For SMS + append_record = upd.append + for row in rows: + row_ = db(atable.id == row["cap_alert.id"]).select(atable.approved_on, + limitby=(0, 1)).first() + if row_ and row_.approved_on is not None: + if s3_utc(row_.approved_on) >= last_check_time: + sms_content = get_sms_content(row) + append_record(sms_content) + + if "upd" in notify_on and len(upd): + output["upd"] = len(upd) + output["upd_records"] = upd + else: + output["upd"] = None + else: + # HTML emails + elements = [] + append = elements.append + append_record = upd.append + + for row in rows: + row_ = db(atable.id == row["cap_alert.id"]).select(atable.approved_on, + limitby=(0, 1)).first() + if row_ and row_.approved_on is not None: + if s3_utc(row_.approved_on) >= last_check_time: + content = get_html_email_content(row) + container = DIV(DIV(content)) + append(container) + append(BR()) + append_record(container) + if "upd" in notify_on and len(upd): + output["upd"] = len(upd) + output["upd_records"] = DIV(*elements) + else: + output["upd"] = None + + output.update(meta_data) + return output + + settings.msg.notify_renderer = custom_msg_render + + # ----------------------------------------------------------------------------- + def custom_msg_notify_subject(resource, data, meta_data): + """ + Custom Method to subject for the email + + Args: + resource: the S3Resource + data: the data returned from S3Resource.select + meta_data: the meta data for the notification + """ + + rows = data["rows"] + subject = "%s %s" % (settings.get_system_name_short(), + T("Alert Notification")) + if len(rows) == 1: + # Since if there are more than one row, the single email has content + # for all rows + atable = current.s3db.cap_alert + row_ = current.db(atable.id == rows[0]["cap_alert.id"]).select(atable.approved_on, + limitby=(0, 1) + ).first() + if row_ and row_.approved_on is not None: + if s3_utc(row_.approved_on) >= meta_data["last_check_time"]: + subject = get_email_subject(rows[0]) + + return subject + + settings.msg.notify_subject = custom_msg_notify_subject + + # ----------------------------------------------------------------------------- + def custom_msg_notify_attachment(resource, data, meta_data): + """ + Custom Method to get the document_ids to be sent as attachment + + Args: + resource: the S3Resource + data: the data returned from S3Resource.select + meta_data: the meta data for the notification + """ + + rows = data["rows"] + document_ids = [] + dappend = document_ids.append + for row in rows: + alert_id = row["cap_alert.id"] + document_id = _get_or_create_attachment(alert_id) + dappend(document_id) + + return document_ids + + settings.msg.notify_attachment = custom_msg_notify_attachment + + # ----------------------------------------------------------------------------- + def custom_msg_notify_send_data(resource, data, meta_data): + """ + Custom Method to send data containing alert_id to the s3msg.send_by_pe_id + + Args: + resource: the S3Resource + data: the data returned from S3Resource.select + meta_data: the meta data for the notification + """ + + rows = data.rows + data = {} + if len(rows) == 1: + row = rows[0] + if "cap_alert.id" in row: + try: + alert_id = int(row["cap_alert.id"]) + data["alert_id"] = alert_id + except ValueError: + pass + + return data + + settings.msg.notify_send_data = custom_msg_notify_send_data + + # ----------------------------------------------------------------------------- + def msg_send_postprocess(message_id, **data): + """ + Custom function that links alert_id in cap module to message_id in + message module + """ + + alert_id = data.get("alert_id", None) + if alert_id and message_id: + current.s3db.cap_alert_message.insert(alert_id = alert_id, + message_id = message_id) + + settings.msg.send_postprocess = msg_send_postprocess + + # ------------------------------------------------------------------------- + # Comment/uncomment modules here to disable/enable them + # @ToDo: Have the system automatically enable migrate if a module is enabled + # Modules menu is defined in modules/eden/menu.py + settings.modules = OrderedDict([ + # Core modules which shouldn't be disabled + ("default", Storage( + name_nice = T("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 = T("Administration"), + #description = "Site Administration", + restricted = True, + 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 = T("Administration"), + #description = "Site Administration", + restricted = True, + module_type = None # No Menu + )), + ("errors", Storage( + name_nice = T("Ticket Viewer"), + #description = "Needed for Breadcrumbs", + restricted = False, + module_type = None # No Menu + )), + ("sync", Storage( + name_nice = T("Synchronization"), + #description = "Synchronization", + restricted = True, + 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 + )), + ("translate", Storage( + name_nice = T("Translation Functionality"), + #description = "Selective translation of strings based on module.", + module_type = None, + )), + ("gis", Storage( + name_nice = T("Mapping"), + #description = "Situation Awareness & Geospatial Analysis", + restricted = True, + module_type = 6, # 6th item in the menu + )), + ("pr", Storage( + name_nice = T("Person Registry"), + #description = "Central point to record details on People", + restricted = True, + 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 = T("Organizations"), + #description = 'Lists "who is doing what & where". Allows relief agencies to coordinate their activities', + restricted = True, + module_type = 10 + )), + # All modules below here should be possible to disable safely + #("hrm", Storage( + # name_nice = T("Staff"), + # #description = "Human Resources Management", + # restricted = True, + # module_type = 2, + #)), + ("cap", Storage( + name_nice = T("Alerting"), + #description = "Create & broadcast CAP alerts", + restricted = True, + module_type = 1, + )), + ("cms", Storage( + name_nice = T("Content Management"), + #description = "Content Management System", + restricted = True, + module_type = 10, + )), + ("doc", Storage( + name_nice = T("Documents"), + #description = "A library of digital resources, such as photos, documents and reports", + restricted = True, + module_type = 10, + )), + ("msg", Storage( + name_nice = T("Messaging"), + #description = "Sends & Receives Alerts via Email & SMS", + restricted = True, + # 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, + )), + ("event", Storage( + name_nice = T("Events"), + #description = "Activate Events (e.g. from Scenario templates) for allocation of appropriate Resources (Human, Assets & Facilities).", + restricted = True, + module_type = 10, + )), + ]) + + # ------------------------------------------------------------------------- + # Functions which are local to this Template + # ------------------------------------------------------------------------- + def pr_subscription_filter_row_options(): + """ + Build the options for the pr_subscription filter datatable from query + @ToDo complete this for locations + """ + + db = current.db + s3db = current.s3db + auth = current.auth + has_role = auth.s3_has_role + stable = s3db.pr_subscription + ftable = s3db.pr_filter + + if current.request.get_vars["option"] == "manage_recipient" and \ + (has_role("ALERT_EDITOR") or has_role("ALERT_APPROVER")): + # Admin based subscription + query = (stable.deleted != True) & \ + (stable.owned_by_group != None) + else: + # Self Subscription + query = (stable.deleted != True) & \ + (stable.owned_by_group == None) & \ + (stable.owned_by_user == auth.user.id) + + left = ftable.on(ftable.id == stable.filter_id) + rows = db(query).select(stable.filter_id, + ftable.query, + left=left) + + filter_options = {} + + if len(rows) > 0: + T = current.T + etable = s3db.event_event_type + ptable = s3db.cap_warning_priority + from s3 import IS_ISO639_2_LANGUAGE_CODE + languages_dict = dict(IS_ISO639_2_LANGUAGE_CODE.language_codes()) + for row in rows: + event_type = None + priorities_id = [] + languages = [] + + filters = json.loads(row.pr_filter.query) + filters = [filter for filter in filters if filter[1] is not None] + if len(filters) > 0: + for filter in filters: + # Get the prefix + prefix = s3_str(filter[0]).strip("[]") + # Get the value for prefix + values = filter[1].split(",") + if prefix == "event_type_id__belongs": + event_type_id = s3_str(values[0]) + row_ = db(etable.id == event_type_id).select(\ + etable.name, + limitby=(0, 1)).first() + event_type = row_.name + elif prefix == "info.priority__belongs": + priorities_id = [int(s3_str(value)) for value in values] + rows_ = db(ptable.id.belongs(priorities_id)).select(ptable.name) + priorities = [row_.name for row_ in rows_] + elif prefix == "info.language__belongs": + languages = [s3_str(languages_dict[value]) for value in values] + if event_type is not None: + display_text = "%s: %s" % (T("Event Type"), event_type) + else: + display_text = "%s: %s" % (T("Event Type"), T("No filter")) + if len(priorities_id) > 0: + display_text = "%s
%s: %s" % (display_text, T("Priorities"), ", ".join(priorities)) + else: + display_text = "%s
%s: %s" % (display_text, T("Priorities"), T("No filter")) + if len(languages) > 0: + display_text = "%s
%s: %s" % (display_text, T("Languages"), ", ".join(languages)) + else: + display_text = "%s
%s: %s" % (display_text, T("Languages"), T("No filter")) + filter_options[row["pr_subscription.filter_id"]] = display_text + else: + filter_options[row["pr_subscription.filter_id"]] = T("No filters") + + return filter_options + + # ------------------------------------------------------------------------- + def get_html_email_content(row, ack_id=None, system=True): + """ + Prepare the content for HTML email + + Args: + row: the row from which the email will be constructed + ack_id: cap_alert_ack.id for including the acknowledgement link + system: is this system notification email or email for first responders + """ + + itable = current.s3db.cap_info + event_type_id = row["cap_info.event_type_id"] + priority_id = row["cap_info.priority"] + response_type = row["_row"]["cap_info.response_type"] if system else row["cap_info.response_type"] + instruction = row["_row"]["cap_info.instruction"] if system else row["cap_info.instruction"] + description = row["_row"]["cap_info.description"] if system else row["cap_info.description"] + status = row["cap_alert.status"] + msg_type = row["cap_alert.msg_type"] + url = "%s/%s" % (row["cap_info.web"], "profile") + try: + from pyshorteners import Shortener + except ImportError: + pass + else: + try: + url = Shortener('Tinyurl', timeout=3).short(url) + except: + pass + + if event_type_id and event_type_id != current.messages["NONE"]: + if not isinstance(event_type_id, lazyT) and \ + not isinstance(event_type_id, DIV): + event_type = itable.event_type_id.represent(event_type_id) + else: + event_type = event_type_id + else: + event_type = T("None") + + if priority_id and priority_id != current.messages["NONE"]: + if not isinstance(priority_id, lazyT) and \ + not isinstance(priority_id, DIV): + priority = itable.priority.represent(priority_id) + else: + priority = priority_id + else: + priority = T("Alert") + + email_content = TAG[""](HR(), BR(), + B(s3_str("%s %s %s" % (T(status.upper()), + T(status.upper()), + T(status.upper())))) + if status != "Actual" else "", + BR() if status != "Actual" else "", + BR() if status != "Actual" else "", + A(T("VIEW ALERT ON THE WEB"), _href = s3_str(url)), + BR(), BR(), + B(s3_str("%s %s %s %s" % (T(row["cap_alert.scope"]), + T(status), + T("Alert") if msg_type != "Alert" else "", + s3_str(msg_type) + ))), + H2(T(s3_str(get_formatted_value(row["cap_info.headline"], + system=system)))), + BR(), + XML("%(label)s: %(identifier)s" % + {"label": B(T("ID")), + "identifier": s3_str(row["cap_alert.identifier"]) + }), + BR(), BR(), + T("""%(priority)s message %(message_type)s in effect for %(area_description)s""") % \ + {"priority": s3_str(priority), + "message_type": s3_str(msg_type), + "area_description": s3_str(get_formatted_value(row["cap_area.name"], + system=system)), + }, + BR(), BR(), + T("This %(severity)s %(event_type)s is %(urgency)s and is %(certainty)s") %\ + {"severity": s3_str(row["cap_info.severity"]), + "event_type": s3_str(event_type), + "urgency": s3_str(row["cap_info.urgency"]), + "certainty": s3_str(row["cap_info.certainty"]), + }, + BR(), BR(), + T("""Message %(identifier)s: %(event_type)s (%(category)s) issued by %(sender_name)s sent at %(date)s from %(source)s""") % \ + {"identifier": s3_str(row["cap_alert.identifier"]), + "event_type": s3_str(event_type), + "category": s3_str(get_formatted_value(row["cap_info.category"], + represent = itable.category.represent, + system=system)), + "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"], + system=system)), + "date": s3_str(get_formatted_value(row["cap_alert.sent"], + represent = current.s3db.cap_alert.sent.represent, + system=system)), + "source": s3_str(row["cap_alert.source"]), + }, + BR(), + BR() if description else "", + XML("%(label)s: %(alert_description)s" % + {"label": B(T("Alert Description")), + "alert_description": s3_str(get_formatted_value(description, + system=False, + ul=True)), + }) + if description else "", + BR() if not isinstance(description, list) else "", + BR() if response_type else "", + XML(T("%(label)s: %(response_type)s") % + {"label": B(T("Expected Response")), + "response_type": s3_str(get_formatted_value(response_type, + represent = itable.response_type.represent, + system=False, + ul=True)), + }) + if response_type else "", + BR() if not isinstance(response_type, list) else "", + BR() if instruction else "", + XML(T("%(label)s: %(instruction)s") % + {"label": B(T("Instructions")), + "instruction": s3_str(get_formatted_value(instruction, + system=False, + ul=True)), + }) + if instruction else "", + BR() if not isinstance(instruction, list) else "", + BR(), + T("Alert is effective from %(effective)s and expires on %(expires)s") % \ + {"effective": s3_str(get_formatted_value(row["cap_info.effective"], + represent = itable.effective.represent, + system=system)), + "expires": s3_str(get_formatted_value(row["cap_info.expires"], + represent = itable.expires.represent, + system=system)), + }, + BR(), BR(), + T("For more details visit %(url)s or contact %(contact)s") % \ + {"url": s3_str(url), + "contact": s3_str(get_formatted_value(row["cap_info.contact"], + system=system)), + }, + BR(), BR(), + T("To acknowledge the alert, use the following link: %(ack_link)s") % \ + {"ack_link": "%s%s" % (current.deployment_settings.get_base_public_url(), + URL(c="cap", f="alert_ack", args=[ack_id, "update"])), + } if ack_id else "", + BR() if ack_id else "", + BR() if ack_id else "", + B(s3_str("%s %s %s" % (T(status.upper()), + T(status.upper()), + T(status.upper())))) + if status != "Actual" else "", + ) + + return email_content + + # ------------------------------------------------------------------------- + def get_email_subject(row, system=True): + """ + Prepare the subject for Email + """ + + itable = current.s3db.cap_info + event_type_id = row["cap_info.event_type_id"] + msg_type = T(row["cap_alert.msg_type"]) + + if event_type_id and event_type_id != current.messages["NONE"]: + if not isinstance(event_type_id, lazyT) and \ + not isinstance(event_type_id, DIV): + event_type = itable.event_type_id.represent(event_type_id) + else: + event_type = event_type_id + else: + event_type = T("None") + + subject = "[%s] %s %s" % (get_formatted_value(row["cap_info.sender_name"], + system=system), + event_type, + msg_type) + # RFC 2822 + return s3_str(s3_truncate(subject, length=78)) + + # ------------------------------------------------------------------------- + def get_sms_content(row, ack_id=None, system=True): + """ + Prepare the content for SMS + + Args: + row: the row from which the sms will be constructed + ack_id: cap_alert_ack.id for including the acknowledgement link + system: is this system notification email or email for first responders + """ + + itable = current.s3db.cap_info + event_type_id = row["cap_info.event_type_id"] + priority_id = row["cap_info.priority"] + url = "%s/%s" % (row["cap_info.web"], "profile") + try: + from pyshorteners import Shortener + except ImportError: + pass + else: + try: + url = Shortener('Tinyurl', timeout=3).short(url) + except: + pass + + if not isinstance(event_type_id, lazyT) and \ + not isinstance(event_type_id, DIV): + event_type = itable.event_type_id.represent(event_type_id) + else: + event_type = event_type_id + + if priority_id and priority_id != current.messages["NONE"]: + if not isinstance(priority_id, lazyT) and \ + not isinstance(priority_id, DIV): + priority = itable.priority.represent(priority_id) + else: + priority = priority_id + else: + priority = T("Unknown") + + if ack_id: + sms_body = \ +T("""%(status)s %(message_type)s for %(area_description)s with %(priority)s priority %(event_type)s issued by %(sender_name)s at %(date)s (ID:%(identifier)s) \nTo acknowledge the alert, click: %(ack_link)s \n\n""") % \ + {"status": s3_str(row["cap_alert.status"]), + "message_type": s3_str(row["cap_alert.msg_type"]), + "area_description": s3_str(get_formatted_value(row["cap_area.name"], + system=system)), + "priority": s3_str(priority), + "event_type": s3_str(event_type), + "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"], + system=system)), + "date": s3_str(row["cap_alert.sent"]), + "identifier": s3_str(row["cap_alert.identifier"]), + "ack_link": "%s%s" % (current.deployment_settings.get_base_public_url(), + URL(c="cap", f="alert_ack", args=[ack_id, "update"])), + } + else: + sms_body = \ +T("""%(status)s %(message_type)s for %(area_description)s with %(priority)s priority %(event_type)s issued by %(sender_name)s at %(date)s (ID:%(identifier)s). \nView Alert in web at %(profile)s \n\n""") % \ + {"status": s3_str(row["cap_alert.status"]), + "message_type": s3_str(row["cap_alert.msg_type"]), + "area_description": s3_str(get_formatted_value(row["cap_area.name"], + system=system)), + "priority": s3_str(priority), + "event_type": s3_str(event_type), + "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"], + system=system)), + "date": s3_str(row["cap_alert.sent"]), + "identifier": s3_str(row["cap_alert.identifier"]), + "profile": s3_str(url), + } + + return s3_str(sms_body) + + # ------------------------------------------------------------------------- + def get_facebook_content(row, system=False): + """ + prepare the content for facebook post + """ + + itable = current.s3db.cap_info + event_type_id = row["cap_info.event_type_id"] + priority_id = row["cap_info.priority"] + response_type = row["cap_info.response_type"] + instruction = row["cap_info.instruction"] + description = row["cap_info.description"] + url = "%s/%s" % (row["cap_info.web"], "profile") + try: + from pyshorteners import Shortener + try: + url = Shortener('Tinyurl', timeout=3).short(url) + except: + pass + except ImportError: + pass + + if event_type_id and event_type_id != current.messages["NONE"]: + if not isinstance(event_type_id, lazyT): + event_type = itable.event_type_id.represent(event_type_id) + else: + event_type = event_type_id + else: + event_type = T("None") + + if priority_id and priority_id != current.messages["NONE"]: + if not isinstance(priority_id, lazyT): + priority = itable.priority.represent(priority_id) + else: + priority = priority_id + else: + priority = T("Alert") + + facebook_content = [ + T("%(scope)s %(status)s Alert") % \ + {"scope": s3_str(row["cap_alert.scope"]), + "status": s3_str(row["cap_alert.status"]), + }, + T((s3_str(get_formatted_value(row["cap_info.headline"], + system=system)))), + T("ID: %(identifier)s") % {"identifier": s3_str(row["cap_alert.identifier"])}, + T("""%(priority)s message %(message_type)s in effect for %(area_description)s""") % \ + {"priority": s3_str(priority), + "message_type": s3_str(row["cap_alert.msg_type"]), + "area_description": s3_str(get_formatted_value(row["cap_area.name"], + system=system)), + }, + T("This %(severity)s %(event_type)s is %(urgency)s and is %(certainty)s") % \ + {"severity": s3_str(row["cap_info.severity"]), + "event_type": s3_str(event_type), + "urgency": s3_str(row["cap_info.urgency"]), + "certainty": s3_str(row["cap_info.certainty"]), + }, + T("""Message %(identifier)s: %(event_type)s (%(category)s) issued by %(sender_name)s sent at %(date)s from %(source)s""") % \ + {"identifier": s3_str(row["cap_alert.identifier"]), + "event_type": s3_str(event_type), + "category": s3_str(get_formatted_value(row["cap_info.category"], + represent = itable.category.represent, + system=system)), + "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"], + system=system)), + "date": s3_str(get_formatted_value(row["cap_alert.sent"], + represent = current.s3db.cap_alert.sent.represent, + system=system)), + "source": s3_str(row["cap_alert.source"]), + }, + T("Alert Description: %(alert_description)s") % \ + {"alert_description": s3_str(get_formatted_value(description, + system=system)), + } if description else "", + T("Expected Response: %(response_type)s") % \ + {"response_type": s3_str(get_formatted_value(response_type, + represent = itable.response_type.represent, + system=system)), + } if response_type else "", + T("Instruction: %(instruction)s") % \ + {"instruction": s3_str(get_formatted_value(instruction, system=system))} + if instruction else "", + T("Alert is effective from %(effective)s and expires on %(expires)s") % \ + {"effective": s3_str(get_formatted_value(row["cap_info.effective"], + represent = itable.effective.represent, + system=system)), + "expires": s3_str(get_formatted_value(row["cap_info.expires"], + represent = itable.expires.represent, + system=system)), + }, + T("For more details visit %(url)s or contact %(contact)s") % \ + {"url": s3_str(url), + "contact": s3_str(get_formatted_value(row["cap_info.contact"], system=system)), + } + if row["cap_info.contact"] else + T("For more details visit %(url)s") % \ + {"url": s3_str(url)} + ] + + return "\n\n".join(s3_str(item) for item in facebook_content if item!="") + + # ------------------------------------------------------------------------- + def create_ack(alert_id, user_id): + """ + Create a specific acknowledgement + + Args: + alert_id: The particular alert ID for acknowledging + user_id: The user ID who owns the record + + TODO: + Use location where the alert is targeted for + """ + + ack_data = {"alert_id": alert_id, + "owned_by_user": int(user_id), + } + ack_table = current.s3db.cap_alert_ack + ack_id = ack_table.insert(**ack_data) + current.auth.s3_set_record_owner(ack_table, ack_id) + # Uncomment this when there is onaccept hook + #s3db.onaccept(ack_table, {"id": ack_id}) + + return ack_id + + # ------------------------------------------------------------------------- + def get_formatted_value(value, + represent = None, + system = True, + ul = False, + ): + """ + For non-system notification returns the formatted represented value + """ + + if not value: + return None + else: + if system: + # For system notification value is already properly formatted for representation + return value + else: + if isinstance(value, list): + nvalue = [] + for value_ in value: + if value_: + if represent: + nvalue.append(represent(value_)) + else: + nvalue.append(value_) + if len(nvalue): + if ul: + nvalue = UL(nvalue) + else: + nvalue = ", ".join(nvalue) + else: + return None + else: + if represent: + nvalue = represent(value) + else: + nvalue = value + + return nvalue + + # ------------------------------------------------------------------------- + def _get_or_create_attachment(alert_id): + """ + Retrieve the CAP attachment for the alert_id if present + else creates CAP file as attachment to be sent with the + email + + Args: + alert_id: the cap_alert record ID + + Returns: + doc_id of the CAP file + """ + + s3db = current.s3db + rtable = s3db.cap_resource + dtable = s3db.doc_document + + # Check for existing CAP XML resource + query = (rtable.alert_id == alert_id) & \ + (rtable.mime_type == "cap") & \ + (rtable.deleted != True) & \ + (dtable.doc_id == rtable.doc_id) & \ + (dtable.deleted != True) + row = current.db(query).select(dtable.doc_id, + limitby = (0, 1), + ).first() + if row: + return row.doc_id + + # Create a CAP resource for the CAP XML file + record = {"alert_id": alert_id, + "resource_desc": T("CAP XML File"), + "mime_type": "cap" # Hard-coded to separate from attachment from user + } + resource_id = rtable.insert(**record) + + # Post-process the CAP resource + record["id"] = resource_id + s3db.update_super(rtable, record) + doc_id = record["doc_id"] + auth = current.auth + auth.s3_set_record_owner(rtable, resource_id) + auth.s3_make_session_owner(rtable, resource_id) + s3db.onaccept("cap_resource", record, method="create") + + # Generate the CAP XML + resource = s3db.resource("cap_alert", id=alert_id) + cap_xml = resource.export_xml( + stylesheet = os.path.join(current.request.folder, + "static", + "formats", + "cap", + "export.xsl", + ), + pretty_print = True, + ) + + stream = BytesIO(cap_xml) + filename = "%s_%s.xml" % ("cap_alert", s3_str(alert_id)) + + # Store the CAP XML as doc_document for the CAP resource + document = {"file": dtable.file.store(stream, filename), + "doc_id": doc_id, + } + document_id = dtable.insert(**document) + + return document_id + +# END ========================================================================= diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultItems.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultItems.csv new file mode 100644 index 0000000..e0f1938 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultItems.csv @@ -0,0 +1,11 @@ +"Catalog","Category Code","Category","Item Code","Item Name","Unit of Measure","Pack","Pack2 Quantity","Notes" +"Default","Agriculture","Agriculture","AgRice20","Rice Seed","sack20kg",,,"This should provide enough seed for 1 Hectare of land" +"Default","Food","Food","FdRice5","Rice","kg","sack",5,"This should feed 12 people for 1 day" +"Default","Food","Food","FdRice50","Rice","kg","sack",50,"This should feed 125 people for 1 day" +"Default","Food","Food","FdCookKit","Cooking Utensils","kit",,,"Cooking Utensils for a Household" +"Default","Health","Health","HlFAidKit","First Aid Kit","kit",,,"This should provide basic first aid (bandages, oral rehydration salts, etc) for 100 people to self-administer" +"Default","Health","Health","HlMedKit","Medical Kit","kit",,,"This should provide medical supplies (medicines, vaccines) for a professional clinic to provide assistance to a total community of 10,000 people." +"Default","Shelter","Shelter","ShTentKit","Shelter Kit","kit",,,"This kit is suitable to provide emergency repair to a damaged home. It contains a tarpaulin, zinc sheet, wooden poles, hammer & nails" +"Default","Shelter","Shelter","ShTent","Tent","piece",,,"This should house a family of up to 8 people" +"Default","WASH","WASH","WSHygKit","Hygiene Kit","kit",,,"Personal Hygiene supplies for 100 Households (5 persons/household): Each get 2x Buckets, 10x Soap, Cotton cloth" +"Default","WASH","WASH","WswPure","Water Purification Sachets","kit",,,"Designed to provide a 1st phase drinking water purification solution at the household level. Contains 600 sachets to provide sufficient drinking water (4l) for 100 people for 30 days." diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultSkillCompetency.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultSkillCompetency.csv new file mode 100644 index 0000000..989a79e --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultSkillCompetency.csv @@ -0,0 +1,4 @@ +Type,Name,Priority +Default,"Level 1",3 +Default,"Level 2",2 +Default,"Level 3",1 diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultSkillList.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultSkillList.csv new file mode 100644 index 0000000..c507e9d --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DefaultSkillList.csv @@ -0,0 +1,11 @@ +Type,Name,Comments +"Default","Disaster Psychology", +"Default","Fire Suppression", +"Default","Hazmat", +"Default","Incident Command", +"Default","Language - American Sign Language", +"Default","Language - French", +"Default","Language - Spanish", +"Default","Medical Treatment", +"Default","Security", +"Default","Scene Sizeup", diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DrivingSkillList.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DrivingSkillList.csv new file mode 100644 index 0000000..33dfba5 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DrivingSkillList.csv @@ -0,0 +1,14 @@ +Type,Name,Comments +Driving License,"Driving - Motorcycle" +Driving License,"Driving - Car" +Driving License,"Driving - Class A: Combination (tractor plus trailer) vehicle","26,000 lb (11,790 kg) or more. Includes split (coupled) buses." +Driving License,"Driving - Class B: Single (straight) vehicle","26,000 lb (11,790 kg) or more (includes most buses including articulated buses). Also includes combination vehicles for commercial use weighing less than 26,000 lb GVW." +Driving License,"Driving - Class C: Commercial vehicle","that doesn't fit classes A or B but is placarded for hazardous materials or is intended to carry more than 15 persons (excluding Georgia). May include heavy-duty non-commercial vehicles with trailers capable of carrying over 16,000 lb, and all vehicles that can carry over 16,000 lb but not more than 25,999 lb." +Driving License,"Driving - P: Passenger Transport","Required to drive a bus that carries 16 or more persons" +Driving License,"Driving - H: Hazardous Materials","Also requires a TSA criminal background check as well as a written exam. The driver must be a US Citizen or permanent lawful resident to obtain an H or X endorsement." +Driving License,"Driving - M: Metal coil" +Driving License,"Driving - N: Tank Vehicles","Required for carrying liquids in bulk" +Driving License,"Driving - T: Double/ Triple Trailers (Road trains)","Class A licenses only" +Driving License,"Driving - X: Hazardous Materials and Tank Combination"," Also requires a TSA criminal background check as well as a written exam. The driver must be a US Citizen or permanent lawful resident to obtain an H or X endorsement." +Driving License,"Driving - L: Air Brakes" +Driving License,"Driving - S: School Bus","In addition to a standard bus endorsement, additional TSA and CORI background checks are required." \ No newline at end of file diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DrivingSkillList_EU.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DrivingSkillList_EU.csv new file mode 100644 index 0000000..a8c8cef --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/DrivingSkillList_EU.csv @@ -0,0 +1,23 @@ +Type,Name,Comments +Driving License,"Driving - Motorcycle" +Driving License,"Driving - Car" +Driving License,"Driving - Class AM (14 years)","Two-wheel mopeds, Three-wheeled vehicles, light quadricycles. Maximum speed 45 km/h and engine capacity of less than or equal to 50 cm cubic" +Driving License,"Driving - Class A1 (16 years)","Motorcycles, Tricycles, Agricultural Machinery. Maximum power 11 kW (Motorcycles) e 15 kW (Tricycles)" +Driving License,"Driving - Class A2 (18 years)","Motorcycles. Power not exceeding 35 kW" +Driving License,"Driving - Class A (20 o 21 years)","Tricycles or Motorcycles. Power greater than 15 kW, top speed exceeding 45 km/h" +Driving License,"Driving - Class B1 (16 years)","Quadricycles. Mass less or equal to 400 kg (550 kg for vehicles used for the transport of goods, maximum engine power less than or equal to 15 kW" +Driving License,"Driving - Class B/B96 (18 years)","Vehicles or machines Agricole. Max mass authorized 3500 kg and designed for the transport of not more than eight persons in addition to the driver. It can be combined with a trailer." +Driving License,"Driving - Class BE (18 years)","Vehicles with a towing vehicle in category B and a trailer or semitrailer. Maximum mass of the trailer does not exceed 3500 kg." +Driving License,"Driving - Class C1 (18 years)","Vehicles designed for the transport of not more than eight persons in addition to the driver. Maximum authorized mass exceeding 3500 kg, but not exceeding 7500 kg; can be combined with a trailer having a maximum authorized mass does not exceed 750 kg." +Driving License,"Driving - Class C1E (18 years)","Vehicles with engine category C1 or B. Maximum authorized mass of the trailer is 750 Kg (C1) o 3500 Kg (B), overall should not exceed 12000 Kg" +Driving License,"Driving - Class C (21 years)","Vehicles for the transport of not more than eight persons in addition to the driver. It can be combined with a trailer having a maximum authorized mass does not exceed 750 kg" +Driving License,"Driving - Class CE (21 years)","Vehicles with engine type C and with a trailer. Maximum authorized mass 750 kg" +Driving License,"Driving - Class D1 (21 years)","Vehicles designed for the transport of not more than 16 people, in addition to the driver. Maximum length of 8 meters, can be combined with a trailer having a maximum authorized mass does not exceed 750 kg." +Driving License,"Driving - Class D1E (21 years)","Vehicles with engine type D1 and its trailer. Maximum authorized mass of the trailer is 750 Kg" +Driving License,"Driving - Class D (24 years)","Vehicles designed for the transport of more than eight persons in addition to the driver, can be coupled to a trailer. Maximum authorized mass of the trailer is 750 kg" +Driving License,"Driving - Class DE (24 years)","Vehicles with engine type D and a trailer. Maximum authorized mass of the trailer is 750 Kg" +Driving License,"Driving - Class KA (21 years)","Karts of total mass until ad 1,3 tons. Rental service with driver." +Driving License,"Driving - Class KB (21 years)","Karts of total mass over 1,3 tons and taxi. Rental service with driver." +Driving License,"Driving - Class CQC Person (21 years)","Vehicles of category D1, D e/o D+E in public service or line rental with driver" +Driving License,"Driving - Class CQC Goods (21 years)","Vehicles of category C1, C e/o C+E for professional transport" +Driving License,"Driving - Class CFP","ADR Class B / A o B+explosives / B+radioactive (Vehicles carrying dangerous goods)" diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/LanguageCompetency.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/LanguageCompetency.csv new file mode 100644 index 0000000..e7deabd --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/LanguageCompetency.csv @@ -0,0 +1,8 @@ +Type,Name,Priority +Languages,"No",1 +Languages,"Bad",2 +Languages,"Fair",3 +Languages,"Average",4 +Languages,"Good",5 +Languages,"Excellent",6 +Languages,"Fluent",7 diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/LanguageSkillList.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/LanguageSkillList.csv new file mode 100644 index 0000000..b3fcea4 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/LanguageSkillList.csv @@ -0,0 +1,11 @@ +Type,Name,Comments +Languages,"English - Spoken" +Languages,"English - Written" +Languages,"French - Spoken" +Languages,"French - Written" +Languages,"Spanish - Spoken" +Languages,"Spanish - Written" +Languages,"Arabic - Spoken" +Languages,"Arabic - Written" +Languages,"Chinese - Spoken" +Languages,"Chinese - Written" \ No newline at end of file diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/StandardItems.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/StandardItems.csv new file mode 100644 index 0000000..363bb9b --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/StandardItems.csv @@ -0,0 +1,279 @@ +"Catalog","Category Code","Category","Item Name","Unit of Measure","Pack","Pack Quantity","Pack2","Pack2 Quantity","Pack3","Pack3 Quantity","Comments" +"Standard","CLOTHING","Appliances, Clothing, and Furniture","Childrens Clothing","ea",,,,,,,"priority" +"Standard","CLOTHING","Appliances, Clothing, and Furniture","Furniture","ea",,,,,,, +"Standard","CLOTHING","Appliances, Clothing, and Furniture","Household appliance","ea",,,,,,, +"Standard","CLOTHING","Appliances, Clothing, and Furniture","Linens","ea",,,,,,,"priority" +"Standard","CLOTHING","Appliances, Clothing, and Furniture","Mens Clothing","ea",,,,,,,"priority" +"Standard","CLOTHING","Appliances, Clothing, and Furniture","Womens Clothing","ea",,,,,,,"priority" +"Standard","MATERIALS","Building Materials","Asphalt","cy",,,,,,, +"Standard","MATERIALS","Building Materials","Blinds","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Cabinet","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Carpeting","sy",,,,,,, +"Standard","MATERIALS","Building Materials","Concrete","cy",,,,,,, +"Standard","MATERIALS","Building Materials","Counter Top","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Door","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Drywall","sf",,,,,,, +"Standard","MATERIALS","Building Materials","Ducting","lf",,,,,,, +"Standard","MATERIALS","Building Materials","Electrical Materials","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Faucet","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Fencing","lf",,,,,,, +"Standard","MATERIALS","Building Materials","Grips & Handles","ea",,,,,,, +"Standard","MATERIALS","Building Materials","HVAC Tools","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Levels","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Loader","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Lumber","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Manholes","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Paint","gal",,,,,,, +"Standard","MATERIALS","Building Materials","PayLoader","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Pipe","lf",,,,,,, +"Standard","MATERIALS","Building Materials","Pipe Accessories","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Pipe Repair Kits","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Polymer/Setting Aid","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Roofing Materials","sq",,,,,,, +"Standard","MATERIALS","Building Materials","Sand","cy",,,,,,, +"Standard","MATERIALS","Building Materials","Sand Bags","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Screens","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Sink","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Steel","lbs",,,,,,, +"Standard","MATERIALS","Building Materials","Sulfur Dioxide","cy",,,,,,, +"Standard","MATERIALS","Building Materials","Tarps","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Tile","sf",,,,,,, +"Standard","MATERIALS","Building Materials","Toilet","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Tub","ea",,,,,,, +"Standard","MATERIALS","Building Materials","Windows","ea",,,,,,, +"Standard","$","Cash","Bank Cards","$",,,,,,, +"Standard","$","Cash","Check","$",,,,,,, +"Standard","$","Cash","Domestic Currency","$",,,,,,,"priority" +"Standard","$","Cash","Foreign currency","$ equivalent",,,,,,, +"Standard","$","Cash","Vouchers","$",,,,,,, +"Standard","EQUIP","Equipment & Tools","Aerator","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Air Compressor","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Air Conditioner/Heater","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Air Cooled Chiller Unit","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Air Curtain Burner","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Air Supply","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Aircraft","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Ambulance & Equipment","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","ATM","ea",,,,,,,"Cash Dispenser" +"Standard","EQUIP","Equipment & Tools","Auger","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Barge","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Barricades","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Beacon","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Boat","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Bolt Cutters","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Boots","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Box Blade","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Breaker","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Broom","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Bucket","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Cabling","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Cage","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Camera","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Camera Snakes","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Carbon","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Chain Saw","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Chipper","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Chlorination Equipment","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Chlorination/Dechlorination ","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Compactor","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Computer (Laptops, Monitors, Cables, PC's)","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Concrete Grinder","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Copier","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Crane","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Cutter","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Dehumidifier","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Derrick","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Dock Plates","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Dozer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Drywall Tools","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Dumpster","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Electrical panel","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Excavator","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Face Masks","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Fan","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Fax Machine","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Field Rake","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Fire Hydrant","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Fire Pump","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Fire Suppression","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Flashlight","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Flotation Device","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Fork Lift","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Furnace","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Garbage Can","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Generator","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Generator Cable","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Gloves","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","GPS / Transponder","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Grader","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Grader Blade","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Hardhat","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Heater","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Helicopter","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Helmet","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Hose","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Ice Machine","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Jackhammer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Jaws of Life","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Laboratory/Sampling Equipment","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Ladder","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Life","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Light Plant","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Light Tower","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Lighting","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Megaphone/Loudspeaker","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Message Board","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Metal Detector","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Mixer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Mop","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Mower","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Mulcher","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pallet","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pallet Grabber","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pallet Jack","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Phone","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Phone Line","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Plastic Liners","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pliers","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Plumbing Tools","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Poles","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Power Cord","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Power Distribution Box","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Power Line","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Power Supply","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","PPE","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pressure Washer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Printer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Projector","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pump","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pump Extender","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Pump System","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Radio","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Rope","lf",,,,,,, +"Standard","EQUIP","Equipment & Tools","Satellite","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Saw","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Scanner","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Scraper","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Screwdriver","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Sensor","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Shovel","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Shrinkwrap","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Sign","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Snow Thrower","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Solids Processing","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Sprayer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Strainers/T-Joints/Elbows","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Stretcher","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Striking Tools","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Striper","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Sweeper","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Table","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Tamper Plate","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Tank","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Tent","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Tire Balancer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Tire Changer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Topdresser","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Towels","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Tractor","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Transformer","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Traprake","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Trench Box","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Trencher","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Trowel","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Tub Grinder","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Utencils","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Vacuum","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Valves","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Vehicles","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Ventilator","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Vibrator","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Video Conferencing","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Video Transmitter","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Walkers","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Water Cooler","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Water Purification System","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Welder","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Well Equipment","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Wheelchair","ea",,,,,,, +"Standard","EQUIP","Equipment & Tools","Wrench","ea",,,,,,, +"Standard","FACILITY","Facilities","Agriculture Laboratory","ea",,,,,,, +"Standard","FACILITY","Facilities","Airport","ea",,,,,,, +"Standard","FACILITY","Facilities","Animal Feedlot","ea",,,,,,, +"Standard","FACILITY","Facilities","Animal Shelter","ea",,,,,,, +"Standard","FACILITY","Facilities","Barn","ea",,,,,,, +"Standard","FACILITY","Facilities","Cold Storage","ea",,,,,,, +"Standard","FACILITY","Facilities","Empty Lot","ea",,,,,,, +"Standard","FACILITY","Facilities","Grain Storage","ea",,,,,,, +"Standard","FACILITY","Facilities","Hand Wash Station","ea",,,,,,, +"Standard","FACILITY","Facilities","Mobile Kitchen","ea",,,,,,, +"Standard","FACILITY","Facilities","Office","ea",,,,,,, +"Standard","FACILITY","Facilities","Other Facilities","ea",,,,,,, +"Standard","FACILITY","Facilities","Rail Station","ea",,,,,,, +"Standard","FACILITY","Facilities","Shelter","ea",,,,,,, +"Standard","FACILITY","Facilities","Staging","ea",,,,,,, +"Standard","FACILITY","Facilities","Storage","ea",,,,,,, +"Standard","FOOD","Food & Water","Animal Feed","lbs",,,,,,, +"Standard","FOOD","Food & Water","Canned/Dried Food","lbs",,,,,,,"priority" +"Standard","FOOD","Food & Water","Ice","lbs",,,,,,,"priority" +"Standard","FOOD","Food & Water","Infant Formula","lbs",,,,,,,"priority" +"Standard","FOOD","Food & Water","Non-Potable Water","gal",,,,,,, +"Standard","FOOD","Food & Water","Perishable Food","lbs",,,,,,,"priority" +"Standard","FOOD","Food & Water","Potable Water","gal",,,,,,,"priority" +"Standard","FUEL","Fuel","Aviation","gal",,,,,,, +"Standard","FUEL","Fuel","Diesel","gal",,,,,,, +"Standard","FUEL","Fuel","Gasoline","gal",,,,,,,"priority" +"Standard","FUEL","Fuel","Heating Oil","gal",,,,,,, +"Standard","FUEL","Fuel","Liquid Propane","gal",,,,,,,"priority" +"Standard","FUEL","Fuel","Natural Gas","gal",,,,,,, +"Standard","SUPPLIES","General supplies","Anticeptic","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Bandages","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Batteries","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Body Bags","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Boxes","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Cane","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Cleaners","ea",,,,,,,"priority" +"Standard","SUPPLIES","General supplies","Crutches","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Defibrilator","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Diapers","dz",,,,,,, +"Standard","SUPPLIES","General supplies","Hearing Aid","ea",,,,,,, +"Standard","SUPPLIES","General supplies","ID Badges","ea",,,,,,, +"Standard","SUPPLIES","General supplies","Insect Repellant","ea",,,,,,, +"Standard","MEDICAL","Medical supplies","Office Supplies","ea",,,,,,, +"Standard","MEDICAL","Medical supplies","Plastic Sheeting","roll",,,,,,,"priority" +"Standard","MEDICAL","Medical supplies","Tape","roll",,,,,,, +"Standard","MEDICAL","Medical supplies","Tarps","ea",,,,,,,"priority" +"Standard","MEDICAL","Medical supplies","Toiletries","ea",,,,,,,"priority" +"Standard","MEDICAL","Medical supplies","Towels","ea",,,,,,, +"Standard","MEDICAL","Medical supplies","Trash Bags","bx",,,,,,,"priority" +"Standard","MEDICAL","Medical supplies","Ventilator","ea",,,,,,, +"Standard","MEDICAL","Medical supplies","Wheelchair","ea",,,,,,, +"Standard","OTHER","Other","Other","ea",,,,,,, +"Standard","SERVICES","Services","Accounting","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Administrative","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Architecture","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Automotive repair","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Care and shelter","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Catering","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Communications","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Construction","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Debris removal","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Demolition","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Engineering","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Firefighting ","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Fumigation","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","General Labor","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Health Care","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Household Repair","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Housing","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Legal","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Other Services","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Photography","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Planning (Urban)","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Resource Protection (Environmental)","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Search and Rescue","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Security","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Skilled Labor","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Translation","hour","day",8,"week",40,"month",160, +"Standard","SERVICES","Services","Transportation","hour","day",8,"week",40,"month",160,"priority" +"Standard","SERVICES","Services","Warehousing","hour","day",8,"week",40,"month",160, diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/__init__.py b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/__init__.py @@ -0,0 +1 @@ + diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/auth_roles.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/auth_roles.csv new file mode 100644 index 0000000..4022c6a --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/auth_roles.csv @@ -0,0 +1,91 @@ +uid,role,description,controller,function,table,entity,uacl,oacl,Notes +ANONYMOUS,Anonymous,Unauthenticated users,,,org_organisation,,READ,READ,Required for self-registration +ANONYMOUS,Anonymous,,org,sites_for_org,,,READ,READ,Required for self-registration +ANONYMOUS,Anonymous,,gis,,,,READ,READ, +ANONYMOUS,Anonymous,,vulnerability,,,,READ,READ, +ANONYMOUS,Anonymous,,water,,,,READ,READ, +ANONYMOUS,Anonymous,,delphi,,,,READ,READ, +AUTHENTICATED,Authenticated,Authenticated - all logged-in users,gis,,,,READ,READ, +AUTHENTICATED,Authenticated,,vulnerability,,,,READ,READ, +AUTHENTICATED,Authenticated,,water,,,,READ,READ, +AUTHENTICATED,Authenticated,,delphi,,,,READ,READ, +AUTHENTICATED,Authenticated,,impact,,,,READ,READ, +AUTHENTICATED,Authenticated,,doc,,,,READ,READ, +EDITOR,Editor,Editor - can access & make changes to any unprotected data,pr,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,vulnerability,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,survey,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,security,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,fire,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,deploy,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,water,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,budget,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,cap,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,vol,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,delphi,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,org,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,scenario,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,vehicle,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,member,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,cr,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,gis,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,asset,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,patient,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,transport,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,po,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,impact,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,dvr,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,event,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,msg,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,supply,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,project,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,doc,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,hrm,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,appadmin,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,hms,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,req,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,mpr,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,sync,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,inv,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,stats,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,edu,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,dvi,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +EDITOR,Editor,,cms,,,,ALL,CREATE|READ|UPDATE|REVIEW|APPROVE, +MAP_ADMIN,Map Admin,"Complementary role: FULL access mode only to the maps and their configurations +When to assign: Staff Directors.",gis,,,,ALL,ALL, +ORG_ADMIN,Organization Admin,,org,,,,ALL,ALL, +ORG_GROUP_ADMIN,Organzation Group Admin,,,,,,,, +medical_admin,Medical Details Admin,"Complementary role: FULL access but only to medical data of registered evacuees. It must be assigned with other roles to grant access to the other types of data. +When to assign: Medical Staff.",patient,,,,ALL,ALL, +medical_admin,Medical Details Admin,,hms,,,,ALL,ALL, +medical_admin,Medical Details Admin,,dvi,,,,ALL,ALL, +notification_sender,Notification Sender,,msg,,,,ALL,ALL, +org_reader,Organization Reader,"Complementary role: Access in ""READ only"" mode to Organization data and its branches +When to assign: Staff Directors",org,,,,READ,READ, +private_user_editor,Private User Editor,"FULL access to modules Evacuees and Shelter. +When to assign: private user member of a shelter / Organization and committed to provide assistance to some evacuees",cr,,,,ALL,ALL, +private_user_editor,Private User Editor,,dvr,,,,ALL,ALL, +private_user_reader,Private User Reader,"Access in ""READ only"" mode to modules Evacuees and Shelter. +When to assign: private user member of a Shelter / Organization and committed to provide assistance to some evacuees.",cr,,,,READ,READ, +private_user_reader,Private User Reader,,dvr,,,,READ,READ, +public_auth_editor,Public Authority Editor,"FULL access to modules Evacuees and Shelter. +When to assign: supervisor user member of local government office with the rights to register data.",cr,,,,ALL,ALL, +public_auth_editor,Public Authority Editor,,dvr,,,,ALL,ALL, +public_auth_editor,Public Authority Editor,,mpr,,,,READ,READ, +public_auth_reader,Public Authority Reader,"Access in ""READ only"" mode to modules Evacuees and Shelter. +When to assign: supervisor user member of a local government office.",dvr,,,,READ,READ, +staff_admin,Staff Admin,"FULL access to modules Staff, Volunteer, Evacuees and Shelter. +When to assign: Staff assigned to manage one or more shelters.",vol,,,,ALL,ALL, +staff_admin,Staff Admin,,cr,,,,ALL,ALL, +staff_admin,Staff Admin,,dvr,,,,ALL,ALL, +staff_admin,Staff Admin,,hrm,,,,ALL,ALL, +staff_reader,Staff Reader,"Access in ""READ only"" mode to modules Staff, Volunteer, Evacuees and Shelter. +When to assign: Staff assigned to manage one or more shelters.",vol,,,,READ,READ, +staff_reader,Staff Reader,,cr,,,,READ,READ, +staff_reader,Staff Reader,,dvr,,,,READ,READ, +staff_reader,Staff Reader,,hrm,,,,READ,READ, +vol_editor,Volunteer Editor,"FULL access to modules Evacuees and Shelter. +When to assign: volunteer (trusted person) volunteer who provides assistance to some evacuees inside a well-defined group of shelters.",cr,,,,ALL,ALL, +vol_editor,Volunteer Editor,,dvr,,,,ALL,ALL, +vol_reader,Volunteer Reader,"Access in ""READ only"" mode to modules Evacuees and Shelter. +When to assign: volunteer who provides assistance to some evacuees inside a well- defined group of shelters.",cr,,,,READ,READ, +vol_reader,Volunteer Reader,,dvr,,,,READ,READ, diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/certificate.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/certificate.csv new file mode 100644 index 0000000..9de4fee --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/certificate.csv @@ -0,0 +1,21 @@ +Name +"CPA - Certified Public Accountant" +"CSW - Certified Social Worker" +"DR1 - Driver's License - Car" +"DR2 - Driver's License - Lt truck" +"DR3 - Driver's License Heavy truck" +"DR4 - Driver's License Bus" +"DR5 - Driver's License Commercial" +"DR6 - Driver's License Motorcycle" +"EMT - Emergency Medical Technician" +"HRO - Ham Radio Operator" +"LPC - Licensed Professional Counselor" +"LPN - Licensed Practical Nurse" +"LSW - Licensed Social Worker" +"LVN - Licensed Vocational Nurse" +"MD - Medical Doctor" +"MFT - Marriage and Family Therapist" +"MT - Medical Technician" +"PA - Physician Assistant" +"PSY - Psychologist" +"RN - Registered Nurse" \ No newline at end of file diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/cms_post.csv b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/cms_post.csv new file mode 100644 index 0000000..e220722 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/cms_post.csv @@ -0,0 +1,4 @@ +Name,Module,Resource,Body +About,default,about,"

About

" +Help,default,help,"

Help

User & Administration Guide - online version" +CMS Module Index,cms,,"

Content Management System

This module allows the editing of page content using a web browser.

Posts can be either full pages, embedded within other pages or part of a series (for use as news items or blog posts)

" diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/config.py b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/config.py new file mode 100644 index 0000000..8abff19 --- /dev/null +++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/config.py @@ -0,0 +1,1500 @@ +# -*- coding: utf-8 -*- + +from collections import OrderedDict + +from gluon import current +from gluon.storage import Storage + +def config(settings): + T = current.T + + # Pre-Populate + # http://eden.sahanafoundation.org/wiki/DeveloperGuidelines/PrePopulate + # Configure/disable pre-population of the database. + # To pre-populate the database On 1st run should specify directory(s) in + # /modules/templates/ + # eg: + # ["default"] (1 is a shortcut for this) + # ["default", "default/users"] + # Unless doing a manual DB migration, where prepopulate = 0 + # In Production, prepopulate = 0 (to save 1x DAL hit every page) + settings.base.prepopulate.append("Spotter") + + # Uncomment this to prefer scalability-optimized strategies globally + #settings.base.bigtable = True + + # Theme (folder to use for views/layout.html) + #settings.base.theme = "default" + + # Enable Guided Tours + # - defaults to module enabled or not + #settings.base.guided_tour = True + + # Authentication settings + # These settings should be changed _after_ the 1st (admin) user is + # registered in order to secure the deployment + # 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 + # Disable welcome-emails to newly registered users + #settings.auth.registration_welcome_email = False + + # Allow a new user to be linked to a record (and a new record will be created if it doesn't already exist) + settings.auth.registration_link_user_to = {"staff":T("Staff"), + "volunteer":T("Volunteer"), + "member":T("Member")} + + # Always notify the approver of a new (verified) user, even if the user is automatically approved + #settings.auth.always_notify_approver = False + + # The name of the teams that users are added to when they opt-in to receive alerts + #settings.auth.opt_in_team_list = ["Updates"] + # Uncomment this to set the opt in default to True + #settings.auth.opt_in_default = True + # Uncomment this to request the Home Phone when a user registers + settings.auth.registration_requests_home_phone = True + # Uncomment this to request the Mobile Phone when a user registers + settings.auth.registration_requests_mobile_phone = True + # Uncomment this to have the Mobile Phone selection during registration be mandatory + #settings.auth.registration_mobile_phone_mandatory = True + # Uncomment this to request the Organisation when a user registers + settings.auth.registration_requests_organisation = True + # Uncomment this to have the Organisation selection during registration be mandatory + settings.auth.registration_organisation_required = True + # Uncomment this to hide the Create-Organisation link in registration forms + #settings.auth.registration_organisation_link_create = False + # Uncomment this to have the Organisation input hidden unless the user enters a non-whitelisted domain + #settings.auth.registration_organisation_hidden = True + # Uncomment this to default the Organisation during registration + settings.auth.registration_organisation_default = "My Organisation" + # Uncomment this to request the Organisation Group when a user registers + #settings.auth.registration_requests_organisation_group = True + # Uncomment this to have the Organisation Group selection during registration be mandatory + #settings.auth.registration_organisation_group_required = True + # Uncomment this to request the Site when a user registers + #settings.auth.registration_requests_site = True + # Uncomment this to allow Admin to see Organisations in User Admin even if the Registration doesn't request this + #settings.auth.admin_sees_organisation = True + # Uncomment to set the default role UUIDs assigned to newly-registered users + # This is a dictionary of lists, where the key is the realm that the list of roles applies to + # The key 0 implies not realm restricted + # The keys "organisation_id" and "site_id" can be used to indicate the user's "organisation_id" and "site_id" + #settings.auth.registration_roles = { 0: ["STAFF", "PROJECT_EDIT"]} + # Whether the first user to register for an Org should get the ORG_ADMIN role for that Org + settings.auth.org_admin_to_first = True + # Define which entity types to use as realm entities in role manager + settings.auth.realm_entity_types = ("org_organisation",) + # Uncomment to activate entity role manager tabs for OrgAdmins + settings.auth.entity_role_manager = True + # Define modules for entity role manager + #settings.auth.role_modules = OrderedDict([]) + # Define access levels for entity role manager + #settings.auth.access_levels = OrderedDict([]) + # Uncomment this to enable record approval + settings.auth.record_approval = True + # Uncomment this and specify a list of tablenames for which record approval is required + settings.auth.record_approval_required_for = ("cap_alert",) + settings.auth.record_approval_manual = ("cap_alert",) + # Uncomment this to request an image when users register + #settings.auth.registration_requests_image = True + # Uncomment this to direct newly-registered users to their volunteer page to be able to add extra details + # NB This requires Verification/Approval to be Off + # @ToDo: Extend to all optional Profile settings: Homepage, Twitter, Facebook, Mobile Phone, Image + #settings.auth.registration_volunteer = True + # Terms of Service to be able to Register on the system + # https://termsfeed.com/terms-conditions/generator/ + # uses