def postp(r, output): if r.interactive: # Normal Action Buttons s3_action_buttons(r) # Custom Action Buttons for Enable/Disable table = r.table query = (table.deleted == False) rows = 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] from s3 import s3_str s3.actions += [dict(label=s3_str(T("Enable")), _class="action-btn", url=URL(args=["[id]", "enable"]), restrict = restrict_e), dict(label=s3_str(T("Disable")), _class="action-btn", url = URL(args = ["[id]", "disable"]), restrict = restrict_d), ] if not s3task._is_alive(): # No Scheduler Running s3.actions += [dict(label=s3_str(T("Poll")), _class="action-btn", url = URL(args = ["[id]", "poll"]), restrict = restrict_d) ] return output
def people_affected(cls): """ Count total number of affected people by demographic type """ db = current.db s3db = current.s3db table = s3db.req_need_line query = (table.deleted == False) demographic = table.parameter_id represent = demographic.represent total = table.value.sum() rows = db(query).select(demographic, total, groupby = demographic, orderby = ~(total), limitby = (0, 5), ) values = [] for row in rows: value = row[total] parameter_id = row[demographic] values.append({"label": s3_str(represent(parameter_id)), "value": value if value else 0, "filterKey": parameter_id, }) return [{"key": s3_str(current.T("People Affected")), "values": values, }, ]
def custom_postp(r, output): # Call standard postp if callable(standard_postp): output = standard_postp(r, output) from gluon import URL from s3 import s3_str if workflow == "issue": s3.actions = [ { "label": s3_str(T("Issue")), "url": URL( f="asset", args=["[id]", "log", "assignperson"], ), "_class": "action-btn", }, ] elif workflow == "return": s3.actions = [ { "label": s3_str(T("Return")), "url": URL( f="asset", args=["[id]", "log", "return"], ), "_class": "action-btn", }, ] return output
def postp(r, output): if r.interactive: # Normal Action Buttons s3_action_buttons(r) # Custom Action Buttons for Enable/Disable table = r.table query = (table.deleted == False) rows = 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] from s3 import s3_str s3.actions += [ dict(label=s3_str(T("Enable")), _class="action-btn", url=URL(args=["[id]", "enable"]), restrict=restrict_e), dict(label=s3_str(T("Disable")), _class="action-btn", url=URL(args=["[id]", "disable"]), restrict=restrict_d), ] if not s3task._is_alive(): # No Scheduler Running s3.actions += [ dict(label=s3_str(T("Poll")), _class="action-btn", url=URL(args=["[id]", "poll"]), restrict=restrict_d) ] return output
def get_sms_content(row): """ prepare the content for SMS """ from gluon.languages import lazyT itable = current.s3db.cap_info event_type_id = row["cap_info.event_type_id"] priority_id = row["cap_info.priority"] if not isinstance(event_type_id, lazyT): event_type = itable.event_type_id.represent(event_type_id) else: event_type = event_type_id if priority_id and priority_id != "-": if not isinstance(priority_id, lazyT): priority = itable.priority.represent(priority_id) else: priority = priority_id else: priority = T("None") sms_body = \ T("""%(Status)s %(MessageType)s for %(AreaDescription)s with %(Priority)s priority %(EventType)s issued by %(SenderName)s at %(Date)s (ID:%(Identifier)s) \n\n""") % \ dict(Status = s3_str(row["cap_alert.status"]), MessageType = s3_str(row["cap_alert.msg_type"]), AreaDescription = s3_str(row["cap_area.name"]), Priority = s3_str(priority), EventType = s3_str(event_type), SenderName = s3_str(row["cap_info.sender_name"]), Date = s3_str(row["cap_alert.sent"]), Identifier = s3_str(row["cap_alert.identifier"])) return s3_str(sms_body)
def set_priority_js(): """ Output json for priority field """ wptable = s3db.cap_warning_priority rows = db(wptable).select( wptable.name, wptable.urgency, wptable.severity, wptable.certainty, wptable.color_code, orderby=wptable.name, ) from gluon.serializers import json as jsons from s3 import s3_str p_settings = [(s3_str(T(r.name)), r.urgency, r.severity, r.certainty, r.color_code)\ for r in rows] priority_conf = '''S3.cap_priorities=%s''' % jsons(p_settings) js_global = s3.js_global if not priority_conf in js_global: js_global.append(priority_conf) return
def render_list(self, value, labels, show_link=True): """ Helper method to render list-type representations from bulk()-results. @param value: the list @param labels: the labels as returned from bulk() @param show_link: render references as links, should be the same as used with bulk() """ show_link = show_link and self.show_link values = [v for v in value if v is not None] if not len(values): return "" if show_link: labels_ = (labels[v] if v in labels else self.default for v in values) else: labels_ = sorted( s3_str(labels[v]) if v in labels else self.default for v in values) if current.auth.permission.format == "xls": return ", ".join(labels_) html = UL(_class="service-list") for label in labels_: html.append(LI(label)) return html
def get_email_subject(row): """ prepare the subject for Email """ from gluon.languages import lazyT itable = current.s3db.cap_info event_type_id = row["cap_info.event_type_id"] priority_id = row["cap_info.priority"] if not isinstance(event_type_id, lazyT): event_type = itable.event_type_id.represent(event_type_id) else: event_type = event_type_id if priority_id and priority_id != "-": if not isinstance(priority_id, lazyT): priority = itable.priority.represent(priority_id) else: priority = priority_id else: priority = T("None") subject = "[%s] %s %s" % (s3_str(row["cap_info.sender_name"]), event_type, priority) return subject
def write(self, sheet, rowindex, colindex, label, style="odd"): """ Write a label/value into the XLS worksheet @param sheet: the worksheet @param rowindex: the row index @param colindex: the column index @param label: the label/value to write @param style: style name (S3XLS styles) """ styles = self.styles if not styles: self.styles = styles = S3XLS._styles() style = styles.get(style) if not style: import xlwt style = xlwt.XFStyle() label = s3_str(label) # Adjust column width col = sheet.col(colindex) curwidth = col.width or 0 adjwidth = max(len(label) * 240, 2480) if label else 2480 col.width = max(curwidth, adjwidth) row = sheet.row(rowindex) row.write(colindex, label, style)
def needs_by_status(cls): """ Count need lines per status - for all open Events """ db = current.db s3db = current.s3db table = s3db.req_need_line etable = s3db.event_event ltable = s3db.event_event_need # Extract the data status = table.status number = table.id.count() query = (etable.closed == False) & \ (etable.id == ltable.event_id) & \ (ltable.need_id == table.need_id) & \ (table.deleted == False) rows = db(query).select(status, number, groupby = status) # Build data structure for chart renderer rows = dict((row[status], row[number]) for row in rows) data = [] for code, label, color in cls.REQ_STATUS[::-1]: # clockwise value = rows.get(code) data.append({"label": s3_str(label), "value": value if value else 0, "color": color, "filterKey": code, }) return data
def needs_by_status(cls): """ Count need lines per status """ db = current.db # Extract the data table = current.s3db.req_need_line status = table.status number = table.id.count() query = (table.deleted == False) rows = db(query).select(status, number, groupby=status) # Build data structure for chart renderer rows = dict((row[status], row[number]) for row in rows) data = [] for code, label, color in cls.REQ_STATUS: value = rows.get(code) data.append({ "label": s3_str(label), "value": value if value else 0, "color": color, "filterKey": code, }) return data
def needs_by_status(cls): """ Count need lines per status """ db = current.db # Extract the data table = current.s3db.req_need_line status = table.status number = table.id.count() query = (table.deleted == False) rows = db(query).select(status, number, groupby = status) # Build data structure for chart renderer rows = dict((row[status], row[number]) for row in rows) data = [] for code, label, color in cls.REQ_STATUS: value = rows.get(code) data.append({"label": s3_str(label), "value": value if value else 0, "color": color, "filterKey": code, }) return data
def ucce_masterkey_context(masterkey): """ Provide context information for a masterkey (populates session in mobile app when linking to this masterkey) Args: masterkey: the auth_masterkey Row Returns: JSON-serializable dict with the context data """ db = current.db s3db = current.s3db context = {} # Look up the project linked to the master key ptable = s3db.project_project ltable = s3db.project_project_masterkey query = (ltable.id == masterkey.id) & \ (ltable.deleted == False) & \ (ptable.id == ltable.project_id) & \ (ptable.deleted == False) project = db(query).select(ptable.id, ptable.name, limitby = (0, 1), ).first() if project: # Provide the project title context["projectTitle"] = project.name # Provide a list of available translations in this project languages = [] # Use translated language names from s3 import IS_ISO639_2_LANGUAGE_CODE, s3_str represent = IS_ISO639_2_LANGUAGE_CODE.represent_local # Look up the languages ttable = s3db.project_project_target l10ntable = s3db.dc_target_l10n query = (ttable.project_id == project.id) & \ (ttable.deleted == False) & \ (ttable.target_id == l10ntable.target_id) & \ (l10ntable.language != None) # Build the language list rows = db(query).select(l10ntable.language) seen = set() for row in rows: code = row.language if code not in seen: languages.append((code, s3_str(represent(code)))) seen.add(code) context["surveyLanguages"] = languages return context
def draw_value(self, x, y, value, width=120, height=40, size=7, bold=True, valign=None): """ Helper function to draw a centered text above position (x, y); allows the text to wrap if it would otherwise exceed the given width Args: x: drawing position y: drawing position value: the text to render width: the maximum available width (points) height: the maximum available height (points) size: the font size (points) bold: use bold font valign: vertical alignment ("top"|"middle"|"bottom"), default "bottom" Returns: The actual height of the text element drawn """ # Preserve line breaks by replacing them with <br/> tags value = s3_str(value).strip("\n").replace('\n', '<br />\n') styleSheet = getSampleStyleSheet() style = styleSheet["Normal"] style.fontName = BOLD if bold else NORMAL style.fontSize = size style.leading = size + 2 style.splitLongWords = False style.alignment = TA_CENTER para = Paragraph(value, style) aW, aH = para.wrap(width, height) while ((aH > height or aW > width) and style.fontSize > 4): # Reduce font size to make fit style.fontSize -= 1 style.leading = style.fontSize + 2 para = Paragraph(value, style) aW, aH = para.wrap(width, height) if valign == "top": vshift = aH elif valign == "middle": vshift = aH / 2.0 else: vshift = 0 para.drawOn(self.canv, x - para.width / 2, y - vshift) return aH
def onapprove(record): # Normal onapprove s3db.cap_alert_approve(record) # Sync FTP Repository current.s3task.async("cap_ftp_sync") # Twitter Post if settings.get_cap_post_to_twitter(): try: import tweepy except ImportError: current.log.debug("tweepy module needed for sending tweets") else: T = current.T db = current.db alert_id = int(record["id"]) atable = s3db.cap_alert itable = s3db.cap_info arow = db(atable.id == alert_id).select(atable.status, atable.sender, atable.sent, limitby=(0, 1)).first() query = (itable.alert_id == alert_id) & \ (itable.deleted != True) irows = db(query).select(itable.headline, itable.web) # @ToDo: shorten url # @ToDo: Handle the multi-message nicely? # @ToDo: Send resource url with tweet for irow in irows: twitter_text = \ ("""%(Status)s: %(Headline)s %(SENDER)s: %(SenderName)s %(WEBSITE)s: %(Website)s""") % dict(Status = arow.status, Headline = s3_str(irow.headline), SENDER = T("Sender"), SenderName = s3_str(arow.sender), WEBSITE = T("Website"), Website = irow.web) try: current.msg.send_tweet(text=twitter_text) except tweepy.error.TweepError, e: current.log.debug("Sending tweets failed: %s" % e)
def people_affected(cls): """ Count total number of affected people by demographic type - for all open Events """ db = current.db s3db = current.s3db table = s3db.req_need_line etable = s3db.event_event ltable = s3db.event_event_need query = (etable.closed == False) & \ (etable.id == ltable.event_id) & \ (ltable.need_id == table.need_id) & \ (table.deleted == False) demographic = table.parameter_id represent = demographic.represent total = table.value.sum() rows = db(query).select( demographic, total, groupby=demographic, orderby=~(total), limitby=(0, 5), ) values = [] for row in rows: value = row[total] parameter_id = row[demographic] values.append({ "label": s3_str(represent(parameter_id)), "value": value if value else 0, "filterKey": parameter_id, }) return [ { "key": s3_str(current.T("People Affected")), "values": values, }, ]
def represent_row(self, row, prefix=None): """ Represent a single Row @param row: the req_need Row """ # Custom Row (with the tag left-joined) req_number = row["req_need_tag.value"] if req_number: return s3_str(req_number) else: # Fallback to name name = row["req_need.name"] if name: return s3_str(name) else: return current.messages["NONE"]
def onapprove(record): # Normal onapprove s3db.cap_alert_approve(record) # Sync FTP Repository current.s3task. async ("cap_ftp_sync") # Twitter Post if settings.get_cap_post_to_twitter() and \ record["scope"] != "Private": try: import tweepy except ImportError: current.log.debug( "tweepy module needed for sending tweets") else: alert_id = int(record["id"]) atable = s3db.cap_alert itable = s3db.cap_info arow = db(atable.id == alert_id).select( atable.status, limitby=(0, 1)).first() query = (itable.alert_id == alert_id) & \ (itable.deleted != True) irows = db(query).select(itable.headline, itable.sender_name, itable.web) # @ToDo: shorten url # @ToDo: Handle the multi-message nicely? # @ToDo: Send resource url with tweet for irow in irows: twitter_text = \ ("""%(Status)s: %(Headline)s %(SENDER)s: %(SenderName)s %(WEBSITE)s: %(Website)s%(Profile)s""") % dict(Status = arow.status, Headline = s3_str(irow.headline), SENDER = T("Sender"), SenderName = s3_str(irow.sender_name), WEBSITE = T("Website"), Website = irow.web, Profile = "/profile", ) try: current.msg.send_tweet(text=twitter_text) except tweepy.error.TweepError, e: current.log.debug("Sending tweets failed: %s" % e)
def cap_AreaRowOptionsBuilder(alert_id, caller=None): """ Build the options for the cap_area associated with alert_id with the translated name (if available) @param caller: currently used by assign method """ atable = s3db.cap_area if caller: assign = caller == "assign" else: assign = None if assign: query = (atable.is_template == True) & (atable.deleted != True) else: query = (atable.alert_id == alert_id) & (atable.deleted != True) rows = db(query).select(atable.id, atable.template_area_id, atable.name, orderby=atable.id) values = [row.id for row in rows] count = len(values) if count: from s3 import s3_str if count == 1: query_ = (atable.id == values[0]) else: query_ = (atable.id.belongs(values)) ltable = s3db.cap_area_name if assign: left = [ltable.on((ltable.area_id == atable.id) & \ (ltable.language == session.s3.language)), ] else: left = [ltable.on((ltable.area_id == atable.template_area_id) & \ (ltable.language == session.s3.language)), ] fields = [atable.name, ltable.name_l10n, ] rows_ = db(query_).select(left=left, limitby=(0, count), *fields) cap_area_options = {} for row_ in rows_: cap_area_options[row_["cap_area.name"]] = \ s3_str(row_["cap_area_name.name_l10n"] or \ row_["cap_area.name"]) return cap_area_options
def cap_AreaRowOptionsBuilder(alert_id, caller=None): """ Build the options for the cap_area associated with alert_id with the translated name (if available) @param caller: currently used by assign method """ atable = s3db.cap_area if caller: assign = caller == "assign" else: assign = None if assign: query = (atable.is_template == True) & (atable.deleted != True) else: query = (atable.alert_id == alert_id) & (atable.deleted != True) rows = db(query).select(atable.id, atable.template_area_id, atable.name, orderby=atable.id) values = [row.id for row in rows] count = len(values) if count: from s3 import s3_str if count == 1: query_ = (atable.id == values[0]) else: query_ = (atable.id.belongs(values)) ltable = s3db.cap_area_name if assign: left = [ltable.on((ltable.area_id == atable.id) & \ (ltable.language == session.s3.language)), ] else: left = [ltable.on((ltable.area_id == atable.template_area_id) & \ (ltable.language == session.s3.language)), ] fields = [ atable.name, ltable.name_l10n, ] rows_ = db(query_).select(left=left, limitby=(0, count), *fields) cap_area_options = {} for row_ in rows_: cap_area_options[row_["cap_area.name"]] = \ s3_str(row_["cap_area_name.name_l10n"] or \ row_["cap_area.name"]) return cap_area_options
def json(self): """ Produce the notification emails as JSON-serializable dict @returns: a dict of dicts of the structure: {recipient_type: {email, subject, message}} """ data = self.data() if not data: return {} for key, value in list(data.items()): if value is None: data[key] = "-" else: data[key] = s3_str(value) output = { "delegationID": self.delegation_id, "data": data, "email": {}, "autoSelect": {}, "cc": data.get("coordinator_email"), "templates": {}, } recipients = self.recipients for recipient in ("organisation", "volunteer", "office"): config = recipients.get(recipient) if not config: continue template_name, auto_select = config templates = self.templates(template_name) if templates: output["templates"][recipient] = { "subject": templates[0], "message": templates[1], } else: continue if recipient == "office": email = data.get("volunteer_office_email") else: email = data.get("%s_email" % recipient) if email: output["email"][recipient] = email output["autoSelect"][recipient] = auto_select return output
def custom_postp(r, output): # Call standard postp if callable(standard_postp): output = standard_postp(r, output) if ERT_LEADER: # Don't open Projects: Open Activities from gluon import URL from s3 import s3_str s3.actions = [dict(label=s3_str(T("Open")), _class="action-btn", url=URL(args=["[id]", "activity"]))] return output
def needs_by_category(cls): """ Count current case activities by need type """ db = current.db s3db = current.s3db atable = s3db.br_case_activity stable = s3db.br_case_activity_status join = stable.on((stable.id == atable.status_id) & \ (stable.is_closed == False)) today = current.request.utcnow.date() query = ((atable.date == None) | (atable.date <= today)) & \ ((atable.end_date == None) | (atable.end_date >= today)) & \ (atable.person_id != None) & \ (atable.need_id != None) & \ (atable.deleted == False) number = atable.id.count() rows = db(query).select( atable.need_id, number, join=join, groupby=atable.need_id, orderby=~number, limitby=(0, 5), ) represent = atable.need_id.represent labels = represent.bulk([row.br_case_activity.need_id for row in rows]) values = [] for row in rows: value = row[number] need_id = row.br_case_activity.need_id need = labels.get(need_id) values.append({ "label": str(need), "color": cls.get_color(need_id), "value": value if value else 0, }) return [ { "key": s3_str(current.T("Current Need Reports")), "values": values, }, ]
def offers_by_category(cls): """ Count current assistance offers by need type """ db = current.db s3db = current.s3db atable = s3db.br_assistance_offer today = current.request.utcnow.date() query = (atable.status == "APR") & \ (atable.availability == "AVL") & \ ((atable.date == None) | (atable.date <= today)) & \ ((atable.end_date == None) | (atable.end_date >= today)) & \ (atable.pe_id != None) & \ (atable.need_id != None) & \ (atable.deleted == False) number = atable.id.count() rows = db(query).select( atable.need_id, number, groupby=atable.need_id, orderby=~number, limitby=(0, 5), ) represent = atable.need_id.represent labels = represent.bulk( [row.br_assistance_offer.need_id for row in rows]) values = [] for row in rows: value = row[number] need_id = row.br_assistance_offer.need_id need = labels.get(need_id) values.append({ "label": str(need), "color": cls.get_color(need_id), "value": value if value else 0, }) return [ { "key": s3_str(current.T("Current Relief Offers")), "values": values, }, ]
def draw_value(self, x, y, value, width=120, height=40, size=7, bold=True, valign=None): """ Helper function to draw a centered text above position (x, y); allows the text to wrap if it would otherwise exceed the given width @param x: drawing position @param y: drawing position @param value: the text to render @param width: the maximum available width (points) @param height: the maximum available height (points) @param size: the font size (points) @param bold: use bold font @param valign: vertical alignment ("top"|"middle"|"bottom"), default "bottom" @returns: the actual height of the text element drawn """ # Preserve line breaks by replacing them with <br/> tags value = s3_str(value).strip("\n").replace('\n','<br />\n') stylesheet = getSampleStyleSheet() style = stylesheet["Normal"] style.fontName = BOLD if bold else NORMAL style.fontSize = size style.leading = size + 2 style.splitLongWords = False style.alignment = TA_CENTER para = Paragraph(value, style) aw, ah = para.wrap(width, height) while((ah > height or aw > width) and style.fontSize > 4): # Reduce font size to make fit style.fontSize -= 1 style.leading = style.fontSize + 2 para = Paragraph(value, style) aw, ah = para.wrap(width, height) if valign == "top": vshift = ah elif valign == "middle": vshift = ah / 2.0 else: vshift = 0 para.drawOn(self.canv, x - para.width / 2, y - vshift) return ah
def represent_row(self, row): """ Represent a single Row Args: row: the org_site Row """ name = row["org_site.name"] if not name: return self.default if self.show_type: instance_type = row["org_site.instance_type"] facility_types = row.get("facility_types") if facility_types: represent = current.s3db.org_site_facility_type.facility_type_id.represent type_names = represent.multiple(facility_types) name = "%s (%s)" % (name, type_names) else: instance_type = current.auth.org_site_types.get( instance_type, None) if instance_type: name = "%s (%s)" % (name, instance_type) organisation_id = row.get("org_organisation.id") root_org = row.get("org_organisation.root_organisation") ns = None parent = None if root_org == self.root_org: if organisation_id != self.organisation_id: parent = self.org_labels.get(organisation_id) else: ns = self.org_labels.get(root_org) if root_org != organisation_id: parent = self.org_labels.get(organisation_id) if ns: if parent: name = "ᛜᛜ%s > %s > %s" % (ns, parent, name) else: name = "ᛜᛜ%s > %s" % (ns, name) elif parent: name = "ᛜ%s > %s" % (parent, name) return s3_str(name)
def custom_postp(r, output): # Call standard postp if callable(standard_postp): output = standard_postp(r, output) if ERT_LEADER: # Don't open Projects: Open Activities from gluon import URL from s3 import s3_str s3.actions = [ dict(label=s3_str(T("Open")), _class="action-btn", url=URL(args=["[id]", "activity"])), ] return output
def link(self, k, v, row=None): """ Represent a (key, value) as hypertext link. - same as default, except with k and v reversed ;) @param k: the key [here: the shipment code] @param v: the representation of the key [here: the record ID] @param row: the row with this key """ if self.linkto: k = s3_str(k) return A(k, _href=self.linkto.replace("[id]", v) \ .replace("%5Bid%5D", v)) else: return k
def represent_row(self, row, prefix=None): """ Represent a single Row @param row: the project_activity Row """ # Custom Row (with the Orgs left-joined) organisation_id = row["project_activity_organisation.organisation_id"] if organisation_id: return self.org_represent(organisation_id) else: # Fallback to name name = row["project_activity.name"] if name: return s3_str(name) else: return current.messages["NONE"]
def write_details_to_csv(csv_file, statistic, data): cnt = 0 writerow = csv_file.writerow for (location, details) in data.items(): loc = location_list[location] for key in sorted(details.keys()): row = details[key] source = row[2] source_url = "" url = urlparse.urlparse(source) if url[0] != "": source_url = source (head, tail) = split(url[2]) source = tail.replace("%20", " ") cnt += 1 writerow([ statistic, row[0], loc[0], loc[1], loc[2], loc[3], key, s3_str(source), row[1], source_url ])
def draw_label(self, x, y, colname, default=""): """ Helper function to draw a centered label below position (x, y) @param x: drawing position @param y: drawing position @param colname: the column name of the field to look up the label @param default: the default label (if label cannot be looked up), pass colname=None to enforce using the default """ if colname: label = self.labels.get(colname, default) else: label = default c = self.canv c.setFont(NORMAL, 5) c.drawCentredString(x, y - 6, s3_str(label))
def draw_field(self, x, y, colname, size=7, bold=True): """ Helper function to draw a centered field value of self.item above position (x, y) @param x: drawing position @param y: drawing position @param colname: the column name of the field to look up the value @param size: the font size (points) @param bold: use bold font """ c = self.canv font = BOLD if bold else NORMAL value = self.item.get(colname) if value: c.setFont(font, size) c.drawCentredString(x, y, s3_str(value))
def represent_row(self, row): """ Represent a row @param row: the Row """ pentity = row.pr_pentity instance_type = pentity.instance_type item = object.__getattribute__(row, instance_type) if instance_type == "pr_person": if self.as_string: pe_str = current.T("private") else: pe_str = SPAN(current.T("private"), _class="free-hint") elif "name" in item: pe_str = s3_str(item["name"]) else: pe_str = "?" return pe_str
def get_sms_content(row): """ prepare the content for SMS """ from gluon.languages import lazyT itable = current.s3db.cap_info event_type_id = row["cap_info.event_type_id"] priority_id = row["cap_info.priority"] if not isinstance(event_type_id, lazyT): event_type = itable.event_type_id.represent(event_type_id) else: event_type = event_type_id if priority_id and priority_id != "-": if not isinstance(priority_id, lazyT): priority = itable.priority.represent(priority_id) else: priority = priority_id else: priority = T("None") sms_body = \ T("""%(Status)s %(MessageType)s for %(AreaDescription)s with %(Priority)s priority %(EventType)s issued by %(SenderName)s at %(Date)s (ID:%(Identifier)s)""") % \ dict(Status = s3_str(row["cap_alert.status"]), MessageType = s3_str(row["cap_alert.msg_type"]), AreaDescription = s3_str(row["cap_area.name"]), Priority = s3_str(priority), EventType = s3_str(event_type), SenderName = s3_str(row["cap_info.sender_name"]), Date = s3_str(row["cap_alert.sent"]), Identifier = s3_str(row["cap_alert.identifier"])) return sms_body
def inject_js(widget_id, options): """ Helper function to inject static JS and instantiate the foodRegistration widget @param widget_id: the node ID where to instantiate the widget @param options: dict of widget options (JSON-serializable) """ T = current.T s3 = current.response.s3 appname = current.request.application # Custom Ajax-URL options["ajaxURL"] = URL( c="dvr", f="case_event", args=["register_food.json"], ) options["selectAllText"] = s3_str(T("Select All")) # Custom JS scripts = s3.scripts # @Todo: minify? #if s3.debug: # script = "/%s/static/themes/DRK/js/food.js" % appname #else: # script = "/%s/static/themes/DRK/js/food.min.js" % appname script = "/%s/static/themes/DRK/js/food.js" % appname scripts.append(script) # Instantiate widget scripts = s3.jquery_ready script = '''$('#%(id)s').foodRegistration(%(options)s)''' % \ {"id": widget_id, "options": json.dumps(options)} if script not in scripts: scripts.append(script)
def inject_js(widget_id, options): """ Helper function to inject static JS and instantiate the foodRegistration widget @param widget_id: the node ID where to instantiate the widget @param options: dict of widget options (JSON-serializable) """ T = current.T s3 = current.response.s3 appname = current.request.application # Custom Ajax-URL options["ajaxURL"] = URL(c = "dvr", f = "case_event", args = ["register_food.json"], ) options["selectAllText"] = s3_str(T("Select All")) # Custom JS scripts = s3.scripts # @Todo: minify? #if s3.debug: # script = "/%s/static/themes/DRK/js/food.js" % appname #else: # script = "/%s/static/themes/DRK/js/food.min.js" % appname script = "/%s/static/themes/DRK/js/food.js" % appname scripts.append(script) # Instantiate widget scripts = s3.jquery_ready script = '''$('#%(id)s').foodRegistration(%(options)s)''' % \ {"id": widget_id, "options": json.dumps(options)} if script not in scripts: scripts.append(script)
def set_priority_js(): """ Output json for priority field """ wptable = s3db.cap_warning_priority rows = db(wptable).select(wptable.name, wptable.urgency, wptable.severity, wptable.certainty, wptable.color_code, orderby = wptable.name, ) from gluon.serializers import json as jsons from s3 import s3_str p_settings = [(s3_str(T(r.name)), r.urgency, r.severity, r.certainty, r.color_code)\ for r in rows] priority_conf = '''S3.cap_priorities=%s''' % jsons(p_settings) js_global = s3.js_global if not priority_conf in js_global: js_global.append(priority_conf) return
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) if len(rows) > 0: T = current.T etable = s3db.event_event_type ptable = s3db.cap_warning_priority filter_options = {} 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 == "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 == "language__belongs": languages = [s3_str(value) for value in values] if event_type is not None: display_text = "<b>%s:</b> %s" % (T("Event Type"), event_type) else: display_text = "<b>%s</b>" % (T("Event Type: None")) if len(priorities_id) > 0: display_text = "%s<br/><b>%s</b>: %s" % (display_text, T("Priorities"), priorities) else: display_text = "%s<br/><b>%s</b>" % (display_text, T("Priorities: None")) if len(languages) > 0: display_text = "%s<br/><b>%s</b>:%s" % (display_text, T("Languages"), languages) else: display_text = "%s<br/><b>%s</b>" % (display_text, T("Languages: None")) 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): """ prepare the content for html email """ from gluon.languages import lazyT itable = current.s3db.cap_info event_type_id = row["cap_info.event_type_id"] priority_id = row["cap_info.priority"] if not isinstance(event_type_id, lazyT): event_type = itable.event_type_id.represent(event_type_id) else: event_type = event_type_id if priority_id and \ priority_id != "-": if not isinstance(priority_id, lazyT): priority = itable.priority.represent(priority_id) else: priority = priority_id else: priority = T("None") category = itable.category.represent(row["cap_info.category"]) response_type = itable.response_type.represent(row["cap_info.response_type"]) subject = \ T("%(Scope)s %(Status)s Alert") % \ dict(Scope = s3_str(row["cap_alert.scope"]), Status = s3_str(row["cap_alert.status"])) headline = H2(T((s3_str(row["cap_info.headline"])))) id = T("ID: %(Identifier)s") % dict(Identifier = s3_str(row["cap_alert.identifier"])) body1 = \ T("""%(Priority)s message %(MessageType)s in effect for %(AreaDescription)s""") % dict(\ Priority = s3_str(priority), MessageType = s3_str(row["cap_alert.msg_type"]), AreaDescription = s3_str(row["cap_area.name"])) body2 = \ T("This %(Severity)s %(EventType)s is %(Urgency)s and is %(Certainty)s") %\ dict(Severity = s3_str(row["cap_info.severity"]), EventType = s3_str(event_type), Urgency = s3_str(row["cap_info.urgency"]), Certainty = s3_str(row["cap_info.certainty"])) body3 = \ T("""Message %(Identifier)s: %(EventType)s (%(Category)s) issued by %(SenderName)s sent at %(Date)s from %(Source)s""") % \ dict(Identifier = s3_str(row["cap_alert.identifier"]), EventType = s3_str(event_type), Category = s3_str(category), SenderName = s3_str(row["cap_info.sender_name"]), Date = s3_str(row["cap_alert.sent"]), Source = s3_str(row["cap_alert.source"])) body4 = T("Alert Description: %(AreaDescription)s") % \ dict(AreaDescription = s3_str(row["cap_area.name"])) body5 = T("Expected Response: %(ResponseType)s") % \ dict(ResponseType = s3_str(response_type)) body6 = T("Instructions: %(Instruction)s") % \ dict(Instruction=s3_str(row["cap_info.instruction"])) body7 = \ T("Alert is effective from %(Effective)s and expires on %(Expires)s") % \ dict(Effective = s3_str(row["cap_info.effective"]), Expires = s3_str(row["cap_info.expires"])) body8 = T("For more details visit %(URL)s or contact %(Contact)s") % \ dict(URL = s3_str(row["cap_info.web"]), Contact = s3_str(row["cap_info.contact"])) body9 = A(T("VIEW ALERT ON THE WEB"), _href = "%s/%s" % (s3_str(row["cap_info.web"]), "profile")) return TAG[""](HR(), BR(), body9, BR(), BR(), subject, headline, BR(), id, BR(), BR(), body1, BR(), body2, BR(), BR(), body3, BR(), BR(), body4, BR(), BR(), body5, BR(), BR(), body6, BR(), BR(), body7, BR(), BR(), body8, BR())
def registration_ajax(self, r, **attr): """ Ajax response method, expects a JSON input like: {l: the PE label (from the input field), c: boolean to indicate whether to just check the PE label or to register payments t: the event type code x: [array of PE labels, only when registering for multiple persons ] } @param r: the S3Request instance @param attr: controller parameters @return: JSON response, structure: {l: the actual PE label (to update the input field), p: the person details, d: the family details, f: [{n: the flag name i: the flag instructions }, ...], b: profile picture URL, i: {<event_code>: [<msg>, <blocked_until_datetime>]}, x: [{l: the family member PE label, n: the family member full name, d: the family member date of birth, p: the family member profile picture URL, r: {<event_code>: [<msg>, <blocked_until_datetime>]}, }, ], s: whether the action is permitted or not e: form error (for label field) a: error message w: warning message m: success message } """ T = current.T s3db = current.s3db # Load JSON data from request body s = r.body s.seek(0) try: data = json.load(s) except (ValueError, TypeError): r.error(400, current.ERROR.BAD_REQUEST) # Initialize processing variables output = {} error = None alert = None message = None warning = None permitted = False flags = [] # Identify the person pe_label = data.get("l") person = self.get_person(pe_label) if person is None: error = s3_str(T("No person found with this ID number")) else: # Get flag info flag_info = s3db.dvr_get_flag_instructions(person.id, action = "id-check", ) permitted = flag_info["permitted"] check = data.get("c") if check: output["l"] = person.pe_label # Person details person_details = self.person_details(person) output["p"] = s3_str(person_details) # Profile picture profile_picture = self.profile_picture(person) output["b"] = profile_picture # Household Size details = s3db.dvr_get_household_size(person.id, dob = person.date_of_birth, ) if details: output["d"] = {"d": details} # Family Members family_members = self.get_family_members(person) if family_members: output["x"] = family_members # Flag Info info = flag_info["info"] for flagname, instructions in info: flags.append({"n": s3_str(T(flagname)), "i": s3_str(T(instructions)), }) # Blocking periods for events event_types = self.get_event_types() blocked = self.get_blocked_events(person.id) intervals = {} for type_id, info in blocked.items(): event_type = event_types.get(type_id) if not event_type: continue code = event_type.code msg, dt = info intervals[code] = (s3_str(msg), "%sZ" % s3_encode_iso_datetime(dt), ) output["i"] = intervals else: # Check event code and permission type_id = None event_code = data.get("t") if not event_code: alert = T("No event type specified") elif not permitted: alert = T("Event registration not permitted") else: event_type = self.get_event_type(event_code) if not event_type: alert = T("Invalid event type: %s") % event_code else: type_id = event_type.id if type_id: family_labels = data.get("x") if family_labels: # Register event for multiple family members # Get family members and interval rules family_members = self.get_family_members(person, include_ids = True, ) family_members = dict((m["l"], m) for m in family_members) registered = 0 alerts = [] for label in family_labels: # Get the family member person_id member = family_members.get(label) if not member: continue member_id = member.get("id") # Check interval rules rules = member.get("r") if rules and event_code in rules: # Event type is currently blocked for this # family member alerts.append(": ".join((s3_str(member["n"]), s3_str(rules[event_code][0]), ))) else: # Ok - register the event self.register_event(member_id, type_id) registered += 1 if alerts: alert = ", ".join(alerts) if registered: message = T("%(number)s registrations successful") % \ {"number": registered} elif not alerts: alert = T("Could not register event") else: # Check whether event type is blocked for this person person_id = person.id blocked = self.get_blocked_events(person_id, type_id = type_id, ) if type_id in blocked: # Event type is currently blocked for this person alert = blocked[type_id][0] else: # Ok - register the event success = self.register_event(person.id, type_id) if success: message = T("Event registered") else: alert = T("Could not register event") # Add messages to output if alert: output["a"] = s3_str(alert) if error: output["e"] = s3_str(error) if message: output["m"] = s3_str(message) if warning: output["w"] = s3_str(warning) # Add flag info to output output["s"] = permitted output["f"] = flags current.response.headers["Content-Type"] = "application/json" return json.dumps(output)
def needs_by_district(cls): """ Count need lines per district and status (top 5 districts) - for all open Events """ T = current.T db = current.db s3db = current.s3db table = s3db.req_need_line ntable = s3db.req_need etable = s3db.event_event ltable = s3db.event_event_need status = table.status number = table.id.count() location = ntable.location_id base_query = (etable.closed == False) & \ (etable.id == ltable.event_id) & \ (ltable.need_id == ntable.id) & \ (ntable.id == table.need_id) & \ (table.deleted == False) # Get the top-5 locations by number of need lines query = base_query & (location != None) rows = db(query).select( location, number, groupby=location, orderby=~(number), limitby=(0, 5), ) locations = [row[location] for row in rows] data = [] if locations: # Get labels for locations location_represent = S3Represent(lookup="gis_location", fields=["L2"]) location_labels = location_represent.bulk(locations) # Count need lines per status and location query = base_query & (location.belongs(locations)) rows = db(query).select( location, status, number, groupby=(status, location), ) # Group results as {status: {location: number}} per_status = {} for row in rows: row_status = row[status] if row_status in per_status: per_status[row_status][row[location]] = row[number] else: per_status[row_status] = {row[location]: row[number]} # Build data structure for chart renderer # - every status gives a series # - every district gives a series entry for code, label, color in cls.REQ_STATUS: series = { "key": s3_str(T(label)), "color": color, "filterKey": code, } values = [] per_location = per_status.get(code) for location_id in locations: if per_location: value = per_location.get(location_id) else: value = None location_label = location_labels.get(location_id) item = { "label": location_label, "value": value if value else 0, "filterKey": location_label, } values.append(item) series["values"] = values data.append(series) return data
def get_family_members(self, person, include_ids=False): """ Get infos for all family members of person @param person: the person (Row) @param include_ids: include the person record IDs @returns: array with family member infos, format: [{i: the person record ID (if requested) l: pe_label, n: fullname, d: dob_formatted, p: picture_URL, r: { event_code: { m: message, e: earliest_date_ISO } }, ... ] """ db = current.db s3db = current.s3db ptable = s3db.pr_person itable = s3db.pr_image gtable = s3db.pr_group mtable = s3db.pr_group_membership ctable = s3db.dvr_case stable = s3db.dvr_case_status # Get all case groups this person belongs to person_id = person.id query = ((mtable.person_id == person_id) & \ (mtable.deleted != True) & \ (gtable.id == mtable.group_id) & \ (gtable.group_type == 7)) rows = db(query).select(gtable.id) group_ids = set(row.id for row in rows) members = {} if group_ids: join = [ptable.on(ptable.id == mtable.person_id), ctable.on((ctable.person_id == ptable.id) & \ (ctable.archived == False) & \ (ctable.deleted == False)), ] left = [stable.on(stable.id == ctable.status_id), itable.on((itable.pe_id == ptable.pe_id) & \ (itable.profile == True) & \ (itable.deleted == False)), ] query = (mtable.group_id.belongs(group_ids)) & \ (mtable.deleted != True) & \ (stable.is_closed != True) rows = db(query).select(ptable.id, ptable.pe_label, ptable.first_name, ptable.last_name, ptable.date_of_birth, itable.image, join = join, left = left, ) for row in rows: member_id = row.pr_person.id if member_id not in members: members[member_id] = row output = [] if members: # All event types and blocking rules event_types = self.get_event_types() intervals = self.get_interval_rules(set(members.keys())) for member_id, data in members.items(): member = data.pr_person picture = data.pr_image # Person data data = {"l": member.pe_label, "n": s3_fullname(member), "d": S3DateTime.date_represent(member.date_of_birth), } # Record ID? if include_ids: data["id"] = member_id # Profile picture URL if picture.image: data["p"] = URL(c = "default", f = "download", args = picture.image, ) # Blocking rules event_rules = intervals.get(member_id) if event_rules: rules = {} for event_type_id, rule in event_rules.items(): code = event_types.get(event_type_id).code rules[code] = (s3_str(rule[0]), "%sZ" % s3_encode_iso_datetime(rule[1]), ) data["r"] = rules # Add info to output output.append(data) return output
itable.web) if record["scope"] != "Private" and len(rows_): # 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] for row_ in rows_: title = "[%s] %s %s" % (row_.cap_info.sender_name, itable.event_type_id.represent(row_.cap_info.event_type_id), itable.priority.represent(row_.cap_info.priority), ) async_task("cap_gcm", args=[title, "%s/%s" % (s3_str(row_.cap_info.web), "profile"), s3_str(row_.cap_info.headline), json.dumps(registration_ids), ]) # Twitter Post if settings.get_cap_post_to_twitter(): try: import tweepy except ImportError:
def get_interval_rules(self, person_ids): """ Get interval (blocking) rules for persons @param person_ids: list|tuple|set of person record IDs @return: rules dict, format: {person_id: {event_type_id: (message, earliest_datetime), ... }, ... } """ T = current.T db = current.db s3db = current.s3db now = current.request.utcnow day_start = now.replace(hour=0, minute=0, second=0, microsecond=0, ) next_day = day_start + datetime.timedelta(days=1) output = {} table = s3db.dvr_case_event event_type_id = table.type_id person_id = table.person_id # Get event types to check event_types = self.get_event_types() # Check impermissible combinations etable = s3db.dvr_case_event_exclusion query = (table.person_id.belongs(person_ids)) & \ (table.date >= day_start) & \ (table.deleted == False) & \ (etable.excluded_by_id == table.type_id) & \ (etable.deleted == False) rows = db(query).select(table.person_id, etable.type_id, etable.excluded_by_id, ) collisions = {} for row in rows: event = row.dvr_case_event exclusion = row.dvr_case_event_exclusion pid = event.person_id if pid in collisions: excluded = collisions[pid] else: excluded = collisions[pid] = {} tid = exclusion.type_id if tid in excluded: excluded[tid].append(exclusion.excluded_by_id) else: excluded[tid] = [exclusion.excluded_by_id] for pid, excluded in collisions.items(): if pid not in output: rules = output[pid] = {} else: rules = output[pid] for tid, excluded_by_ids in excluded.items(): event_type = event_types.get(tid) if not event_type: continue excluded_by_names = [] seen = set() for excluded_by_id in excluded_by_ids: if excluded_by_id in seen: continue else: seen.add(excluded_by_id) excluded_by_type = event_types.get(excluded_by_id) if not excluded_by_type: continue excluded_by_names.append(s3_str(T(excluded_by_type.name))) if excluded_by_names: msg = T("%(event)s already registered today, not combinable") % \ {"event": ", ".join(excluded_by_names) } rules[tid] = (msg, next_day) # Helper function to build event type sub-query def type_query(items): if len(items) == 1: return (event_type_id == items[0]) elif items: return (event_type_id.belongs(set(items))) else: return None # Check maximum occurences per day check = [tid for tid, row in event_types.items() if row.max_per_day and tid != "_default" ] q = type_query(check) if q is not None: # Get number of events per type and person today cnt = table.id.count() query = (table.person_id.belongs(person_ids)) & q & \ (table.date >= day_start) & \ (table.deleted != True) rows = db(query).select(person_id, event_type_id, cnt, groupby = (person_id, event_type_id), ) # Check limit for row in rows: number = row[cnt] pid = row[person_id] if pid not in output: rules = output[pid] = {} else: rules = output[pid] tid = row[event_type_id] if tid in rules: continue event_type = event_types[tid] limit = event_type.max_per_day if number >= limit: if number > 1: msg = T("%(event)s already registered %(number)s times today") % \ {"event": T(event_type.name), "number": number, } else: msg = T("%(event)s already registered today") % \ {"event": T(event_type.name), } rules[tid] = (msg, next_day) # Check minimum intervals check = [tid for tid, row in event_types.items() if row.min_interval and tid != "_default" ] q = type_query(check) if q is not None: # Get the last events for these types per person query = (table.person_id.belongs(person_ids)) & q & \ (table.deleted != True) timestamp = table.date.max() rows = db(query).select(person_id, event_type_id, timestamp, groupby = (person_id, event_type_id), ) # Check intervals represent = table.date.represent for row in rows: latest = row[timestamp] pid = row[person_id] if pid not in output: rules = output[pid] = {} else: rules = output[pid] tid = row[event_type_id] if tid in rules: continue event_type = event_types[tid] interval = event_type.min_interval if latest: earliest = latest + datetime.timedelta(hours=interval) if earliest > now: msg = T("%(event)s already registered on %(timestamp)s") % \ {"event": T(event_type.name), "timestamp": represent(latest), } rules[tid] = (msg, earliest) return output
def xls(self, r, **attr): """ Export the performance indicators as XLS data sheet @param r: the S3Request instance @param attr: controller attributes """ T = current.T s3db = current.s3db try: import xlwt except ImportError: raise HTTP(503, body="XLWT not installed") title = s3_str(T("Performance Indicators")) write = self.write # Create workbook and sheet book = xlwt.Workbook(encoding="utf-8") sheet = book.add_sheet(title) # Get the statistics resource = self.resource table = resource.table indicators = self.indicators(resource) # Title and Report Dates (from filter) write(sheet, 0, 0, title, style="header") dates = [] get_vars = r.get_vars field = table.date for fvar in ("~.date__ge", "~.date__le"): dtstr = get_vars.get(fvar) if dtstr: try: dt = s3_decode_iso_datetime(dtstr).date() except (ValueError, AttributeError): dt = None else: dates.append(field.represent(dt)) else: dates.append("...") if dates: write(sheet, 1, 0, " -- ".join(dates)) # Basic performance indicators rowindex = 3 # Total number of consultations write(sheet, rowindex, 0, T("Total Number of Consultations")) write(sheet, rowindex, 1, indicators.get("total_responses", "")) rowindex += 1 write(sheet, rowindex, 0, T("Total Number of Clients")) write(sheet, rowindex, 1, indicators.get("total_clients", "")) rowindex += 1 write(sheet, rowindex, 0, T("Average Duration of Consultations (minutes)")) avg_hours_per_response = indicators.get("avg_hours_per_response") if avg_hours_per_response: avg_minutes_per_response = int(round(avg_hours_per_response * 60)) else: avg_minutes_per_response = "" write(sheet, rowindex, 1, avg_minutes_per_response) rowindex += 1 write(sheet, rowindex, 0, T("Average Number of Consultations per Client")) write(sheet, rowindex, 1, indicators.get("avg_responses_per_client", "")) # Distribution rowindex = 8 write(sheet, rowindex, 0, T("Distribution of Clients")) write(sheet, rowindex, 1, T("Single")) write(sheet, rowindex, 2, indicators.get("singles", "")) rowindex += 1 write(sheet, rowindex, 1, T("Family")) write(sheet, rowindex, 2, indicators.get("families", "")) rowindex += 1 write(sheet, rowindex, 1, T("Group Counseling")) rowindex += 1 write(sheet, rowindex, 1, T("Individual Counseling")) write(sheet, rowindex, 2, indicators.get("total_responses", "")) # Top-5's rowindex = 13 write(sheet, rowindex, 0, T("Top 5 Countries of Origin")) top_5_nationalities = indicators.get("top_5_nationalities") if top_5_nationalities: dtable = s3db.pr_person_details field = dtable.nationality for rank, nationality in enumerate(top_5_nationalities): write(sheet, rowindex, 1, "%s - %s" % (rank + 1, field.represent(nationality))) rowindex += 1 rowindex += 1 write(sheet, rowindex, 0, T("Top 5 Counseling Reasons")) top_5_needs = indicators.get("top_5_needs") if top_5_needs: ttable = s3db.dvr_response_theme field = ttable.need_id for rank, need in enumerate(top_5_needs): write(sheet, rowindex, 1, "%s - %s" % (rank + 1, field.represent(need))) rowindex += 1 # Output output = StringIO() book.save(output) output.seek(0) # Response headers from gluon.contenttype import contenttype disposition = "attachment; filename=\"%s\"" % "indicators.xls" response = current.response response.headers["Content-Type"] = contenttype(".xls") response.headers["Content-disposition"] = disposition from gluon.streamer import DEFAULT_CHUNK_SIZE return response.stream(output, chunk_size=DEFAULT_CHUNK_SIZE, request=r, )
def draw(self): """ Draw the card (one side) Instance attributes (NB draw-function should not modify them): - self.canv...............the canvas (provides the drawing methods) - self.resource...........the resource - self.item...............the data item (dict) - self.labels.............the field labels (dict) - self.backside...........this instance should render the backside of a card - self.multiple...........there are multiple cards per page - self.width..............the width of the card (in points) - self.height.............the height of the card (in points) NB Canvas coordinates are relative to the lower left corner of the card's frame, drawing must not overshoot self.width/self.height """ T = current.T c = self.canv w = self.width #h = self.height common = self.common orange = HexColor(0xEE4229) item = self.item raw = item["_row"] # Get the org logo logos = common.get("logos") if logos: root_org = raw["org_organisation.root_organisation"] logo = logos.get(root_org) else: logo = None if not self.backside: draw_field = self.draw_field draw_value = self.draw_value draw_label = self.draw_label # Horizontal alignments LEFT = w / 4 - 5 CENTER = w / 2 - 5 RIGHT = w * 3 / 4 - 5 # Vertical alignments TOP = 200 LOWER = [76, 58, 40] BOTTOM = 16 # Org Logo if logo: self.draw_image(logo, LEFT, TOP, width=60, height=60, valign="middle", halign="center") # Get the profile picture pictures = common.get("pictures") if pictures: picture = pictures.get(raw["pr_person.pe_id"]) if picture: self.draw_image(picture, RIGHT, TOP, width=60, height=60, valign="middle", halign="center") # Center fields in reverse order so that vertical positions # can be adjusted for very long and hence wrapping strings y = 98 # Organisation/Branch org_name = s3_str(item.get("org_organisation.name")) aH = draw_value(CENTER, y, org_name, height=16, size=8) draw_label(CENTER, y, "hrm_human_resource.organisation_id") # Job Title y += aH + 12 job_title = s3_str(item.get("hrm_human_resource.job_title_id")) aH = draw_value(CENTER, y, job_title, height=16, size=8) draw_label(CENTER, y, "hrm_human_resource.job_title_id") # Name y += aH + 12 name = s3_format_fullname(fname = raw["pr_person.first_name"], mname = raw["pr_person.middle_name"], lname = raw["pr_person.last_name"], truncate = False, ) draw_value(CENTER, y, name, height=24, size=10) draw_label(CENTER, y, None, T("Name")) # IDs draw_field(LEFT, LOWER[0], "pr_national_id_identity.value") draw_label(LEFT, LOWER[0], None, T("ID")) # TODO only volunteers can have this? (Adjust label by HR type?) draw_field(RIGHT, LOWER[0], "hrm_human_resource.code") draw_label(RIGHT, LOWER[0], None, T("Volunteer ID")) # Medical Details draw_field(LEFT, LOWER[1], "pr_physical_description.blood_type") draw_label(LEFT, LOWER[1], None, T("Blood Type")) draw_field(RIGHT, LOWER[1], "pr_physical_description.allergic") draw_label(RIGHT, LOWER[1], None, T("Allergic")) # Issuing/Expirey Dates # TODO adjust interval per org (org_idcard) today = current.request.now.date() format_date = current.calendar.format_date issued_on = format_date(today) expires_on = format_date(today + relativedelta(months=24)) c.setFont(BOLD, 7) c.drawCentredString(LEFT, LOWER[2], issued_on) draw_label(LEFT, LOWER[2], None, T("Issued on")) c.setFont(BOLD, 7) c.drawCentredString(RIGHT, LOWER[2], expires_on) draw_label(RIGHT, LOWER[2], None, T("Expires on")) # Barcode code = raw["hrm_human_resource.code"] if code: self.draw_barcode(s3_str(code), CENTER, BOTTOM, height=12, halign="center") # Graphics c.setFillColor(orange) c.rect(0, 0, w, 12, fill=1, stroke=0) c.rect(w - 12, 0, 12, 154, fill=1, stroke=0) # Add a utting line with multiple cards per page if self.multiple: c.setDash(1, 2) self.draw_outline() else: # Horizontal alignments CENTER = w / 2 # Vertical alignments TOP = 200 BOTTOM = 16 # TODO Mission statement # TODO IFRC membership statement # TODO Signature and caption # Barcode code = raw["hrm_human_resource.code"] if code: self.draw_barcode(s3_str(code), CENTER, BOTTOM, height=12, halign="center") # Graphics if logo: self.draw_image(logo, CENTER, TOP, width=60, height=60, valign="middle", halign="center") c.setFillColor(orange) c.rect(0, 0, w, 10, fill=1, stroke=0)
def needs_by_district(cls): """ Count need lines per district and status (top 5 districts) - for all open Events """ T = current.T db = current.db s3db = current.s3db table = s3db.req_need_line ntable = s3db.req_need etable = s3db.event_event ltable = s3db.event_event_need status = table.status number = table.id.count() location = ntable.location_id base_query = (etable.closed == False) & \ (etable.id == ltable.event_id) & \ (ltable.need_id == ntable.id) & \ (ntable.id == table.need_id) & \ (table.deleted == False) # Get the top-5 locations by number of need lines query = base_query & (location != None) rows = db(query).select(location, number, groupby = location, orderby = ~(number), limitby = (0, 5), ) locations = [row[location] for row in rows] data = [] if locations: # Get labels for locations location_represent = S3Represent(lookup="gis_location", fields=["L2"]) location_labels = location_represent.bulk(locations) # Count need lines per status and location query = base_query & (location.belongs(locations)) rows = db(query).select(location, status, number, groupby = (status, location), ) # Group results as {status: {location: number}} per_status = {} for row in rows: row_status = row[status] if row_status in per_status: per_status[row_status][row[location]] = row[number] else: per_status[row_status] = {row[location]: row[number]} # Build data structure for chart renderer # - every status gives a series # - every district gives a series entry for code, label, color in cls.REQ_STATUS: series = {"key": s3_str(T(label)), "color": color, "filterKey": code, } values = [] per_location = per_status.get(code) for location_id in locations: if per_location: value = per_location.get(location_id) else: value = None location_label = location_labels.get(location_id) item = {"label": location_label, "value": value if value else 0, "filterKey": location_label, } values.append(item) series["values"] = values data.append(series) return data
def get_interval_rules(self, person_ids): """ Get interval (blocking) rules for persons @param person_ids: list|tuple|set of person record IDs @return: rules dict, format: {person_id: {event_type_id: (message, earliest_datetime), ... }, ... } """ T = current.T db = current.db s3db = current.s3db now = current.request.utcnow day_start = now.replace( hour=0, minute=0, second=0, microsecond=0, ) next_day = day_start + datetime.timedelta(days=1) output = {} table = s3db.dvr_case_event event_type_id = table.type_id person_id = table.person_id # Get event types to check event_types = self.get_event_types() # Check impermissible combinations etable = s3db.dvr_case_event_exclusion query = (table.person_id.belongs(person_ids)) & \ (table.date >= day_start) & \ (table.deleted == False) & \ (etable.excluded_by_id == table.type_id) & \ (etable.deleted == False) rows = db(query).select( table.person_id, etable.type_id, etable.excluded_by_id, ) collisions = {} for row in rows: event = row.dvr_case_event exclusion = row.dvr_case_event_exclusion pid = event.person_id if pid in collisions: excluded = collisions[pid] else: excluded = collisions[pid] = {} tid = exclusion.type_id if tid in excluded: excluded[tid].append(exclusion.excluded_by_id) else: excluded[tid] = [exclusion.excluded_by_id] for pid, excluded in collisions.items(): if pid not in output: rules = output[pid] = {} else: rules = output[pid] for tid, excluded_by_ids in excluded.items(): event_type = event_types.get(tid) if not event_type: continue excluded_by_names = [] seen = set() for excluded_by_id in excluded_by_ids: if excluded_by_id in seen: continue else: seen.add(excluded_by_id) excluded_by_type = event_types.get(excluded_by_id) if not excluded_by_type: continue excluded_by_names.append(s3_str(T(excluded_by_type.name))) if excluded_by_names: msg = T("%(event)s already registered today, not combinable") % \ {"event": ", ".join(excluded_by_names) } rules[tid] = (msg, next_day) # Helper function to build event type sub-query def type_query(items): if len(items) == 1: return (event_type_id == items[0]) elif items: return (event_type_id.belongs(set(items))) else: return None # Check maximum occurences per day check = [ tid for tid, row in event_types.items() if row.max_per_day and tid != "_default" ] q = type_query(check) if q is not None: # Get number of events per type and person today cnt = table.id.count() query = (table.person_id.belongs(person_ids)) & q & \ (table.date >= day_start) & \ (table.deleted != True) rows = db(query).select( person_id, event_type_id, cnt, groupby=(person_id, event_type_id), ) # Check limit for row in rows: number = row[cnt] pid = row[person_id] if pid not in output: rules = output[pid] = {} else: rules = output[pid] tid = row[event_type_id] if tid in rules: continue event_type = event_types[tid] limit = event_type.max_per_day if number >= limit: if number > 1: msg = T("%(event)s already registered %(number)s times today") % \ {"event": T(event_type.name), "number": number, } else: msg = T("%(event)s already registered today") % \ {"event": T(event_type.name), } rules[tid] = (msg, next_day) # Check minimum intervals check = [ tid for tid, row in event_types.items() if row.min_interval and tid != "_default" ] q = type_query(check) if q is not None: # Get the last events for these types per person query = (table.person_id.belongs(person_ids)) & q & \ (table.deleted != True) timestamp = table.date.max() rows = db(query).select( person_id, event_type_id, timestamp, groupby=(person_id, event_type_id), ) # Check intervals represent = table.date.represent for row in rows: latest = row[timestamp] pid = row[person_id] if pid not in output: rules = output[pid] = {} else: rules = output[pid] tid = row[event_type_id] if tid in rules: continue event_type = event_types[tid] interval = event_type.min_interval if latest: earliest = latest + datetime.timedelta(hours=interval) if earliest > now: msg = T("%(event)s already registered on %(timestamp)s") % \ {"event": T(event_type.name), "timestamp": represent(latest), } rules[tid] = (msg, earliest) return output
def get_family_members(self, person, include_ids=False): """ Get infos for all family members of person @param person: the person (Row) @param include_ids: include the person record IDs @returns: array with family member infos, format: [{i: the person record ID (if requested) l: pe_label, n: fullname, d: dob_formatted, p: picture_URL, r: { event_code: { m: message, e: earliest_date_ISO } }, ... ] """ db = current.db s3db = current.s3db ptable = s3db.pr_person itable = s3db.pr_image gtable = s3db.pr_group mtable = s3db.pr_group_membership ctable = s3db.dvr_case stable = s3db.dvr_case_status # Get all case groups this person belongs to person_id = person.id query = ((mtable.person_id == person_id) & \ (mtable.deleted != True) & \ (gtable.id == mtable.group_id) & \ (gtable.group_type == 7)) rows = db(query).select(gtable.id) group_ids = set(row.id for row in rows) members = {} if group_ids: join = [ptable.on(ptable.id == mtable.person_id), ctable.on((ctable.person_id == ptable.id) & \ (ctable.archived == False) & \ (ctable.deleted == False)), ] left = [stable.on(stable.id == ctable.status_id), itable.on((itable.pe_id == ptable.pe_id) & \ (itable.profile == True) & \ (itable.deleted == False)), ] query = (mtable.group_id.belongs(group_ids)) & \ (mtable.deleted != True) & \ (stable.is_closed != True) rows = db(query).select( ptable.id, ptable.pe_label, ptable.first_name, ptable.last_name, ptable.date_of_birth, itable.image, join=join, left=left, ) for row in rows: member_id = row.pr_person.id if member_id not in members: members[member_id] = row output = [] if members: # All event types and blocking rules event_types = self.get_event_types() intervals = self.get_interval_rules(set(members.keys())) for member_id, data in members.items(): member = data.pr_person picture = data.pr_image # Person data data = { "l": member.pe_label, "n": s3_fullname(member), "d": S3DateTime.date_represent(member.date_of_birth), } # Record ID? if include_ids: data["id"] = member_id # Profile picture URL if picture.image: data["p"] = URL( c="default", f="download", args=picture.image, ) # Blocking rules event_rules = intervals.get(member_id) if event_rules: rules = {} for event_type_id, rule in event_rules.items(): code = event_types.get(event_type_id).code rules[code] = ( s3_str(rule[0]), "%sZ" % s3_encode_iso_datetime(rule[1]), ) data["r"] = rules # Add info to output output.append(data) return output
def registration_ajax(self, r, **attr): """ Ajax response method, expects a JSON input like: {l: the PE label (from the input field), c: boolean to indicate whether to just check the PE label or to register payments t: the event type code x: [array of PE labels, only when registering for multiple persons ] } @param r: the S3Request instance @param attr: controller parameters @return: JSON response, structure: {l: the actual PE label (to update the input field), p: the person details, d: the family details, f: [{n: the flag name i: the flag instructions }, ...], b: profile picture URL, i: {<event_code>: [<msg>, <blocked_until_datetime>]}, x: [{l: the family member PE label, n: the family member full name, d: the family member date of birth, p: the family member profile picture URL, r: {<event_code>: [<msg>, <blocked_until_datetime>]}, }, ], s: whether the action is permitted or not e: form error (for label field) a: error message w: warning message m: success message } """ T = current.T s3db = current.s3db # Load JSON data from request body s = r.body s.seek(0) try: data = json.load(s) except (ValueError, TypeError): r.error(400, current.ERROR.BAD_REQUEST) # Initialize processing variables output = {} error = None alert = None message = None warning = None permitted = False flags = [] # Identify the person pe_label = data.get("l") person = self.get_person(pe_label) if person is None: error = s3_str(T("No person found with this ID number")) else: # Get flag info flag_info = s3db.dvr_get_flag_instructions( person.id, action="id-check", ) permitted = flag_info["permitted"] check = data.get("c") if check: output["l"] = person.pe_label # Person details person_details = self.person_details(person) output["p"] = s3_str(person_details) # Profile picture profile_picture = self.profile_picture(person) output["b"] = profile_picture # Household Size details = s3db.dvr_get_household_size( person.id, dob=person.date_of_birth, ) if details: output["d"] = {"d": details} # Family Members family_members = self.get_family_members(person) if family_members: output["x"] = family_members # Flag Info info = flag_info["info"] for flagname, instructions in info: flags.append({ "n": s3_str(T(flagname)), "i": s3_str(T(instructions)), }) # Blocking periods for events event_types = self.get_event_types() blocked = self.get_blocked_events(person.id) intervals = {} for type_id, info in blocked.items(): event_type = event_types.get(type_id) if not event_type: continue code = event_type.code msg, dt = info intervals[code] = ( s3_str(msg), "%sZ" % s3_encode_iso_datetime(dt), ) output["i"] = intervals else: # Check event code and permission type_id = None event_code = data.get("t") if not event_code: alert = T("No event type specified") elif not permitted: alert = T("Event registration not permitted") else: event_type = self.get_event_type(event_code) if not event_type: alert = T("Invalid event type: %s") % event_code else: type_id = event_type.id if type_id: family_labels = data.get("x") if family_labels: # Register event for multiple family members # Get family members and interval rules family_members = self.get_family_members( person, include_ids=True, ) family_members = dict( (m["l"], m) for m in family_members) registered = 0 alerts = [] for label in family_labels: # Get the family member person_id member = family_members.get(label) if not member: continue member_id = member.get("id") # Check interval rules rules = member.get("r") if rules and event_code in rules: # Event type is currently blocked for this # family member alerts.append(": ".join(( s3_str(member["n"]), s3_str(rules[event_code][0]), ))) else: # Ok - register the event self.register_event(member_id, type_id) registered += 1 if alerts: alert = ", ".join(alerts) if registered: message = T("%(number)s registrations successful") % \ {"number": registered} elif not alerts: alert = T("Could not register event") else: # Check whether event type is blocked for this person person_id = person.id blocked = self.get_blocked_events( person_id, type_id=type_id, ) if type_id in blocked: # Event type is currently blocked for this person alert = blocked[type_id][0] else: # Ok - register the event success = self.register_event(person.id, type_id) if success: message = T("Event registered") else: alert = T("Could not register event") # Add messages to output if alert: output["a"] = s3_str(alert) if error: output["e"] = s3_str(error) if message: output["m"] = s3_str(message) if warning: output["w"] = s3_str(warning) # Add flag info to output output["s"] = permitted output["f"] = flags current.response.headers["Content-Type"] = "application/json" return json.dumps(output)
def draw(self): """ Draw the card (one side) Instance attributes (NB draw-function should not modify them): - self.canv...............the canvas (provides the drawing methods) - self.resource...........the resource - self.item...............the data item (dict) - self.labels.............the field labels (dict) - self.backside...........this instance should render the backside of a card - self.multiple...........there are multiple cards per page - self.width..............the width of the card (in points) - self.height.............the height of the card (in points) NB Canvas coordinates are relative to the lower left corner of the card's frame, drawing must not overshoot self.width/self.height """ T = current.T c = self.canv w = self.width #h = self.height common = self.common blue = HexColor(0x27548F) item = self.item raw = item["_row"] root_org = raw["org_organisation.root_organisation"] # Get the localized root org name org_names = common.get("root_org_names") if org_names: root_org_name = org_names.get(root_org) #draw_field = self.draw_field draw_value = self.draw_value draw_label = self.draw_label code = raw["pr_person.pe_label"] if not self.backside: # Horizontal alignments LEFT = w / 4 - 5 CENTER = w / 2 - 5 RIGHT = w * 3 / 4 - 5 # Vertical alignments TOP = 200 #LOWER = [76, 58, 40] BOTTOM = 16 # Organisation name if root_org_name: draw_value(LEFT, TOP, root_org_name, width = 55, height = 55, size = 10, valign = "middle", ) # Get the profile picture pictures = common.get("pictures") if pictures: picture = pictures.get(raw["pr_person.pe_id"]) if picture: self.draw_image(picture, RIGHT, TOP, width = 60, height = 55, valign = "middle", halign = "center", ) # Center fields in reverse order so that vertical positions # can be adjusted for very long and hence wrapping strings y = 98 # ID ah = draw_value(CENTER, y, code, height=24, size=8) draw_label(CENTER, y, None, T("ID Number")) # Name y += ah + 12 name = s3_format_fullname(fname = raw["pr_person.first_name"], mname = raw["pr_person.middle_name"], lname = raw["pr_person.last_name"], truncate = False, ) draw_value(CENTER, y, name, height=24, size=10) draw_label(CENTER, y, None, T("Name")) # Barcode if code: self.draw_barcode(s3_str(code), CENTER, BOTTOM, height = 12, halign = "center", maxwidth = w - 15, ) # Graphics c.setFillColor(blue) c.rect(0, 0, w, 12, fill=1, stroke=0) c.rect(w - 12, 0, 12, 154, fill=1, stroke=0) # Add a utting line with multiple cards per page if self.multiple: c.setDash(1, 2) self.draw_outline() else: # Horizontal alignments CENTER = w / 2 # Vertical alignments TOP = 200 MIDDLE = 85 BOTTOM = 16 # QR Code if code: identity = "%s//%s:%s:%s" % (code, raw["pr_person.first_name"] or "", raw["pr_person.middle_name"] or "", raw["pr_person.last_name"] or "", ) self.draw_qrcode(identity, CENTER, MIDDLE, size=60, halign="center", valign="center") # Barcode if code: self.draw_barcode(s3_str(code), CENTER, BOTTOM, height = 12, halign = "center", maxwidth = w - 15 ) # Graphics c.setFillColor(blue) c.rect(0, 0, w, 10, fill=1, stroke=0)
def get_html_email_content(row): """ prepare the content for html email """ subject = \ T("%(Scope)s %(Status)s Alert: %(Headline)s (ID: %(Identifier)s)") % \ dict(Scope = s3_str(row["cap_alert.scope"]), Status = s3_str(row["cap_alert.status"]), Headline = s3_str(row["cap_info.headline"]), Identifier = s3_str(row["cap_alert.identifier"])) body1 = \ T("""%(Priority)s priority %(MessageType)s message in effect for %(AreaDescription)s""") % dict(\ Priority = s3_str(row["cap_info.priority"]), MessageType = s3_str(row["cap_alert.msg_type"]), AreaDescription = s3_str(row["cap_area.name"])) body2 = \ T("This %(Severity)s %(EventType)s is %(Urgency)s and is %(Certainty)s") %\ dict(Severity = s3_str(row["cap_info.severity"]), EventType = s3_str(row["cap_info.event_type_id"]), Urgency = s3_str(row["cap_info.urgency"]), Certainty = s3_str(row["cap_info.certainty"])) body3 = \ T("""Message %(Identifier)s: %(EventType)s (%(Category)s) issued by %(SenderName)s sent at %(Date)s from %(Source)s""") % \ dict(Identifier = s3_str(row["cap_alert.identifier"]), EventType = s3_str(row["cap_info.event_type_id"]), Category = s3_str(row["cap_info.category"]), SenderName = s3_str(row["cap_info.sender_name"]), Date = s3_str(row["cap_alert.sent"]), Source = s3_str(row["cap_alert.source"])) body4 = T("Alert Description: %(AreaDescription)s") % \ dict(AreaDescription = s3_str(row["cap_area.name"])) body5 = T("Expected Response: %(ResponseType)s") % \ dict(ResponseType = s3_str(row["cap_info.response_type"])) body6 = T("Instructions: %(Instruction)s") % \ dict(Instruction=s3_str(row["cap_info.instruction"])) body7 = \ T("Alert is effective from %(Effective)s and expires on %(Expires)s") % \ dict(Effective = s3_str(row["cap_info.effective"]), Expires = s3_str(row["cap_info.expires"])) body8 = T("For more details visit %(URL)s or contact %(Contact)s") % \ dict(URL = s3_str(row["cap_info.web"]), Contact = s3_str(row["cap_info.contact"])) body9 = A(T("VIEW ALERT ON THE WEB"), _href = URL(s3_str(row["cap_info.web"]), "/profile")) return TAG[""](HR(), BR(), subject, BR(), BR(), body1, BR(), body2, BR(), BR(), body3, BR(), BR(), body4, BR(), BR(), body5, BR(), BR(), body6, BR(), BR(), body7, BR(), BR(), body8, BR(), BR(), body9, BR())