def insert_waitinglist_from_form(dbo, post, username): """ Creates a waiting list record from the screen data: The webpy data object containing form parameters """ l = dbo.locale if post["description"] == "": raise asm3.utils.ASMValidationError(_("Description cannot be blank", l)) if post.integer("owner") == 0: raise asm3.utils.ASMValidationError(_("Waiting list entries must have a contact", l)) if post["dateputon"] == "": raise asm3.utils.ASMValidationError(_("Date put on cannot be blank", l)) nwlid = dbo.insert("animalwaitinglist", { "SpeciesID": post.integer("species"), "Size": post.integer("size"), "DatePutOnList": post.date("dateputon"), "OwnerID": post.integer("owner"), "AnimalDescription": post["description"], "ReasonForWantingToPart": post["reasonforwantingtopart"], "CanAffordDonation": post.boolean("canafforddonation"), "Urgency": post.integer("urgency"), "DateRemovedFromList": post.date("dateremoved"), "AutoRemovePolicy": post.integer("autoremovepolicy"), "DateOfLastOwnerContact": post.date("dateoflastownercontact"), "ReasonForRemoval": post["reasonforremoval"], "Comments": post["comments"], "UrgencyLastUpdatedDate": dbo.today(), "UrgencyUpdateDate": dbo.today(offset=asm3.configuration.waiting_list_urgency_update_period(dbo)) }, username) # Save any additional field values given asm3.additional.save_values_for_link(dbo, post, nwlid, "waitinglist", True) return nwlid
def insert_foundanimal_from_form(dbo, post, username): """ Inserts a new found animal record from the screen data: The webpy data object containing form parameters """ l = dbo.locale if post.date("datefound") is None: raise asm3.utils.ASMValidationError(_("Date found cannot be blank", l)) if post.date("datereported") is None: raise asm3.utils.ASMValidationError(_("Date reported cannot be blank", l)) if post.integer("owner") == 0: raise asm3.utils.ASMValidationError(_("Found animals must have a contact", l)) nid = dbo.insert("animalfound", { "AnimalTypeID": post.integer("species"), "DateReported": post.date("datereported"), "ReturnToOwnerDate": post.date("returntoownerdate"), "DateFound": post.date("datefound"), "Sex": post.integer("sex"), "BreedID": post.integer("breed"), "AgeGroup": post["agegroup"], "BaseColourID": post.integer("colour"), "DistFeat": post["markings"], "AreaFound": post["areafound"], "AreaPostcode": post["areapostcode"], "MicrochipNumber": post["microchip"], "OwnerID": post.integer("owner"), "Comments": post["comments"] }, username) # Save any additional field values given asm3.additional.save_values_for_link(dbo, post, nid, "foundanimal", True) return nid
def update_waitinglist_from_form(dbo, post, username): """ Updates a waiting list record from the screen data: The webpy data object containing form parameters """ l = dbo.locale wlid = post.integer("id") if not dbo.optimistic_check("animalwaitinglist", post.integer("id"), post.integer("recordversion")): raise asm3.utils.ASMValidationError(_("This record has been changed by another user, please reload.", l)) if post["description"] == "": raise asm3.utils.ASMValidationError(_("Description cannot be blank", l)) if post.integer("owner") == 0: raise asm3.utils.ASMValidationError(_("Waiting list entries must have a contact", l)) if post["dateputon"] == "": raise asm3.utils.ASMValidationError(_("Date put on cannot be blank", l)) dbo.update("animalwaitinglist", wlid, { "SpeciesID": post.integer("species"), "Size": post.integer("size"), "DatePutOnList": post.date("dateputon"), "OwnerID": post.integer("owner"), "AnimalDescription": post["description"], "ReasonForWantingToPart": post["reasonforwantingtopart"], "CanAffordDonation": post.boolean("canafforddonation"), "Urgency": post.integer("urgency"), "DateRemovedFromList": post.date("dateremoved"), "AutoRemovePolicy": post.integer("autoremovepolicy"), "DateOfLastOwnerContact": post.date("dateoflastownercontact"), "ReasonForRemoval": post["reasonforremoval"], "Comments": post["comments"] }, username) asm3.additional.save_values_for_link(dbo, post, wlid, "waitinglist")
def insert_stocklevel_from_form(dbo, post, username): """ Inserts a stocklevel item from a dialog. A usage record will be written, so usage data should be sent too. """ l = dbo.locale if post["name"] == "": raise asm3.utils.ASMValidationError( _("Stock level must have a name", l)) if post["unitname"] == "": raise asm3.utils.ASMValidationError( _("Stock level must have a unit", l)) nid = dbo.insert("stocklevel", { "Name": post["name"], "Description": post["description"], "StockLocationID": post.integer("location"), "UnitName": post["unitname"], "Total": post.floating("total"), "Balance": post.floating("balance"), "Expiry": post.date("expiry"), "BatchNumber": post["batchnumber"], "Cost": post.integer("cost"), "UnitPrice": post.integer("unitprice"), "CreatedDate": dbo.now() }, username, setCreated=False, setRecordVersion=False) insert_stockusage(dbo, username, nid, post.floating("balance"), post.date("usagedate"), post.integer("usagetype"), post["comments"]) return nid
def update_foundanimal_from_form(dbo, post, username): """ Updates a found animal record from the screen post: The webpy data object containing form parameters """ l = dbo.locale lfid = post.integer("id") if not dbo.optimistic_check("animalfound", post.integer("id"), post.integer("recordversion")): raise asm3.utils.ASMValidationError(_("This record has been changed by another user, please reload.", l)) if post.date("datefound") is None: raise asm3.utils.ASMValidationError(_("Date found cannot be blank", l)) if post.date("datereported") is None: raise asm3.utils.ASMValidationError(_("Date reported cannot be blank", l)) if post.integer("owner") == 0: raise asm3.utils.ASMValidationError(_("Found animals must have a contact", l)) dbo.update("animalfound", lfid, { "AnimalTypeID": post.integer("species"), "DateReported": post.date("datereported"), "ReturnToOwnerDate": post.date("returntoownerdate"), "DateFound": post.date("datefound"), "Sex": post.integer("sex"), "BreedID": post.integer("breed"), "AgeGroup": post["agegroup"], "BaseColourID": post.integer("colour"), "DistFeat": post["markings"], "AreaFound": post["areafound"], "AreaPostcode": post["areapostcode"], "MicrochipNumber": post["microchip"], "OwnerID": post.integer("owner"), "Comments": post["comments"] }, username) asm3.additional.save_values_for_link(dbo, post, lfid, "foundanimal")
def update_animalcontrol_from_form(dbo, post, username, geocode=True): """ Updates an animal control incident record from the screen data: The webpy data object containing form parameters """ l = dbo.locale acid = post.integer("id") if not dbo.optimistic_check("animalcontrol", post.integer("id"), post.integer("recordversion")): raise asm3.utils.ASMValidationError(_("This record has been changed by another user, please reload.", l)) if post.date("incidentdate") is None: raise asm3.utils.ASMValidationError(_("Incident date cannot be blank", l)) dbo.update("animalcontrol", acid, { "IncidentDateTime": post.datetime("incidentdate", "incidenttime"), "IncidentTypeID": post.integer("incidenttype"), "CallDateTime": post.datetime("calldate", "calltime"), "CallNotes": post["callnotes"], "CallTaker": post["calltaker"], "CallerID": post.integer("caller"), "VictimID": post.integer("victim"), "DispatchAddress": post["dispatchaddress"], "DispatchTown": post["dispatchtown"], "DispatchCounty": post["dispatchcounty"], "DispatchPostcode": post["dispatchpostcode"], "JurisdictionID": post.integer("jurisdiction"), "PickupLocationID": post.integer("pickuplocation"), "DispatchLatLong": post["dispatchlatlong"], "DispatchedACO": post["dispatchedaco"], "DispatchDateTime": post.datetime("dispatchdate", "dispatchtime"), "RespondedDateTime": post.datetime("respondeddate", "respondedtime"), "FollowupDateTime": post.datetime("followupdate", "followuptime"), "FollowupComplete": post.boolean("followupcomplete"), "FollowupDateTime2": post.datetime("followupdate2", "followuptime2"), "FollowupComplete2": post.boolean("followupcomplete2"), "FollowupDateTime3": post.datetime("followupdate3", "followuptime3"), "FollowupComplete3": post.boolean("followupcomplete3"), "CompletedDate": post.datetime("completeddate", "completedtime"), "IncidentCompletedID": post.integer("completedtype"), "SiteID": post.integer("site"), "OwnerID": post.integer("owner"), "Owner2ID": post.integer("owner2"), "Owner3ID": post.integer("owner3"), "AnimalDescription": post["animaldescription"], "SpeciesID": post.integer("species"), "Sex": post.integer("sex"), "AgeGroup": post["agegroup"] }, username) asm3.additional.save_values_for_link(dbo, post, username, acid, "incident") asm3.diary.update_link_info(dbo, username, asm3.diary.ANIMALCONTROL, acid) update_animalcontrol_roles(dbo, acid, post.integer_list("viewroles"), post.integer_list("editroles")) # Check/update the geocode for the dispatch address if geocode: update_dispatch_geocode(dbo, acid, post["dispatchlatlong"], post["dispatchaddress"], post["dispatchtown"], post["dispatchcounty"], post["dispatchpostcode"])
def sign_document_page(dbo, mid, email): """ Outputs a page that allows signing of document with media id mid. email is the address to send a copy of the signed document to. """ l = dbo.locale scripts = [ asm3.html.script_tag(JQUERY_JS), asm3.html.script_tag(JQUERY_UI_JS), asm3.html.script_tag(BOOTSTRAP_JS), asm3.html.script_tag(TOUCHPUNCH_JS), asm3.html.script_tag(SIGNATURE_JS), asm3.html.css_tag(BOOTSTRAP_CSS), asm3.html.css_tag(BOOTSTRAP_ICONS_CSS), asm3.html.script_i18n(dbo.locale), asm3.html.asm_script_tag("service_sign_document.js") ] dummy, dummy, dummy, contents = asm3.media.get_media_file_data( dbo, int(mid)) content = asm3.utils.fix_relative_document_uris( dbo, asm3.utils.bytes2str(contents)) controller = { "id": mid, "account": dbo.database, "email": email, "notes": asm3.media.get_notes_for_id(dbo, int(mid)), "signed": asm3.media.has_signature(dbo, mid), "content": content } return asm3.html.js_page(scripts, _("Signing Pad", l), controller)
def add_message(dbo, createdby, email, message, forname="*", priority=0, expires=None, added=None): if added is None: added = dbo.today() if expires is None: expires = dbo.today(offset=7) l = dbo.locale mid = dbo.insert( "messages", { "Added": added, "Expires": expires, "CreatedBy": createdby, "Priority": priority, "ForName": forname, "Message": message }) # If email is set, we email the message to everyone that it would match if email == 1: asm3.utils.send_user_email(dbo, createdby, forname, _("Message from {0}", l).format(createdby), message) return mid
def create_animal_from_found(dbo, username, aid): """ Creates an animal record from a found animal with the id given """ a = dbo.first_row( dbo.query("SELECT * FROM animalfound WHERE ID = %d" % int(aid)) ) l = dbo.locale data = { "animalname": _("Found Animal {0}", l).format(aid), "markings": str(a["DISTFEAT"]), "species": str(a["ANIMALTYPEID"]), "comments": str(a["COMMENTS"]), "broughtinby": str(a["OWNERID"]), "originalowner": str(a["OWNERID"]), "animaltype": asm3.configuration.default_type(dbo), "breed1": a["BREEDID"], "breed2": a["BREEDID"], "basecolour": str(a["BASECOLOURID"]), "microchipped": asm3.utils.iif(a["MICROCHIPNUMBER"] is not None and a["MICROCHIPNUMBER"] != "", "1", "0"), "microchipnumber": a["MICROCHIPNUMBER"], "size": asm3.configuration.default_size(dbo), "internallocation": asm3.configuration.default_location(dbo), "dateofbirth": python2display(l, subtract_years(now(dbo.timezone))), "estimateddob": "1", } # If we're creating shelter codes manually, we need to put something unique # in there for now. Use the id if asm3.configuration.manual_codes(dbo): data["sheltercode"] = "FA" + str(aid) data["shortcode"] = "FA" + str(aid) nextid, dummy = asm3.animal.insert_animal_from_form(dbo, asm3.utils.PostedData(data, l), username) return nextid
def tokenCharge(self, payref, item_description=""): payments = self.getPayments(payref) total_charge_sum = sum( round(r.DONATION, 2) + (r.VATAMOUNT if r.VATAMOUNT > 0 else 0) for r in payments # add VAT for consistency with other payment providers ) / 100.0 client_reference_id = "%s-%s" % (self.dbo.database, payref) # prefix database to payref OwnerID = payments[0].OWNERID p = asm3.person.get_person(self.dbo, OwnerID) if not asm3.utils.is_valid_email_address(p.EMAILADDRESS): raise Exception(_("Invalid email address")) url = "https://secure.cardcom.solutions/interface/ChargeToken.aspx" params = { "Operation": "2", # charge + create token, "TerminalNumber": asm3.configuration.cardcom_terminalnumber(self.dbo), "UserName": asm3.configuration.cardcom_username(self.dbo), "codepage": "65001", # unicode "TokenToCharge.DocTypeToCreate": asm3.configuration.cardcom_documenttype(self.dbo), # 3 = nonprofit receipt "TokenToCharge.SumToBill": str(total_charge_sum), "TokenToCharge.CoinID": "1", # TODO: not critical - use ASM currency "TokenToCharge.UniqAsmachta": client_reference_id, "InvoiceHead.CustName": asm3.utils.decode_html(p.OWNERNAME)[:50] , "InvoiceHead.CustAddresLine1": asm3.utils.decode_html(p.OWNERADDRESS)[:50], "InvoiceHead.CustCity": asm3.utils.decode_html(p.OWNERTOWN)[:50], "InvoiceHead.CustMobilePH": asm3.utils.decode_html(p.MOBILETELEPHONE)[:50], "InvoiceHead.ExtIsVatFree": "true",# no VAT for nonprofit receipts. TODO: config? "InvoiceHead.SendByEmail": "false", # TODO: not critical - config? "InvoiceHead.Language": "he", # TODO: not critical - config / use locale? "InvoiceHead.Email": p.EMAILADDRESS, "TokenToCharge.Token": asm3.person.get_extra_id(self.dbo, p, "Cardcom_Token"), "TokenToCharge.CardValidityMonth": asm3.person.get_extra_id(self.dbo, p, "Cardcom_CardValidity").split("/")[0], "TokenToCharge.CardValidityYear": asm3.person.get_extra_id(self.dbo, p, "Cardcom_CardValidity").split("/")[1][-2:], "TokenToCharge.IdentityNumber": asm3.person.get_extra_id(self.dbo, p, "Cardcom_CardOwnerID") } for p in self.getInvoiceItems(payments): params.update(p) asm_debug("params: %s" % params, "cardcom.tokenCharge", self.dbo) r = asm3.utils.post_form(url, params) if not r["status"] < 400: raise Exception("Response not ok: %s %s" % (r["status"], r["response"])) results = asm3.utils.parse_qs(r["response"]) asm_debug("parsed response: %s" % results) if results["ResponseCode"] != "0": asm3.al.error("Bad Cardcom operation response %s: %s" % (results['ResponseCode'], results['Description']), "cardcom.tokenCharge", self.dbo) raise ProcessorError("Bad Cardcom operation response %s: %s" % (results['ResponseCode'], results['Description'])) trxid = "%s/%s" % (results.get('InvoiceResponse.InvoiceNumber',''), results.get('InternalDealNumber','')) rcvd = asm3.utils.cint(results.get("ExtShvaParams.Sum36", total_charge_sum / 100.0)) self.markPaymentReceived(payref, trxid, rcvd, 0, 0, r["response"]) InvoiceResponseCode = results.get("InvoiceResponse.ResponseCode") if InvoiceResponseCode != "0": asm3.al.error("Invoice not created for %s. Response code: %s" % (payref, InvoiceResponseCode), "cardcom.tokenCharge", self.dbo)
def delete_lookup(dbo, username, lookup, iid): l = dbo.locale t = LOOKUP_TABLES[lookup] for fv in t[LOOKUP_FOREIGNKEYS]: table, field = fv.split(".") if 0 < dbo.query_int("SELECT COUNT(*) FROM %s WHERE %s = %s" % (table, field, iid)): raise asm3.utils.ASMValidationError(_("This item is referred to in the database ({0}) and cannot be deleted until it is no longer in use.", l).format(fv)) dbo.delete(lookup, iid, username)
def update_animalcontrol_addlink(dbo, username, acid, animalid): """ Adds a link between an animal and an incident. """ l = dbo.locale if 0 != dbo.query_int("SELECT COUNT(*) FROM animalcontrolanimal WHERE AnimalControlID = ? AND AnimalID = ?", (acid, animalid)): raise asm3.utils.ASMValidationError(_("That animal is already linked to the incident", l)) dbo.execute("INSERT INTO animalcontrolanimal (AnimalControlID, AnimalID) VALUES (?, ?)", (acid, animalid)) asm3.audit.create(dbo, username, "animalcontrolanimal", acid, "", "incident %d linked to animal %d" % (acid, animalid))
def get_microchip_manufacturer(l, chipno): """ Figures out the microchip manufacturer of chipno. Returns a blank for a blank chip number and does validation for a chip number with no matching manufacturer. """ mf = None if chipno is None or chipno == "": return "" for m in MICROCHIP_MANUFACTURERS: if len(chipno) == m["length"] and re.compile(m["regex"]).match(chipno): if m["locales"] == "" or l in m["locales"].split(" "): mf = m["name"] break if mf is None and (len(chipno) != 9 and len(chipno) != 10 and len(chipno) != 15): return _("Invalid microchip number length", l) if mf is None: return _("Unknown microchip brand", l) return mf
def update_stocklevel_from_form(dbo, post, username): """ Updates a stocklevel item from a dialog. The post should include the ID of the stocklevel to adjust and a usage record will be written so usage data should be sent too. """ l = dbo.locale slid = post.integer("stocklevelid") if slid == 0: raise asm3.utils.ASMValidationError("Invalid stock level") if post["name"] == "": raise asm3.utils.ASMValidationError( _("Stock level must have a name", l)) if post["unitname"] == "": raise asm3.utils.ASMValidationError( _("Stock level must have a unit", l)) if post.date("usagedate") is None: raise asm3.utils.ASMValidationError( _("Stock usage must have a date", l)) diff = post.floating("balance") - dbo.query_float( "SELECT Balance FROM stocklevel WHERE ID = ?", [slid]) dbo.update("stocklevel", slid, { "Name": post["name"], "Description": post["description"], "StockLocationID": post.integer("location"), "UnitName": post["unitname"], "Total": post.floating("total"), "Balance": post.floating("balance"), "Expiry": post.date("expiry"), "BatchNumber": post["batchnumber"], "Cost": post.integer("cost"), "UnitPrice": post.integer("unitprice") }, username, setLastChanged=False, setRecordVersion=False) if diff != 0: insert_stockusage(dbo, username, slid, diff, post.date("usagedate"), post.integer("usagetype"), post["comments"])
def insert_animalcontrol_from_form(dbo, post, username, geocode=True): """ Inserts a new animal control incident record from the screen data: The webpy data object containing form parameters """ l = dbo.locale if post.date("incidentdate") is None: raise asm3.utils.ASMValidationError(_("Incident date cannot be blank", l)) nid = dbo.insert("animalcontrol", { "IncidentDateTime": post.datetime("incidentdate", "incidenttime"), "IncidentTypeID": post.integer("incidenttype"), "CallDateTime": post.datetime("calldate", "calltime"), "CallNotes": post["callnotes"], "CallTaker": post["calltaker"], "CallerID": post.integer("caller"), "VictimID": post.integer("victim"), "DispatchAddress": post["dispatchaddress"], "DispatchTown": post["dispatchtown"], "DispatchCounty": post["dispatchcounty"], "DispatchPostcode": post["dispatchpostcode"], "JurisdictionID": post.integer("jurisdiction"), "PickupLocationID": post.integer("pickuplocation"), "DispatchLatLong": post["dispatchlatlong"], "DispatchedACO": post["dispatchedaco"], "DispatchDateTime": post.datetime("dispatchdate", "dispatchtime"), "RespondedDateTime": post.datetime("respondeddate", "respondedtime"), "FollowupDateTime": post.datetime("followupdate", "followuptime"), "FollowupComplete": post.boolean("followupcomplete"), "FollowupDateTime2": post.datetime("followupdate2", "followuptime2"), "FollowupComplete2": post.boolean("followupcomplete2"), "FollowupDateTime3": post.datetime("followupdate3", "followuptime3"), "FollowupComplete3": post.boolean("followupcomplete3"), "CompletedDate": post.datetime("completeddate", "completedtime"), "IncidentCompletedID": post.integer("completedtype"), "SiteID": post.integer("site"), "OwnerID": post.integer("owner"), "Owner2ID": post.integer("owner2"), "Owner3ID": post.integer("owner3"), "AnimalDescription": post["animaldescription"], "SpeciesID": post.integer("species"), "Sex": post.integer("sex"), "AgeGroup": post["agegroup"] }, username) asm3.additional.save_values_for_link(dbo, post, nid, "incident", True) update_animalcontrol_roles(dbo, nid, post.integer_list("viewroles"), post.integer_list("editroles")) # Look up a geocode for the dispatch address if geocode: update_dispatch_geocode(dbo, nid, "", post["dispatchaddress"], post["dispatchtown"], post["dispatchcounty"], post["dispatchpostcode"]) return nid
def save_values_for_link(dbo, post, linkid, linktype="animal", setdefaults=False): """ Saves incoming additional field values from a record. Clears existing additional field values before saving (this is because forms don't send blank values) linkid: The link to the parent record linktype: The class of parent record setdefaults: If True, will set default values for any keys not supplied (Should be True for calls from insert_X_from_form methods) Keys of either a.MANDATORY.ID can be used (ASM internal forms) or keys of the form additionalFIELDNAME (ASM online forms) """ l = dbo.locale dbo.delete( "additional", "LinkType IN (%s) AND LinkID=%s" % (clause_for_linktype(linktype), linkid)) for f in get_field_definitions(dbo, linktype): key = "a.%s.%s" % (f.mandatory, f.id) key2 = "additional%s" % f.fieldname if key not in post and key2 not in post: if setdefaults and f.DEFAULTVALUE and f.DEFAULTVALUE != "": insert_additional(dbo, f.LINKTYPE, linkid, f.ID, f.DEFAULTVALUE) continue elif key not in post: key = key2 val = post[key] if f.fieldtype == YESNO: val = str(post.boolean(key)) elif f.fieldtype == MONEY: val = str(post.integer(key)) elif f.fieldtype == DATE: if len(val.strip()) > 0 and post.date(key) is None: raise asm3.utils.ASMValidationError( _("Additional date field '{0}' contains an invalid date.", l).format(f.fieldname)) val = python2display(dbo.locale, post.date(key)) insert_additional(dbo, f.LINKTYPE, linkid, f.ID, val)
def checkout_adoption_page(dbo, token): """ Outputs a page that generates paperwork, allows an adopter to sign it and then pay their adoption fee and an optional donation """ l = dbo.locale scripts = [ asm3.html.script_tag(JQUERY_JS), asm3.html.script_tag(JQUERY_UI_JS), asm3.html.script_tag(BOOTSTRAP_JS), asm3.html.script_tag(TOUCHPUNCH_JS), asm3.html.script_tag(SIGNATURE_JS), asm3.html.css_tag(BOOTSTRAP_CSS), asm3.html.css_tag(BOOTSTRAP_ICONS_CSS), asm3.html.script_i18n(dbo.locale), asm3.html.asm_script_tag("service_checkout_adoption.js") ] co = asm3.cachedisk.get(token, dbo.database) if co is None: raise asm3.utils.ASMError("invalid token") # Generate the adoption paperwork if it has not been generated already if co["mediaid"] == 0: dtid = asm3.configuration.adoption_checkout_templateid(dbo) content = asm3.wordprocessor.generate_movement_doc( dbo, dtid, co["movementid"], "checkout") # Save the doc with the person and animal, record the person copy for signing tempname = asm3.template.get_document_template_name(dbo, dtid) tempname = "%s - %s::%s" % ( tempname, asm3.animal.get_animal_namecode(dbo, co["animalid"]), asm3.person.get_person_name(dbo, co["personid"])) asm3.media.create_document_media(dbo, "checkout", asm3.media.ANIMAL, co["animalid"], tempname, content) co["mediaid"] = asm3.media.create_document_media( dbo, "checkout", asm3.media.PERSON, co["personid"], tempname, content) content = asm3.utils.fix_relative_document_uris( dbo, asm3.utils.bytes2str(content)) co["mediacontent"] = content asm3.cachedisk.put(token, dbo.database, co, 86400 * 2) # Include extra values co["donationmsg"] = asm3.configuration.adoption_checkout_donation_msg(dbo) co["donationtiers"] = asm3.configuration.adoption_checkout_donation_tiers( dbo) co["token"] = token return asm3.html.js_page(scripts, _("Adoption Checkout", l), co)
def auto_remove_waitinglist(dbo): """ Finds and automatically marks entries removed that have gone past the last contact date + weeks. """ l = dbo.locale rows = dbo.query("SELECT a.ID, a.DateOfLastOwnerContact, " \ "a.AutoRemovePolicy " \ "FROM animalwaitinglist a WHERE a.DateRemovedFromList Is Null " \ "AND AutoRemovePolicy > 0 AND DateOfLastOwnerContact Is Not Null") updates = [] for r in rows: xdate = add_days(r.DATEOFLASTOWNERCONTACT, 7 * r.AUTOREMOVEPOLICY) if after(now(dbo.timezone), xdate): asm3.al.debug("auto removing waitinglist entry %d due to policy" % r.ID, "waitinglist.auto_remove_waitinglist", dbo) updates.append((now(dbo.timezone), _("Auto removed due to lack of owner contact.", l), r.ID)) if len(updates) > 0: dbo.execute_many("UPDATE animalwaitinglist SET DateRemovedFromList = ?, " \ "ReasonForRemoval=? WHERE ID=?", updates)
def sign_document_page(dbo, mid): """ Outputs a page that allows signing of document with media id mid""" l = dbo.locale if asm3.media.has_signature(dbo, mid): return "<!DOCTYPE html><head><title>%s</title></head>" \ "<body><p>%s</p></body></html>" % \ ( _("Already Signed", l), _("Sorry, this document has already been signed", l)) h = [] h.append("""<!DOCTYPE html> <html> <head> <title> %(title)s </title> <meta name="viewport" content="width=device-width, initial-scale=1"> %(css)s %(scripts)s <script type="text/javascript"> $(document).ready(function() { $("#signature").signature({ guideline: true }); $("#sig-clear").click(function() { $("#signature").signature("clear"); }); $("#sig-sign").click(function() { var img = $("#signature canvas").get(0).toDataURL("image/png"); var formdata = "account=%(account)s&method=sign_document&formid=%(id)s&sig=" + encodeURIComponent(img); formdata += "&signdate=" + encodeURIComponent(moment().format("YYYY-MM-DD HH:mm:ss")); if ($("#signature").signature("isEmpty")) { alert("Signature is required"); return; } $.ajax({ type: "POST", url: "service", data: formdata, dataType: "text", mimeType: "textPlain", success: function(response) { $("body").empty().append("<p>%(thankyou)s</p>"); }, error: function(jqxhr, textstatus, response) { $("body").append("<p>" + response + "</p>"); } }); }); $("#reviewlink").click(function() { $("#reviewlink").fadeOut(); $("#review").slideDown(); }); }); </script> <style> button { padding: 10px; font-size: 100%%; } #signature { border: 1px solid #aaa; height: 200px; width: 100%%; max-width: 500px; } </style> </head> <body> """ % { "title": _("Signing Pad", l), "id": mid, "account": dbo.database, "css": asm3.html.asm_css_tag("asm-icon.css"), "thankyou": _("Thank you, the document is now signed.", l), "scripts": asm3.html.script_tag(JQUERY_JS) + asm3.html.script_tag(JQUERY_UI_JS) + asm3.html.script_tag(TOUCHPUNCH_JS) + asm3.html.script_tag(SIGNATURE_JS) + asm3.html.script_tag(MOMENT_JS) }) d = [] docnotes = [] docnotes.append(asm3.media.get_notes_for_id(dbo, int(mid))) mdate, medianame, mimetype, contents = asm3.media.get_media_file_data(dbo, int(mid)) d.append(contents) d.append("<hr />") h.append("<p><b>%s: %s</b></p>" % (_("Signing", l), ", ".join(docnotes))) h.append('<p><a id="reviewlink" href="#">%s</a></p>' % _("View Document", l)) h.append('<div id="review" style="display: none">') h.append("\n".join(d)) h.append('</div>') h.append('<div id="signature"></div>') h.append('<p>') h.append('<button id="sig-clear" type="button">' + _("Clear", l) + '</button>') h.append('<button id="sig-sign" type="button">' + _("Sign", l) + '</button>') h.append('</p>') h.append('<p>') h.append(_("Please click the Sign button when you are finished.", l)) h.append('</p>') h.append('<p>') h.append(_("Once signed, this document cannot be edited or tampered with.", l)) h.append('</p>') h.append("</body></html>") return "\n".join(h)
def handler(post, path, remoteip, referer, querystring): """ Handles the various service method types. post: The GET/POST parameters path: The current system path/code.PATH remoteip: The IP of the caller referer: The referer HTTP header querystring: The complete querystring return value is a tuple containing MIME type, max-age, content """ # Get service parameters account = post["account"] username = post["username"] password = post["password"] method = post["method"] animalid = post.integer("animalid") formid = post.integer("formid") seq = post.integer("seq") title = post["title"] strip_personal = post.integer("sensitive") == 0 cache_key = querystring.replace(" ", "") # Do we have a cached response for these parameters? cached_response = get_cached_response(cache_key) if cached_response is not None: asm3.al.debug("cache hit for %s" % (cache_key), "service.handler") return cached_response # Are we dealing with multiple databases, but no account was specified? if account == "" and MULTIPLE_DATABASES: return ("text/plain", 0, 0, "ERROR: No database/alias specified") dbo = asm3.db.get_database(account) if dbo.database in ( "FAIL", "DISABLED", "WRONGSERVER" ): asm3.al.error("auth failed - invalid smaccount %s from %s (%s)" % (account, remoteip, dbo.database), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid database (%s)" % dbo.database) # If the database has disabled the service API, stop now if not asm3.configuration.service_enabled(dbo): asm3.al.error("Service API is disabled (%s)" % method, "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Service API is disabled") # Do any database updates need doing in this db? dbo.installpath = path if asm3.dbupdate.check_for_updates(dbo): asm3.dbupdate.perform_updates(dbo) # Does the method require us to authenticate? If so, do it. user = None securitymap = "" if method in AUTH_METHODS: # If the database has authenticated service methods disabled, stop now if not asm3.configuration.service_auth_enabled(dbo): asm3.al.error("Service API for auth methods is disabled (%s)" % method, "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Service API for authenticated methods is disabled") user = asm3.users.authenticate(dbo, username, password) if user is None: asm3.al.error("auth failed - %s/%s is not a valid username/password from %s" % (username, password, remoteip), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid username and password") securitymap = asm3.users.get_security_map(dbo, user["USERNAME"]) # Get the preferred locale and timezone for the site l = asm3.configuration.locale(dbo) dbo.locale = l dbo.timezone = asm3.configuration.timezone(dbo) asm3.al.info("call %s->%s [%s %s]" % (username, method, str(animalid), title), "service.handler", dbo) if method =="animal_image": hotlink_protect("animal_image", referer) if asm3.utils.cint(animalid) == 0: asm3.al.error("animal_image failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: mediadate, data = asm3.media.get_image_file_data(dbo, "animal", asm3.utils.cint(animalid), seq) if data == "NOPIC": mediadate, data = asm3.media.get_image_file_data(dbo, "nopic", 0) return set_cached_response(cache_key, "image/jpeg", 86400, 3600, data) elif method =="animal_thumbnail": if asm3.utils.cint(animalid) == 0: asm3.al.error("animal_thumbnail failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: mediadate, data = asm3.media.get_image_file_data(dbo, "animalthumb", asm3.utils.cint(animalid), seq) if data == "NOPIC": mediadate, data = asm3.media.get_image_file_data(dbo, "nopic", 0) return set_cached_response(cache_key, "image/jpeg", 86400, 86400, data) elif method == "animal_view": if asm3.utils.cint(animalid) == 0: asm3.al.error("animal_view failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: return set_cached_response(cache_key, "text/html", 86400, 120, asm3.publishers.html.get_animal_view(dbo, asm3.utils.cint(animalid))) elif method == "animal_view_adoptable_js": return set_cached_response(cache_key, "application/javascript", 10800, 600, asm3.publishers.html.get_animal_view_adoptable_js(dbo)) elif method == "animal_view_adoptable_html": return set_cached_response(cache_key, "text/html", 86400, 120, asm3.publishers.html.get_animal_view_adoptable_html(dbo)) elif method =="dbfs_image": hotlink_protect("dbfs_image", referer) return set_cached_response(cache_key, "image/jpeg", 86400, 86400, asm3.utils.iif(title.startswith("/"), asm3.dbfs.get_string_filepath(dbo, title), asm3.dbfs.get_string(dbo, title))) elif method =="extra_image": hotlink_protect("extra_image", referer) return set_cached_response(cache_key, "image/jpeg", 86400, 86400, asm3.dbfs.get_string(dbo, title, "/reports")) elif method == "json_adoptable_animal": if asm3.utils.cint(animalid) == 0: asm3.al.error("json_adoptable_animal failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data(dbo, None, asm3.utils.cint(animalid), include_additional_fields = True) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "html_adoptable_animals": return set_cached_response(cache_key, "text/html", 10800, 1800, \ asm3.publishers.html.get_adoptable_animals(dbo, style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"), locationid=post.integer("locationid"))) elif method == "html_adopted_animals": return set_cached_response(cache_key, "text/html", 10800, 1800, \ asm3.publishers.html.get_adopted_animals(dbo, daysadopted=post.integer("days"), style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"))) elif method == "html_deceased_animals": return set_cached_response(cache_key, "text/html", 10800, 1800, \ asm3.publishers.html.get_deceased_animals(dbo, daysdeceased=post.integer("days"), style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"))) elif method == "html_flagged_animals": if post["flag"] == "": asm3.al.error("html_flagged_animals requested with no flag.", "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid flag") return set_cached_response(cache_key, "text/html", 10800, 1800, \ asm3.publishers.html.get_flagged_animals(dbo, style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"), flag=post["flag"], allanimals=post.integer("all"))) elif method == "html_held_animals": return set_cached_response(cache_key, "text/html", 10800, 1800, \ asm3.publishers.html.get_held_animals(dbo, style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"))) elif method == "json_adoptable_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data(dbo, None, include_additional_fields = True) if strip_personal: rs = strip_personal_data(rs) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "jsonp_adoptable_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data(dbo, None, include_additional_fields = True) if strip_personal: rs = strip_personal_data(rs) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_adoptable_animal": if asm3.utils.cint(animalid) == 0: asm3.al.error("xml_adoptable_animal failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data(dbo, None, asm3.utils.cint(animalid), include_additional_fields = True) return set_cached_response(cache_key, "application/xml", 3600, 3600, asm3.html.xml(rs)) elif method == "xml_adoptable_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data(dbo, None, include_additional_fields = True) if strip_personal: rs = strip_personal_data(rs) return set_cached_response(cache_key, "application/xml", 3600, 3600, asm3.html.xml(rs)) elif method == "json_found_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_FOUND_ANIMAL) rs = asm3.lostfound.get_foundanimal_last_days(dbo) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "jsonp_found_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_FOUND_ANIMAL) rs = asm3.lostfound.get_foundanimal_last_days(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_found_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_FOUND_ANIMAL) rs = asm3.lostfound.get_foundanimal_last_days(dbo) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.html.xml(rs)) elif method == "json_lost_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_LOST_ANIMAL) rs = asm3.lostfound.get_lostanimal_last_days(dbo) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "jsonp_lost_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_LOST_ANIMAL) rs = asm3.lostfound.get_lostanimal_last_days(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_lost_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_LOST_ANIMAL) rs = asm3.lostfound.get_lostanimal_last_days(dbo) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.html.xml(rs)) elif method == "json_recent_adoptions": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.movement.get_recent_adoptions(dbo) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "jsonp_recent_adoptions": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.movement.get_recent_adoptions(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_recent_adoptions": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.movement.get_recent_adoptions(dbo) return set_cached_response(cache_key, "application/xml", 3600, 3600, asm3.html.xml(rs)) elif method == "html_report": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_REPORT) crid = asm3.reports.get_id(dbo, title) p = asm3.reports.get_criteria_params(dbo, crid, post) rhtml = asm3.reports.execute(dbo, crid, username, p) return set_cached_response(cache_key, "text/html", 600, 600, rhtml) elif method == "csv_mail" or method == "csv_report": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_REPORT) crid = asm3.reports.get_id(dbo, title) p = asm3.reports.get_criteria_params(dbo, crid, post) rows, cols = asm3.reports.execute_query(dbo, crid, username, p) mcsv = asm3.utils.csv(l, rows, cols, True) return set_cached_response(cache_key, "text/csv", 600, 600, mcsv) elif method == "jsonp_recent_changes": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_recent_changes(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(sa))) elif method == "json_recent_changes": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_recent_changes(dbo) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.utils.json(sa)) elif method == "xml_recent_changes": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_recent_changes(dbo) return set_cached_response(cache_key, "application/xml", 3600, 3600, asm3.html.xml(sa)) elif method == "jsonp_shelter_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_shelter_animals(dbo) if strip_personal: sa = strip_personal_data(sa) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(sa))) elif method == "json_shelter_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_shelter_animals(dbo) if strip_personal: sa = strip_personal_data(sa) return set_cached_response(cache_key, "application/json", 3600, 3600, asm3.utils.json(sa)) elif method == "xml_shelter_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_shelter_animals(dbo) if strip_personal: sa = strip_personal_data(sa) return set_cached_response(cache_key, "application/xml", 3600, 3600, asm3.html.xml(sa)) elif method == "rss_timeline": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) return set_cached_response(cache_key, "application/rss+xml", 3600, 3600, asm3.html.timeline_rss(dbo)) elif method == "upload_animal_image": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.ADD_MEDIA) asm3.media.attach_file_from_form(dbo, username, asm3.media.ANIMAL, int(animalid), post) return ("text/plain", 0, 0, "OK") elif method == "online_form_html": if formid == 0: raise asm3.utils.ASMError("method online_form_html requires a valid formid") return set_cached_response(cache_key, "text/html; charset=utf-8", 120, 120, asm3.onlineform.get_onlineform_html(dbo, formid)) elif method == "online_form_json": if formid == 0: raise asm3.utils.ASMError("method online_form_json requires a valid formid") return set_cached_response(cache_key, "application/json; charset=utf-8", 30, 30, asm3.onlineform.get_onlineform_json(dbo, formid)) elif method == "online_form_post": flood_protect("online_form_post", remoteip, 15) asm3.onlineform.insert_onlineformincoming_from_form(dbo, post, remoteip) redirect = post["redirect"] if redirect == "": redirect = BASE_URL + "/static/pages/form_submitted.html" return ("redirect", 0, 0, redirect) elif method == "sign_document": if formid == 0: raise asm3.utils.ASMError("method sign_document requires a valid formid") if post["sig"] == "": return set_cached_response(cache_key, "text/html", 2, 2, sign_document_page(dbo, formid)) else: asm3.media.sign_document(dbo, "service", formid, post["sig"], post["signdate"]) asm3.media.create_log(dbo, "service", formid, "ES02", _("Document signed", l)) return ("text/plain", 0, 0, "OK") else: asm3.al.error("invalid method '%s'" % method, "service.handler", dbo) raise asm3.utils.ASMError("Invalid method '%s'" % method)
def match_report(dbo, username="******", lostanimalid=0, foundanimalid=0, animalid=0, limit=0): """ Generates the match report and returns it as a string """ l = dbo.locale title = _("Match lost and found animals", l) h = [] h.append(asm3.reports.get_report_header(dbo, title, username)) if limit > 0: h.append("<p>(" + _("Limited to {0} matches", l).format(limit) + ")</p>") def p(s): return "<p>%s</p>" % s def td(s): return "<td>%s</td>" % s def hr(): return "<hr />" lastid = 0 matches = match(dbo, lostanimalid, foundanimalid, animalid, limit) if len(matches) > 0: for m in matches: if lastid != m.lid: if lastid != 0: h.append("</tr></table>") h.append(hr()) h.append(p(_("{0} - {1} {2} ({3}), contact {4} ({5}) - lost in {6}, postcode {7}, on {8}", l).format( \ m.lid, "%s %s %s" % (m.lagegroup, m.lbasecolourname, m.lsexname), \ "%s/%s %s" % (m.lspeciesname, m.lbreedname, m.lmicrochip), \ m.ldistinguishingfeatures, m.lcontactname, m.lcontactnumber, m.larealost, m.lareapostcode, python2display(l, m.ldatelost)))) h.append("<table border=\"1\" width=\"100%\"><tr>") h.append("<th>%s</th>" % _("Reference", l)) h.append("<th>%s</th>" % _("Description", l)) h.append("<th>%s</th>" % _("Area Found", l)) h.append("<th>%s</th>" % _("Area Postcode", l)) h.append("<th>%s</th>" % _("Date Found", l)) h.append("<th>%s</th>" % _("Contact", l)) h.append("<th>%s</th>" % _("Number", l)) h.append("<th>%s</th>" % _("Microchip", l)) h.append("<th>%s</th>" % _("Match", l)) h.append("</tr>") lastid = m.lid h.append("<tr>") h.append(td(str(m.fid))) h.append( td("%s %s %s %s %s" % (m.fagegroup, m.fbasecolourname, m.fsexname, m.fspeciesname, m.fbreedname))) h.append(td(m.fareafound)) h.append(td(m.fareapostcode)) h.append(td(python2display(l, m.fdatefound))) h.append(td(m.fcontactname)) h.append(td(m.fcontactnumber)) h.append(td(m.fmicrochip)) h.append(td(str(m.matchpoints) + "%")) h.append("</tr>") h.append("</tr></table>") else: h.append(p(_("No matches found.", l))) h.append(asm3.reports.get_report_footer(dbo, title, username)) return "\n".join(h)
def match(dbo, lostanimalid=0, foundanimalid=0, animalid=0, limit=0): """ Performs a lost and found match by going through all lost animals lostanimalid: Compare this lost animal against all found animals foundanimalid: Compare all lost animals against this found animal animalid: Compare all lost animals against this shelter animal limit: Stop when we hit this many matches (or 0 for all) returns a list of LostFoundMatch objects """ l = dbo.locale batch = [] matches = [] matchspecies = asm3.configuration.match_species(dbo) matchbreed = asm3.configuration.match_breed(dbo) matchage = asm3.configuration.match_age(dbo) matchsex = asm3.configuration.match_sex(dbo) matcharealost = asm3.configuration.match_area_lost(dbo) matchfeatures = asm3.configuration.match_features(dbo) matchpostcode = asm3.configuration.match_postcode(dbo) matchcolour = asm3.configuration.match_colour(dbo) matchmicrochip = asm3.configuration.match_microchip(dbo) matchdatewithin2weeks = asm3.configuration.match_within2weeks(dbo) matchmax = matchspecies + matchbreed + matchage + matchsex + \ matcharealost + matchfeatures + matchpostcode + matchcolour + \ matchmicrochip + matchdatewithin2weeks matchpointfloor = asm3.configuration.match_point_floor(dbo) includeshelter = asm3.configuration.match_include_shelter(dbo) fullmatch = animalid == 0 and lostanimalid == 0 and foundanimalid == 0 # Ignore records older than 6 months to keep things useful giveup = dbo.today(offset=-182) # Get our set of lost animals lostanimals = None if lostanimalid == 0: lostanimals = dbo.query(get_lostanimal_query(dbo) + \ " WHERE a.DateFound Is Null AND a.DateLost > ? ORDER BY a.DateLost", [giveup]) else: lostanimals = dbo.query(get_lostanimal_query(dbo) + \ " WHERE a.ID = ?", [lostanimalid]) oldestdate = giveup if len(lostanimals) > 0: oldestdate = lostanimals[0].DATELOST # Get the set of found animals for comparison foundanimals = None if foundanimalid == 0: foundanimals = dbo.query(get_foundanimal_query(dbo) + \ " WHERE a.ReturnToOwnerDate Is Null" \ " AND a.DateFound >= ? ", [oldestdate]) else: foundanimals = dbo.query( get_foundanimal_query(dbo) + " WHERE a.ID = ?", [foundanimalid]) # Get the set of shelter animals for comparison - anything brought in recently # that's 1. still on shelter or 2. was released to wild, transferred or escaped shelteranimals = None if includeshelter: if animalid == 0: shelteranimals = dbo.query(asm3.animal.get_animal_query(dbo) + " WHERE " + \ "(a.Archived = 0 OR a.ActiveMovementType IN (3,4,7)) " \ "AND a.DateBroughtIn > ?", [oldestdate]) else: shelteranimals = dbo.query( asm3.animal.get_animal_query(dbo) + " WHERE a.ID = ?", [animalid]) asm3.asynctask.set_progress_max(dbo, len(lostanimals)) for la in lostanimals: asm3.asynctask.increment_progress_value(dbo) # Stop if we've hit our limit if limit > 0 and len(matches) >= limit: break # Found animals (if an animal id has been given don't # check found animals) if animalid == 0: for fa in foundanimals: matchpoints = 0 if la["MICROCHIPNUMBER"] != "" and la["MICROCHIPNUMBER"] == fa[ "MICROCHIPNUMBER"]: matchpoints += matchmicrochip if la["ANIMALTYPEID"] == fa["ANIMALTYPEID"]: matchpoints += matchspecies if la["BREEDID"] == fa["BREEDID"]: matchpoints += matchbreed if la["AGEGROUP"] == fa["AGEGROUP"]: matchpoints += matchage if la["SEX"] == fa["SEX"]: matchpoints += matchsex matchpoints += words(la["AREALOST"], fa["AREAFOUND"], matcharealost) matchpoints += words(la["DISTFEAT"], fa["DISTFEAT"], matchfeatures) if la["AREAPOSTCODE"] == fa["AREAPOSTCODE"]: matchpoints += matchpostcode if la["BASECOLOURID"] == fa["BASECOLOURID"]: matchpoints += matchcolour if date_diff_days(la["DATELOST"], fa["DATEFOUND"]) <= 14: matchpoints += matchdatewithin2weeks if matchpoints > matchmax: matchpoints = matchmax if matchpoints >= matchpointfloor: m = LostFoundMatch(dbo) m.lid = la["ID"] m.lcontactname = la["OWNERNAME"] m.lmicrochip = la["MICROCHIPNUMBER"] m.lcontactnumber = la["HOMETELEPHONE"] m.larealost = la["AREALOST"] m.lareapostcode = la["AREAPOSTCODE"] m.lagegroup = la["AGEGROUP"] m.lsexid = la["SEX"] m.lsexname = la["SEXNAME"] m.lspeciesid = la["ANIMALTYPEID"] m.lspeciesname = la["SPECIESNAME"] m.lbreedid = la["BREEDID"] m.lbreedname = la["BREEDNAME"] m.ldistinguishingfeatures = la["DISTFEAT"] m.lbasecolourid = la["BASECOLOURID"] m.lbasecolourname = la["BASECOLOURNAME"] m.ldatelost = la["DATELOST"] m.fid = fa["ID"] m.fanimalid = 0 m.fcontactname = fa["OWNERNAME"] m.fmicrochip = fa["MICROCHIPNUMBER"] m.fcontactnumber = fa["HOMETELEPHONE"] m.fareafound = fa["AREAFOUND"] m.fareapostcode = fa["AREAPOSTCODE"] m.fagegroup = fa["AGEGROUP"] m.fsexid = fa["SEX"] m.fsexname = fa["SEXNAME"] m.fspeciesid = fa["ANIMALTYPEID"] m.fspeciesname = fa["SPECIESNAME"] m.fbreedid = fa["BREEDID"] m.fbreedname = fa["BREEDNAME"] m.fdistinguishingfeatures = fa["DISTFEAT"] m.fbasecolourid = fa["BASECOLOURID"] m.fbasecolourname = fa["BASECOLOURNAME"] m.fdatefound = fa["DATEFOUND"] m.matchpoints = int( (float(matchpoints) / float(matchmax)) * 100.0) matches.append(m) if fullmatch: batch.append(m.toParams()) if limit > 0 and len(matches) >= limit: break # Shelter animals if includeshelter: for a in shelteranimals: matchpoints = 0 foundarea = "" foundpostcode = "" if la["MICROCHIPNUMBER"] != "" and la["MICROCHIPNUMBER"] == a[ "IDENTICHIPNUMBER"]: matchpoints += matchmicrochip if la["ANIMALTYPEID"] == a["SPECIESID"]: matchpoints += matchspecies if la["BREEDID"] == a["BREEDID"] or la["BREEDID"] == a[ "BREED2ID"]: matchpoints += matchbreed if la["BASECOLOURID"] == a["BASECOLOURID"]: matchpoints += matchcolour if la["AGEGROUP"] == a["AGEGROUP"]: matchpoints += matchage if la["SEX"] == a["SEX"]: matchpoints += matchsex matchpoints += words(la["DISTFEAT"], a["MARKINGS"], matchfeatures) if a["ISPICKUP"] == 1: matchpoints += words(la["AREALOST"], a["PICKUPADDRESS"], matcharealost) foundarea = a["PICKUPADDRESS"] elif a["BROUGHTINBYOWNERADDRESS"] is not None: matchpoints += words(la["AREALOST"], a["BROUGHTINBYOWNERADDRESS"], matcharealost) if asm3.utils.nulltostr( a["BROUGHTINBYOWNERPOSTCODE"]).find( la["AREAPOSTCODE"]) != -1: matchpoints += matchpostcode foundarea = a["BROUGHTINBYOWNERADDRESS"] foundpostcode = a["BROUGHTINBYOWNERPOSTCODE"] elif a["ORIGINALOWNERADDRESS"] is not None: matchpoints += words(la["AREALOST"], a["ORIGINALOWNERADDRESS"], matcharealost) if asm3.utils.nulltostr(a["ORIGINALOWNERPOSTCODE"]).find( la["AREAPOSTCODE"]) != -1: matchpoints += matchpostcode foundarea = a["ORIGINALOWNERADDRESS"] foundpostcode = a["ORIGINALOWNERPOSTCODE"] if date_diff_days(la["DATELOST"], a["DATEBROUGHTIN"]) <= 14: matchpoints += matchdatewithin2weeks if matchpoints > matchmax: matchpoints = matchmax if matchpoints >= matchpointfloor: m = LostFoundMatch(dbo) m.lid = la["ID"] m.lcontactname = la["OWNERNAME"] m.lmicrochip = la["MICROCHIPNUMBER"] m.lcontactnumber = la["HOMETELEPHONE"] m.larealost = la["AREALOST"] m.lareapostcode = la["AREAPOSTCODE"] m.lagegroup = la["AGEGROUP"] m.lsexid = la["SEX"] m.lsexname = la["SEXNAME"] m.lspeciesid = la["ANIMALTYPEID"] m.lspeciesname = la["SPECIESNAME"] m.lbreedid = la["BREEDID"] m.lbreedname = la["BREEDNAME"] m.ldistinguishingfeatures = la["DISTFEAT"] m.lbasecolourid = la["BASECOLOURID"] m.lbasecolourname = la["BASECOLOURNAME"] m.ldatelost = la["DATELOST"] m.fid = 0 m.fanimalid = a["ID"] m.fcontactname = _("Shelter animal {0} '{1}'", l).format(a["CODE"], a["ANIMALNAME"]) m.fmicrochip = a["IDENTICHIPNUMBER"] m.fcontactnumber = a["SPECIESNAME"] m.fareafound = foundarea m.fareapostcode = foundpostcode m.fagegroup = a["AGEGROUP"] m.fsexid = a["SEX"] m.fsexname = a["SEXNAME"] m.fspeciesid = a["SPECIESID"] m.fspeciesname = a["SPECIESNAME"] m.fbreedid = a["BREEDID"] m.fbreedname = a["BREEDNAME"] m.fdistinguishingfeatures = a["MARKINGS"] m.fbasecolourid = a["BASECOLOURID"] m.fbasecolourname = a["BASECOLOURNAME"] m.fdatefound = a["DATEBROUGHTIN"] m.matchpoints = int( (float(matchpoints) / float(matchmax)) * 100.0) matches.append(m) if fullmatch: batch.append(m.toParams()) if limit > 0 and len(matches) >= limit: break if fullmatch: dbo.execute("DELETE FROM animallostfoundmatch") sql = "INSERT INTO animallostfoundmatch (AnimalLostID, AnimalFoundID, AnimalID, LostContactName, LostContactNumber, " \ "LostArea, LostPostcode, LostAgeGroup, LostSex, LostSpeciesID, LostBreedID, LostFeatures, LostBaseColourID, LostDate, " \ "LostMicrochipNumber, FoundMicrochipNumber, " \ "FoundContactName, FoundContactNumber, FoundArea, FoundPostcode, FoundAgeGroup, FoundSex, FoundSpeciesID, FoundBreedID, " \ "FoundFeatures, FoundBaseColourID, FoundDate, MatchPoints) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" if len(batch) > 0: dbo.execute_many(sql, batch) return matches
def search(dbo, session, q): """ Performs a database wide search for the term given. special tokens: a:term Only search animals for term ac:term Only search animal control incidents for term p:term Only search people for term la:term Only search lost animals for term li:num Only search licence numbers for term fa:term Only search found animals for term wl:term Only search waiting list entries for term sort:az Sort results alphabetically az sort:za Sort results alphabetically za sort:mr Sort results most recently changed first sort:lr Sort results least recently changed first -- update this list in header.js/bind_search/keywords activelost, activefound, onshelter/os, notforadoption, hold, holdtoday, quarantine, deceased, forpublish, people, vets, retailers, staff, fosterers, volunteers, shelters, aco, banned, homechecked, homecheckers, members, donors, drivers, reservenohomecheck, notmicrochipped, unsigned, signed returns a tuple of: results, timetaken, explain, sortname """ # ar (add results) inner method def ar(rlist, rtype, sortfield): # Return brief records to save bandwidth if rtype == "ANIMAL": rlist = asm3.animal.get_animals_brief(rlist) if rtype == "PERSON": pass # TODO: for r in rlist: r["RESULTTYPE"] = rtype if sortfield == "RELEVANCE": # How "relevant" is this record to what was searched for? # animal name and code weight higher than other elements. # Note that the code below modifies inbound var q, so by the # time we read it here, it should only contain the search term # itself. Weight everything else by last changed date so there # is some semblance of useful order for less relevant items qlow = q.lower() if rtype == "ANIMAL": r["SORTON"] = r["LASTCHANGEDDATE"] if r["SORTON"] is None: r["SORTON"] = THE_PAST if r["ANIMALNAME"].lower( ) == qlow or r["SHELTERCODE"].lower( ) == qlow or r["SHORTCODE"].lower() == qlow: r["SORTON"] = now() # Put matches where term present just behind direct matches elif r["ANIMALNAME"].lower().find( qlow) != -1 or r["SHELTERCODE"].lower().find( qlow) != -1 or r["SHORTCODE"].lower().find( qlow) != -1: r["SORTON"] = now() - datetime.timedelta(seconds=1) elif rtype == "PERSON": r["SORTON"] = r["LASTCHANGEDDATE"] if r["SORTON"] is None: r["SORTON"] = THE_PAST # Count how many of the keywords in the search were present # in the owner name field - if it's all of them then raise # the relevance. qw = qlow.split(" ") qm = 0 for w in qw: if r["OWNERNAME"].lower().find(w) != -1: qm += 1 if qm == len(qw): r["SORTON"] = now() # Put matches where term present just behind direct matches if r["OWNERSURNAME"].lower().find( qlow) or r["OWNERNAME"].lower().find(qlow): r["SORTON"] = now() - datetime.timedelta(seconds=1) elif rtype == "LICENCE": r["SORTON"] = r["ISSUEDATE"] if r["SORTON"] is None: r["SORTON"] = THE_PAST if r["LICENCENUMBER"].lower() == qlow: r["SORTON"] = now() else: r["SORTON"] = r["LASTCHANGEDDATE"] else: r["SORTON"] = r[sortfield] if r["SORTON"] is None and sortfield.endswith("DATE"): r["SORTON"] = THE_PAST results.append(r) l = dbo.locale # start the clock starttime = time.time() # The returned results results = [] # An i18n explanation of what was searched for explain = "" # Max records to be returned by search limit = asm3.configuration.record_search_limit(dbo) # Default sort for the search searchsort = asm3.configuration.search_sort(dbo) q = q.replace("'", "`") # Allow the sort to be overridden if q.find("sort:") != -1: if "sort:az" in q: searchsort = 0 q = q.replace("sort:az", "") elif "sort:za" in q: searchsort = 1 q = q.replace("sort:za", "") elif "sort:lr" in q: searchsort = 2 q = q.replace("sort:lr", "") elif "sort:mr" in q: searchsort = 3 q = q.replace("sort:mr", "") elif "sort:as" in q: searchsort = 4 q = q.replace("sort:as", "") elif "sort:sa" in q: searchsort = 5 q = q.replace("sort:sa", "") elif "sort:rel" in q: searchsort = 6 q = q.replace("sort:rel", "") q = q.strip() # Handle sorting =========================== animalsort = "" personsort = "" wlsort = "" acsort = "" lasort = "" lisort = "" fasort = "" sortdir = "a" sortname = "" # alphanumeric ascending if searchsort == 0: animalsort = "ANIMALNAME" personsort = "OWNERNAME" wlsort = "OWNERNAME" acsort = "OWNERNAME" lasort = "OWNERNAME" lisort = "OWNERNAME" fasort = "OWNERNAME" sortdir = "a" sortname = _("Alphabetically A-Z", l) # alphanumeric descending elif searchsort == 1: animalsort = "ANIMALNAME" personsort = "OWNERNAME" wlsort = "OWNERNAME" acsort = "OWNERNAME" lasort = "OWNERNAME" lisort = "OWNERNAME" fasort = "OWNERNAME" sortdir = "d" sortname = _("Alphabetically Z-A", l) # last changed ascending elif searchsort == 2: animalsort = "LASTCHANGEDDATE" personsort = "LASTCHANGEDDATE" wlsort = "LASTCHANGEDDATE" acsort = "LASTCHANGEDDATE" lasort = "LASTCHANGEDDATE" lisort = "ISSUEDATE" fasort = "LASTCHANGEDDATE" sortdir = "a" sortname = _("Least recently changed", l) # last changed descending elif searchsort == 3: animalsort = "LASTCHANGEDDATE" personsort = "LASTCHANGEDDATE" acsort = "LASTCHANGEDDATE" wlsort = "LASTCHANGEDDATE" lasort = "LASTCHANGEDDATE" lisort = "ISSUEDATE" fasort = "LASTCHANGEDDATE" sortdir = "d" sortname = _("Most recently changed", l) # species ascending elif searchsort == 4: animalsort = "SPECIESNAME" personsort = "OWNERNAME" acsort = "SPECIESNAME" wlsort = "SPECIESNAME" lasort = "SPECIESNAME" lisort = "COMMENTS" fasort = "SPECIESNAME" sortdir = "a" sortname = _("Species A-Z", l) elif searchsort == 5: animalsort = "SPECIESNAME" personsort = "OWNERNAME" acsort = "SPECIESNAME" wlsort = "SPECIESNAME" lasort = "SPECIESNAME" lisort = "COMMENTS" fasort = "SPECIESNAME" sortdir = "d" sortname = _("Species Z-A", l) elif searchsort == 6: animalsort = "RELEVANCE" personsort = "RELEVANCE" wlsort = "RELEVANCE" acsort = "RELEVANCE" lasort = "RELEVANCE" lisort = "RELEVANCE" fasort = "RELEVANCE" sortdir = "d" sortname = _("Most relevant", l) viewperson = asm3.users.check_permission_bool(session, asm3.users.VIEW_PERSON) viewanimal = asm3.users.check_permission_bool(session, asm3.users.VIEW_ANIMAL) viewstaff = asm3.users.check_permission_bool(session, asm3.users.VIEW_STAFF) viewvolunteer = asm3.users.check_permission_bool(session, asm3.users.VIEW_VOLUNTEER) user = session.user locationfilter = session.locationfilter siteid = session.siteid visibleanimalids = session.visibleanimalids # Special token searches if q == "onshelter" or q == "os": explain = _("All animals on the shelter.", l) if viewanimal: ar( asm3.animal.get_animal_find_simple( dbo, "", limit=limit, locationfilter=locationfilter, siteid=siteid, visibleanimalids=visibleanimalids), "ANIMAL", animalsort) elif q == "notforadoption": explain = _("All animals who are flagged as not for adoption.", l) if viewanimal: ar(asm3.animal.get_animals_not_for_adoption(dbo), "ANIMAL", animalsort) elif q == "longterm": explain = _( "All animals who have been on the shelter longer than {0} months.", l).format(asm3.configuration.long_term_months(dbo)) if viewanimal: ar(asm3.animal.get_animals_long_term(dbo), "ANIMAL", animalsort) elif q == "notmicrochipped": explain = _("All animals who have not been microchipped", l) if viewanimal: ar(asm3.animal.get_animals_not_microchipped(dbo), "ANIMAL", animalsort) elif q == "hold": explain = _("All animals who are currently held in case of reclaim.", l) if viewanimal: ar(asm3.animal.get_animals_hold(dbo), "ANIMAL", animalsort) elif q == "holdtoday": explain = _("All animals where the hold ends today.", l) if viewanimal: ar(asm3.animal.get_animals_hold_today(dbo), "ANIMAL", animalsort) elif q == "quarantine": explain = _("All animals who are currently quarantined.", l) if viewanimal: ar(asm3.animal.get_animals_quarantine(dbo), "ANIMAL", animalsort) elif q == "deceased": explain = _("Recently deceased shelter animals (last 30 days).", l) if viewanimal: ar(asm3.animal.get_animals_recently_deceased(dbo), "ANIMAL", animalsort) elif q == "forpublish": explain = _("All animals matching current publishing options.", l) if viewanimal: ar(asm3.publishers.base.get_animal_data(dbo), "ANIMAL", animalsort) elif q == "people": ar( asm3.person.get_person_find_simple(dbo, "", user, classfilter="all", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) explain = _("All people on file.", l) elif q == "vets": explain = _("All vets on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="vet", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "retailers": explain = _("All retailers on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="retailer", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "staff": explain = _("All staff on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="staff", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "fosterers": explain = _("All fosterers on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="fosterer", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "volunteers": explain = _("All volunteers on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="volunteer", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "shelters": explain = _("All animal shelters on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="shelter", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "aco": explain = _("All animal care officers on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="aco", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "banned": explain = _("All banned owners on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="banned", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "homechecked": explain = _("All homechecked owners on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="homechecked", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "homecheckers": explain = _("All homecheckers on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="homechecker", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "members": explain = _("All members on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="member", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "donors": explain = _("All donors on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="donor", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "drivers": explain = _("All drivers on file.", l) if viewperson: ar( asm3.person.get_person_find_simple( dbo, "", user, classfilter="driver", includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q == "reservenohomecheck": explain = _( "People with active reservations, but no homecheck has been done.", l) if viewperson: ar(asm3.person.get_reserves_without_homechecks(dbo), "PERSON", personsort) elif q == "overduedonations": explain = _("People with overdue donations.", l) if viewperson: ar(asm3.person.get_overdue_donations(dbo), "PERSON", personsort) elif q == "signed": explain = _("Document signing requests received in the last week", l) if viewperson: ar(asm3.person.get_signed_requests(dbo, 7), "PERSON", personsort) elif q == "unsigned": explain = _( "Document signing requests issued in the last month that are unsigned", l) if viewperson: ar(asm3.person.get_unsigned_requests(dbo, 31), "PERSON", personsort) elif q == "activelost": explain = _("Lost animals reported in the last 30 days.", l) if asm3.users.check_permission_bool(session, asm3.users.VIEW_LOST_ANIMAL): ar( asm3.lostfound.get_lostanimal_find_simple(dbo, "", limit=limit, siteid=siteid), "LOSTANIMAL", lasort) elif q == "activefound": explain = _("Found animals reported in the last 30 days.", l) if asm3.users.check_permission_bool(session, asm3.users.VIEW_FOUND_ANIMAL): ar( asm3.lostfound.get_foundanimal_find_simple(dbo, "", limit=limit, siteid=siteid), "FOUNDANIMAL", fasort) elif q.startswith("a:") or q.startswith("animal:"): q = q[q.find(":") + 1:].strip() explain = _("Animals matching '{0}'.", l).format(q) if viewanimal: ar( asm3.animal.get_animal_find_simple( dbo, q, limit=limit, locationfilter=locationfilter, siteid=siteid, visibleanimalids=visibleanimalids), "ANIMAL", animalsort) elif q.startswith("ac:") or q.startswith("animalcontrol:"): q = q[q.find(":") + 1:].strip() explain = _("Animal control incidents matching '{0}'.", l).format(q) if asm3.users.check_permission_bool(session, asm3.users.VIEW_INCIDENT): ar( asm3.animalcontrol.get_animalcontrol_find_simple( dbo, q, user, limit=limit, siteid=siteid), "ANIMALCONTROL", acsort) elif q.startswith("p:") or q.startswith("person:"): q = q[q.find(":") + 1:].strip() explain = _("People matching '{0}'.", l).format(q) if viewperson: ar( asm3.person.get_person_find_simple( dbo, q, user, includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) elif q.startswith("wl:") or q.startswith("waitinglist:"): q = q[q.find(":") + 1:].strip() explain = _("Waiting list entries matching '{0}'.", l).format(q) if asm3.users.check_permission_bool(session, asm3.users.VIEW_WAITING_LIST): ar( asm3.waitinglist.get_waitinglist_find_simple(dbo, q, limit=limit, siteid=siteid), "WAITINGLIST", wlsort) elif q.startswith("la:") or q.startswith("lostanimal:"): q = q[q.find(":") + 1:].strip() explain = _("Lost animal entries matching '{0}'.", l).format(q) if asm3.users.check_permission_bool(session, asm3.users.VIEW_LOST_ANIMAL): ar( asm3.lostfound.get_lostanimal_find_simple(dbo, q, limit=limit, siteid=siteid), "LOSTANIMAL", lasort) elif q.startswith("fa:") or q.startswith("foundanimal:"): q = q[q.find(":") + 1:].strip() explain = _("Found animal entries matching '{0}'.", l).format(q) if asm3.users.check_permission_bool(session, asm3.users.VIEW_FOUND_ANIMAL): ar( asm3.lostfound.get_foundanimal_find_simple(dbo, q, limit=limit, siteid=siteid), "FOUNDANIMAL", fasort) elif q.startswith("li:") or q.startswith("license:"): q = q[q.find(":") + 1:].strip() explain = _("License numbers matching '{0}'.", l).format(q) if asm3.users.check_permission_bool(session, asm3.users.VIEW_LICENCE): ar(asm3.financial.get_licence_find_simple(dbo, q, limit), "LICENCE", lisort) # No special tokens, search everything and collate else: if viewanimal: ar( asm3.animal.get_animal_find_simple( dbo, q, limit=limit, locationfilter=locationfilter, siteid=siteid, visibleanimalids=visibleanimalids), "ANIMAL", animalsort) if asm3.users.check_permission_bool(session, asm3.users.VIEW_INCIDENT): ar( asm3.animalcontrol.get_animalcontrol_find_simple( dbo, q, user, limit=limit, siteid=siteid), "ANIMALCONTROL", acsort) if viewperson: ar( asm3.person.get_person_find_simple( dbo, q, user, includeStaff=viewstaff, includeVolunteers=viewvolunteer, limit=limit, siteid=siteid), "PERSON", personsort) if asm3.users.check_permission_bool(session, asm3.users.VIEW_WAITING_LIST): ar( asm3.waitinglist.get_waitinglist_find_simple(dbo, q, limit=limit, siteid=siteid), "WAITINGLIST", wlsort) if asm3.users.check_permission_bool(session, asm3.users.VIEW_LOST_ANIMAL): ar( asm3.lostfound.get_lostanimal_find_simple(dbo, q, limit=limit, siteid=siteid), "LOSTANIMAL", lasort) if asm3.users.check_permission_bool(session, asm3.users.VIEW_FOUND_ANIMAL): ar( asm3.lostfound.get_foundanimal_find_simple(dbo, q, limit=limit, siteid=siteid), "FOUNDANIMAL", fasort) if asm3.users.check_permission_bool(session, asm3.users.VIEW_LICENCE): ar(asm3.financial.get_licence_find_simple(dbo, q, limit), "LICENCE", lisort) explain = _("Results for '{0}'.", l).format(q) # Apply the sort to the results # We return a tuple to the sorted function which forces rows with None in the # SORTON key to the end (True, None) for None values, (False, value) for items if sortdir == "a": sortresults = sorted(results, key=lambda k: (k["SORTON"] is None, k["SORTON"])) else: sortresults = sorted(results, reverse=True, key=lambda k: (k["SORTON"] is not None, k["SORTON"])) # stop the clock timetaken = (time.time() - starttime) # Return our final set of values return sortresults, timetaken, explain, sortname
def checkoutUrl(self, payref, return_url="", item_description=""): asm_debug("%s %s %s" % (payref, return_url, item_description), "cardcom.checkoutUrl", self.dbo) payments = self.getPayments(payref) total_charge_sum = sum( round(r.DONATION, 2) for r in payments ) client_reference_id = "%s-%s" % (self.dbo.database, payref) # prefix database to payref OwnerID = payments[0].OWNERID p = asm3.person.get_person(self.dbo, OwnerID) if not asm3.utils.is_valid_email_address(p.EMAILADDRESS): raise Exception(_("Invalid email address")) url = "https://secure.cardcom.solutions/Interface/LowProfile.aspx" params = { "TerminalNumber": asm3.configuration.cardcom_terminalnumber(self.dbo), "UserName": asm3.configuration.cardcom_username(self.dbo), "SumToBill": str(total_charge_sum / 100.0), "CoinID": "1", # TODO: not critical - use ASM currency "Language": "he", # TODO: not critical - config / use locale? "SuccessRedirectUrl": asm3.configuration.cardcom_successurl(self.dbo), # "https://secure.cardcom.solutions/DealWasSuccessful.aspx", "ErrorRedirectUrl": asm3.configuration.cardcom_errorurl(self.dbo), # "https://secure.cardcom.solutions/DealWasUnSuccessful.aspx?customVar=1234", "APILevel": "10", "IsVirtualTerminalMode": "true", "codepage": "65001", # unicode "ReturnValue": client_reference_id, "IndicatorUrl": "%s/pp_cardcom" % BASE_URL, } # determine operation by total charge amount. If charge amount>0, charge and create token (this will also create a tax document). If charge amount==0, only create token more_params = {} if total_charge_sum > 0: more_params = { "Operation": "2", # charge + create token "DocTypeToCreate": asm3.configuration.cardcom_documenttype(self.dbo), # 3 = nonprofit receipt "InvoiceHead.CustName": asm3.utils.decode_html(p.OWNERNAME)[:50] , "InvoiceHead.CustAddresLine1": asm3.utils.decode_html(p.OWNERADDRESS)[:50], "InvoiceHead.CustCity": asm3.utils.decode_html(p.OWNERTOWN)[:50], "InvoiceHead.CustMobilePH": asm3.utils.decode_html(p.MOBILETELEPHONE)[:50], "InvoiceHead.ExtIsVatFree": "true",# no VAT for nonprofit receipts. TODO: config? "InvoiceHead.SendByEmail": "true", # TODO: not critical - config? "InvoiceHead.Language": "he", # TODO: not critical - config / use locale? "InvoiceHead.Email": p.EMAILADDRESS, } for p in self.getInvoiceItems(payments): more_params.update(p) else: # if total_charge_sum == 0: more_params = { "Operation": "3" # only create token } params.update(more_params) asm_debug("params: %s" % params, "cardcom.checkoutUrl", self.dbo) r = asm3.utils.post_form(url, params) asm_debug("response %s, text: %s" % (r["status"], r["response"]), "cardcom.checkoutUrl", self.dbo) if not r["status"] < 400: raise Exception("Response not ok: %s %s" % (r["status"], r["response"])) cardcom_reply = asm3.utils.parse_qs(r["response"]) if "url" not in cardcom_reply: raise Exception("No url in response text: %s" % r["response"]) asm_debug("return cardcom url: %s" % cardcom_reply['url'], "cardcom.checkoutUrl", self.dbo) return cardcom_reply['url']
def handler(post, path, remoteip, referer, querystring): """ Handles the various service method types. post: The GET/POST parameters path: The current system path/code.PATH remoteip: The IP of the caller referer: The referer HTTP header querystring: The complete querystring return value is a tuple containing MIME type, max-age, content """ # Get service parameters account = post["account"] username = post["username"] password = post["password"] method = post["method"] animalid = post.integer("animalid") formid = post.integer("formid") mediaid = post.integer("mediaid") seq = post.integer("seq") title = post["title"] strip_personal = post.integer("sensitive") == 0 # If this method is in the cache protected list, only use # whitelisted parameters for the key to prevent callers # cache-busting by adding junk parameters cache_key = querystring.replace(" ", "") if method in CACHE_PROTECT_METHODS: cache_key = safe_cache_key(method, cache_key) # Do we have a cached response for these parameters? cached_response = get_cached_response(cache_key, account) if cached_response is not None: asm3.al.debug( "cache hit: %s (%d bytes)" % (cache_key, len(cached_response[3])), "service.handler", account) return cached_response # Are we dealing with multiple databases, but no account was specified? if account == "" and MULTIPLE_DATABASES: return ("text/plain", 0, 0, "ERROR: No database/alias specified") # Is flood protection activated for this method? if method in FLOOD_PROTECT_METHODS: flood_protect(method, remoteip) dbo = asm3.db.get_database(account) if dbo.database in asm3.db.ERROR_VALUES: asm3.al.error( "auth failed - invalid smaccount %s from %s (%s)" % (account, remoteip, dbo.database), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid database (%s)" % dbo.database) # If the database has disabled the service API, stop now if not asm3.configuration.service_enabled(dbo): asm3.al.error("Service API is disabled (%s)" % method, "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Service API is disabled") # Do any database updates need doing in this db? dbo.installpath = path if asm3.dbupdate.check_for_updates(dbo): asm3.dbupdate.perform_updates(dbo) # Does the method require us to authenticate? If so, do it. user = None securitymap = "" if method in AUTH_METHODS: # If the database has authenticated service methods disabled, stop now if not asm3.configuration.service_auth_enabled(dbo): asm3.al.error( "Service API for auth methods is disabled (%s)" % method, "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Service API for authenticated methods is disabled") user = asm3.users.authenticate(dbo, username, password) if user is None: asm3.al.error( "auth failed - %s/%s is not a valid username/password from %s" % (username, password, remoteip), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid username and password") securitymap = asm3.users.get_security_map(dbo, user["USERNAME"]) # Get the preferred locale and timezone for the site l = asm3.configuration.locale(dbo) dbo.locale = l dbo.timezone = asm3.configuration.timezone(dbo) dbo.timezone_dst = asm3.configuration.timezone_dst(dbo) asm3.al.info("call @%s --> %s [%s]" % (username, method, querystring), "service.handler", dbo) if method == "animal_image": hotlink_protect("animal_image", referer) if asm3.utils.cint(animalid) == 0: asm3.al.error( "animal_image failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: dummy, data = asm3.media.get_image_file_data( dbo, "animal", asm3.utils.cint(animalid), seq) if data == "NOPIC": dummy, data = asm3.media.get_image_file_data(dbo, "nopic", 0) return set_cached_response(cache_key, account, "image/jpeg", 86400, 3600, data) elif method == "animal_thumbnail": if asm3.utils.cint(animalid) == 0: asm3.al.error( "animal_thumbnail failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: dummy, data = asm3.media.get_image_file_data( dbo, "animalthumb", asm3.utils.cint(animalid), seq) if data == "NOPIC": dummy, data = asm3.media.get_image_file_data(dbo, "nopic", 0) return set_cached_response(cache_key, account, "image/jpeg", 86400, 3600, data) elif method == "animal_view": if asm3.utils.cint(animalid) == 0: asm3.al.error( "animal_view failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: return set_cached_response( cache_key, account, "text/html", 86400, 120, asm3.publishers.html.get_animal_view( dbo, asm3.utils.cint(animalid))) elif method == "animal_view_adoptable_js": return set_cached_response( cache_key, account, "application/javascript", 10800, 600, asm3.publishers.html.get_animal_view_adoptable_js(dbo)) elif method == "animal_view_adoptable_html": return set_cached_response( cache_key, account, "text/html", 86400, 120, asm3.publishers.html.get_animal_view_adoptable_html(dbo)) elif method == "checkout": processor = asm3.financial.get_payment_processor( dbo, post["processor"]) if not processor.validatePaymentReference(post["payref"]): return ("text/plain", 0, 0, "ERROR: Invalid payref") if processor.isPaymentReceived(post["payref"]): return ("text/plain", 0, 0, "ERROR: Expired payref") return_url = post["return"] or asm3.configuration.payment_return_url( dbo) return set_cached_response( cache_key, account, "text/html", 120, 120, processor.checkoutPage(post["payref"], return_url, title)) elif method == "dbfs_image": hotlink_protect("dbfs_image", referer) return set_cached_response( cache_key, account, "image/jpeg", 86400, 86400, asm3.utils.iif(title.startswith("/"), asm3.dbfs.get_string_filepath(dbo, title), asm3.dbfs.get_string(dbo, title))) elif method == "document_repository": return set_cached_response( cache_key, account, asm3.media.mime_type(asm3.dbfs.get_name_for_id(dbo, mediaid)), 86400, 86400, asm3.dbfs.get_string_id(dbo, mediaid)) elif method == "extra_image": hotlink_protect("extra_image", referer) return set_cached_response( cache_key, account, "image/jpeg", 86400, 86400, asm3.dbfs.get_string(dbo, title, "/reports")) elif method == "media_image": hotlink_protect("media_image", referer) return set_cached_response( cache_key, account, "image/jpeg", 86400, 86400, asm3.dbfs.get_string_id( dbo, dbo.query_int("select dbfsid from media where id = ?", [mediaid]))) elif method == "json_adoptable_animal": if asm3.utils.cint(animalid) == 0: asm3.al.error( "json_adoptable_animal failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data( dbo, None, asm3.utils.cint(animalid), include_additional_fields=True) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "html_adoptable_animals": return set_cached_response(cache_key, account, "text/html", 10800, 1800, \ asm3.publishers.html.get_adoptable_animals(dbo, style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"), locationid=post.integer("locationid"))) elif method == "html_adopted_animals": return set_cached_response(cache_key, account, "text/html", 10800, 1800, \ asm3.publishers.html.get_adopted_animals(dbo, daysadopted=post.integer("days"), style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"))) elif method == "html_deceased_animals": return set_cached_response(cache_key, account, "text/html", 10800, 1800, \ asm3.publishers.html.get_deceased_animals(dbo, daysdeceased=post.integer("days"), style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"))) elif method == "html_flagged_animals": if post["flag"] == "": asm3.al.error("html_flagged_animals requested with no flag.", "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid flag") return set_cached_response(cache_key, account, "text/html", 10800, 1800, \ asm3.publishers.html.get_flagged_animals(dbo, style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"), flag=post["flag"], allanimals=post.integer("all"))) elif method == "html_held_animals": return set_cached_response(cache_key, account, "text/html", 10800, 1800, \ asm3.publishers.html.get_held_animals(dbo, style=post["template"], \ speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"))) elif method == "json_adoptable_animals_xp": rs = strip_personal_data( asm3.publishers.base.get_animal_data( dbo, None, include_additional_fields=True)) return set_cached_response(cache_key, account, "application/json", 600, 600, asm3.utils.json(rs)) elif method == "json_adoptable_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data( dbo, None, include_additional_fields=True) if strip_personal: rs = strip_personal_data(rs) return set_cached_response(cache_key, account, "application/json", 600, 600, asm3.utils.json(rs)) elif method == "jsonp_adoptable_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data( dbo, None, include_additional_fields=True) if strip_personal: rs = strip_personal_data(rs) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_adoptable_animal": if asm3.utils.cint(animalid) == 0: asm3.al.error( "xml_adoptable_animal failed, %s is not an animalid" % str(animalid), "service.handler", dbo) return ("text/plain", 0, 0, "ERROR: Invalid animalid") else: asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data( dbo, None, asm3.utils.cint(animalid), include_additional_fields=True) return set_cached_response(cache_key, account, "application/xml", 600, 600, asm3.html.xml(rs)) elif method == "xml_adoptable_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.publishers.base.get_animal_data( dbo, None, include_additional_fields=True) if strip_personal: rs = strip_personal_data(rs) return set_cached_response(cache_key, account, "application/xml", 600, 600, asm3.html.xml(rs)) elif method == "json_found_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_FOUND_ANIMAL) rs = asm3.lostfound.get_foundanimal_last_days(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "jsonp_found_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_FOUND_ANIMAL) rs = asm3.lostfound.get_foundanimal_last_days(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_found_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_FOUND_ANIMAL) rs = asm3.lostfound.get_foundanimal_last_days(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.html.xml(rs)) elif method == "json_held_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.animal.get_animals_hold(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "xml_held_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.animal.get_animals_hold(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.html.xml(rs)) elif method == "jsonp_held_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.animal.get_animals_hold(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "json_lost_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_LOST_ANIMAL) rs = asm3.lostfound.get_lostanimal_last_days(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "jsonp_lost_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_LOST_ANIMAL) rs = asm3.lostfound.get_lostanimal_last_days(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_lost_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_LOST_ANIMAL) rs = asm3.lostfound.get_lostanimal_last_days(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.html.xml(rs)) elif method == "json_recent_adoptions": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.movement.get_recent_adoptions(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.utils.json(rs)) elif method == "jsonp_recent_adoptions": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.movement.get_recent_adoptions(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rs))) elif method == "xml_recent_adoptions": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) rs = asm3.movement.get_recent_adoptions(dbo) return set_cached_response(cache_key, account, "application/xml", 3600, 3600, asm3.html.xml(rs)) elif method == "html_report": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_REPORT) crid = asm3.reports.get_id(dbo, title) p = asm3.reports.get_criteria_params(dbo, crid, post) rhtml = asm3.reports.execute(dbo, crid, username, p) rhtml = asm3.utils.fix_relative_document_uris(dbo, rhtml) return set_cached_response(cache_key, account, "text/html", 600, 600, rhtml) elif method == "csv_mail" or method == "csv_report": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_REPORT) crid = asm3.reports.get_id(dbo, title) p = asm3.reports.get_criteria_params(dbo, crid, post) rows, cols = asm3.reports.execute_query(dbo, crid, username, p) mcsv = asm3.utils.csv(l, rows, cols, True) return set_cached_response(cache_key, account, "text/csv", 600, 600, mcsv) elif method == "json_report" or method == "json_mail": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_REPORT) crid = asm3.reports.get_id(dbo, title) p = asm3.reports.get_criteria_params(dbo, crid, post) rows, cols = asm3.reports.execute_query(dbo, crid, username, p) return set_cached_response(cache_key, account, "application/json", 600, 600, asm3.utils.json(rows)) elif method == "jsonp_report" or method == "jsonp_mail": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_REPORT) crid = asm3.reports.get_id(dbo, title) p = asm3.reports.get_criteria_params(dbo, crid, post) rows, cols = asm3.reports.execute_query(dbo, crid, username, p) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(rows))) elif method == "jsonp_recent_changes": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_recent_changes(dbo) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(sa))) elif method == "json_recent_changes": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_recent_changes(dbo) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.utils.json(sa)) elif method == "xml_recent_changes": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_recent_changes(dbo) return set_cached_response(cache_key, account, "application/xml", 3600, 3600, asm3.html.xml(sa)) elif method == "jsonp_shelter_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_shelter_animals(dbo) if strip_personal: sa = strip_personal_data(sa) return ("application/javascript", 0, 0, "%s(%s);" % (post["callback"], asm3.utils.json(sa))) elif method == "json_shelter_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_shelter_animals(dbo) if strip_personal: sa = strip_personal_data(sa) return set_cached_response(cache_key, account, "application/json", 3600, 3600, asm3.utils.json(sa)) elif method == "xml_shelter_animals": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) sa = asm3.animal.get_shelter_animals(dbo) if strip_personal: sa = strip_personal_data(sa) return set_cached_response(cache_key, account, "application/xml", 3600, 3600, asm3.html.xml(sa)) elif method == "rss_timeline": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.VIEW_ANIMAL) return set_cached_response(cache_key, account, "application/rss+xml", 3600, 3600, asm3.html.timeline_rss(dbo)) elif method == "upload_animal_image": asm3.users.check_permission_map(l, user["SUPERUSER"], securitymap, asm3.users.ADD_MEDIA) asm3.media.attach_file_from_form(dbo, username, asm3.media.ANIMAL, int(animalid), post) return ("text/plain", 0, 0, "OK") elif method == "online_form_html": if formid == 0: raise asm3.utils.ASMError( "method online_form_html requires a valid formid") return set_cached_response( cache_key, account, "text/html; charset=utf-8", 120, 120, asm3.onlineform.get_onlineform_html(dbo, formid)) elif method == "online_form_json": if formid == 0: raise asm3.utils.ASMError( "method online_form_json requires a valid formid") return set_cached_response( cache_key, account, "application/json; charset=utf-8", 30, 30, asm3.onlineform.get_onlineform_json(dbo, formid)) elif method == "online_form_post": asm3.onlineform.insert_onlineformincoming_from_form( dbo, post, remoteip) redirect = post["redirect"] if redirect == "": redirect = BASE_URL + "/static/pages/form_submitted.html" return ("redirect", 0, 0, redirect) elif method == "sign_document": if formid == 0: raise asm3.utils.ASMError( "method sign_document requires a valid formid") if post["sig"] == "": m = asm3.media.get_media_by_id(dbo, formid) if m is None: raise asm3.utils.ASMError("invalid link") token = asm3.utils.md5_hash_hex("%s%s" % (m.ID, m.LINKID)) if token != post["token"]: raise asm3.utils.ASMError("invalid token") return set_cached_response( cache_key, account, "text/html", 2, 2, sign_document_page(dbo, formid, post["email"])) else: asm3.media.sign_document(dbo, "service", formid, post["sig"], post["signdate"], "signemail") asm3.media.create_log(dbo, "service", formid, "ES02", _("Document signed", l)) if post.boolean("sendsigned"): m = asm3.media.get_media_by_id(dbo, formid) if m is None: raise asm3.utils.ASMError("cannot find %s" % formid) content = asm3.utils.bytes2str( asm3.dbfs.get_string(dbo, m.MEDIANAME)) contentpdf = asm3.utils.html_to_pdf(dbo, content) attachments = [("%s.pdf" % m.ID, "application/pdf", contentpdf) ] fromaddr = asm3.configuration.email(dbo) asm3.utils.send_email(dbo, fromaddr, post["email"], "", "", _("Signed Document", l), m.MEDIANOTES, "plain", attachments) return ("text/plain", 0, 0, "OK") else: asm3.al.error("invalid method '%s'" % method, "service.handler", dbo) raise asm3.utils.ASMError("Invalid method '%s'" % method)
def checkout_adoption_post(dbo, post): """ Called by the adoption checkout frontend with the document signature and donation amount. Handles the document signing, triggers creation of the payment records, etc. Returns the URL needed to redirect to the payment processor to complete payment. """ l = dbo.locale co = asm3.cachedisk.get(post["token"], dbo.database) if co is None: raise asm3.utils.ASMError("invalid token") mediaid = co["mediaid"] donationamt = post.integer("donationamt") * 100 # Sign the docs if they haven't been done already if not asm3.media.has_signature(dbo, mediaid): signdate = "%s %s" % (python2display( l, dbo.now()), format_time(dbo.now())) asm3.media.sign_document(dbo, "service", mediaid, post["sig"], signdate, "signemail") if post.boolean("sendsigned"): m = asm3.media.get_media_by_id(dbo, mediaid) if m is None: raise asm3.utils.ASMError("cannot find %s" % mediaid) content = asm3.utils.bytes2str( asm3.dbfs.get_string(dbo, m.MEDIANAME)) contentpdf = asm3.utils.html_to_pdf(dbo, content) attachments = [("%s.pdf" % m.ID, "application/pdf", contentpdf)] asm3.utils.send_email(dbo, asm3.configuration.email(dbo), co["email"], "", "", _("Signed Document", l), m.MEDIANOTES, "plain", attachments) # Create the due payment records if they haven't been done already, along with a receipt/payref if co["paymentfeeid"] == 0: co["paymentprocessor"] = asm3.configuration.adoption_checkout_processor( dbo) co["receiptnumber"] = asm3.financial.get_next_receipt_number( dbo) # Both go on the same receipt co["payref"] = "%s-%s" % (co["personcode"], co["receiptnumber"]) # Adoption Fee co["paymentfeeid"] = asm3.financial.insert_donation_from_form( dbo, "checkout", asm3.utils.PostedData( { "person": str(co["personid"]), "animal": str(co["animalid"]), "movement": str(co["movementid"]), "type": asm3.configuration.adoption_checkout_feeid(dbo), "payment": asm3.configuration.adoption_checkout_payment_method(dbo), "amount": co["fee"], "due": python2display(l, dbo.now()), "receiptnumber": co["receiptnumber"], "giftaid": str(co["giftaid"]) }, l)) # Donation (not linked to movement on purpose to avoid showing on adoption fee reports) if donationamt > 0: co["paymentdonid"] = asm3.financial.insert_donation_from_form( dbo, "checkout", asm3.utils.PostedData( { "person": str(co["personid"]), "animal": str(co["animalid"]), "type": str( asm3.configuration.adoption_checkout_donationid( dbo)), "payment": str( asm3.configuration. adoption_checkout_payment_method(dbo)), "amount": str(donationamt), "due": python2display(l, dbo.now()), "receiptnumber": co["receiptnumber"], "giftaid": str(co["giftaid"]) }, l)) # Update the cache entry asm3.cachedisk.put(post["token"], dbo.database, co, 86400 * 2) elif co["paymentdonid"] > 0 and donationamt > 0: # payments have already been created, must be a user revisiting the checkout. # update their donation amount in case they made a different choice this time. dbo.update("ownerdonation", co["paymentdonid"], {"Donation": donationamt}, "checkout") elif co["paymentdonid"] > 0 and donationamt == 0: # The user has changed their voluntary donation amount to 0 - delete it dbo.delete("ownerdonation", co["paymentdonid"], "checkout") # Construct the payment checkout URL params = { "account": dbo.database, "method": "checkout", "processor": co["paymentprocessor"], "payref": co["payref"], "title": _("{0}: Adoption fee") + asm3.utils.iif(co["paymentdonid"] != "0", _(" + donation"), "") } url = "%s?%s" % (SERVICE_URL, asm3.utils.urlencode(params)) return url
# tablename : ( tablelabel, namefield, namelabel, descfield, modifiers,(foreignkeys) ) # modifiers: # add - add new records # del - can delete # ret - can retire a value # species - has a SpeciesID column (breed) # pubspec - has a PetFinderSpecies column (species) # pubbreed - has a PetFinderBreed column (breed) # pubcol - has an AdoptAPetColour column (basecolour) # sched - has a RescheduleDays column (vaccinationtype) # cost - has a DefaultCost column (citationtype, costtype, donationtype, licencetype) # units - has Units column (internallocation) # site - has SiteID column (internallocation) # vat - has an IsVAT column (donationtype) LOOKUP_TABLES = { "lksaccounttype": (_("Account Types"), "AccountType", _("Type"), "", "", ("accounts.AccountType", )), "lkanimalflags": (_("Animal Flags"), "Flag", _("Flag"), "", "add del", ""), "animaltype": (_("Animal Types"), "AnimalType", _("Type"), "AnimalDescription", "add del ret", ("animal.AnimalTypeID", )), "basecolour": (_("Colors"), "BaseColour", _("Color"), "BaseColourDescription", "add del ret pubcol", ("animal.BaseColourID", "animallost.BaseColourID", "animalfound.BaseColourID")), "breed": (_("Breeds"), "BreedName", _("Breed"), "BreedDescription", "add del ret species pubbreed", ("animal.BreedID", "animal.Breed2ID", "animallost.BreedID", "animalfound.BreedID")), "lkcoattype": (_("Coat Types"), "CoatType", _("Coat Type"), "", "add del", ("animal.CoatType", )),
# tablename : ( tablelabel, namefield, namelabel, descfield, modifiers,(foreignkeys) ) # modifiers: # add - add new records # del - can delete # ret - can retire a value # species - has a SpeciesID column (breed) # pubspec - has a PetFinderSpecies column (species) # pubbreed - has a PetFinderBreed column (breed) # pubcol - has an AdoptAPetColour column (basecolour) # sched - has a RescheduleDays column (vaccinationtype) # cost - has a DefaultCost column (citationtype, costtype, donationtype, licencetype) # units - has Units column (internallocation) # site - has SiteID column (internallocation) # vat - has an IsVAT column (donationtype) LOOKUP_TABLES = { "lksaccounttype": (_("Account Types"), "AccountType", _("Type"), "", "", ("accounts.AccountType",)), "lkanimalflags": (_("Animal Flags"), "Flag", _("Flag"), "", "add del", ""), "animaltype": (_("Animal Types"), "AnimalType", _("Type"), "AnimalDescription", "add del ret", ("animal.AnimalTypeID",)), "basecolour": (_("Colors"), "BaseColour", _("Color"), "BaseColourDescription", "add del ret pubcol", ("animal.BaseColourID", "animallost.BaseColourID", "animalfound.BaseColourID")), "breed": (_("Breeds"), "BreedName", _("Breed"), "BreedDescription", "add del ret species pubbreed", ("animal.BreedID", "animal.Breed2ID", "animallost.BreedID", "animalfound.BreedID")), "lkcoattype": (_("Coat Types"), "CoatType", _("Coat Type"), "", "add del", ("animal.CoatType",)), "citationtype": (_("Citation Types"), "CitationName", _("Citation Type"), "CitationDescription", "add del ret cost", ("ownercitation.CitationTypeID",)), "lksclinicstatus": (_("Clinic Statuses"), "Status", _("Status"), "", "", ("clinicappointment.Status",)), "costtype": (_("Cost Types"), "CostTypeName", _("Cost Type"), "CostTypeDescription", "add del ret cost", ("animalcost.CostTypeID",)), "deathreason": (_("Death Reasons"), "ReasonName", _("Reason"), "ReasonDescription", "add del ret", ("animal.PTSReasonID",)), "diet": (_("Diets"), "DietName", _("Diet"), "DietDescription", "add del ret", ("animaldiet.DietID",)), "donationpayment": (_("Payment Methods"), "PaymentName", _("Type"), "PaymentDescription", "add del ret", ("ownerdonation.DonationPaymentID",)), "donationtype": (_("Payment Types"), "DonationName", _("Type"), "DonationDescription", "add del ret cost vat", ("ownerdonation.DonationTypeID", "accounts.DonationTypeID")), "entryreason": (_("Entry Reasons"), "ReasonName", _("Reason"), "ReasonDescription", "add del ret", ("animal.EntryReasonID", "adoption.ReturnedReasonID") ), "incidentcompleted":(_("Incident Completed Types"), "CompletedName", _("Completed Type"), "CompletedDescription", "add del ret", ("animalcontrol.IncidentCompletedID",)), "incidenttype": (_("Incident Types"), "IncidentName", _("Type"), "IncidentDescription", "add del ret", ("animalcontrol.IncidentTypeID",)),
def create_animal(dbo, username, wlid): """ Creates an animal record from a waiting list entry with the id given """ l = dbo.locale a = dbo.first_row( dbo.query("SELECT * FROM animalwaitinglist WHERE ID = ?", [wlid])) data = { "animalname": _("Waiting List {0}", l).format(wlid), "markings": str(a["ANIMALDESCRIPTION"]), "reasonforentry": str(a["REASONFORWANTINGTOPART"]), "species": str(a["SPECIESID"]), "hiddenanimaldetails": str(a["COMMENTS"]), "broughtinby": str(a["OWNERID"]), "originalowner": str(a["OWNERID"]), "animaltype": asm3.configuration.default_type(dbo), "entryreason": asm3.configuration.default_entry_reason(dbo), "breed1": asm3.configuration.default_breed(dbo), "breed2": asm3.configuration.default_breed(dbo), "basecolour": asm3.configuration.default_colour(dbo), "size": asm3.configuration.default_size(dbo), "internallocation": asm3.configuration.default_location(dbo), "dateofbirth": python2display(l, subtract_years(now(dbo.timezone))), "estimateddob": "1" } # If we aren't showing the time brought in, set it to midnight if not asm3.configuration.add_animals_show_time_brought_in(dbo): data["timebroughtin"] = "00:00:00" # If we're creating shelter codes manually, we need to put something unique # in there for now. Use the id if asm3.configuration.manual_codes(dbo): data["sheltercode"] = "WL" + str(wlid) data["shortcode"] = "WL" + str(wlid) nextid, code = asm3.animal.insert_animal_from_form( dbo, asm3.utils.PostedData(data, l), username) # Now that we've created our animal, we should remove this entry from the waiting list dbo.update( "animalwaitinglist", wlid, { "DateRemovedFromList": dbo.today(), "ReasonForRemoval": _("Moved to animal record {0}", l).format(code) }, username) # If there were any logs and media entries on the waiting list, create them on the animal # Media for me in dbo.query( "SELECT * FROM media WHERE LinkTypeID = ? AND LinkID = ?", (asm3.media.WAITINGLIST, wlid)): ext = me.medianame ext = ext[ext.rfind("."):].lower() mediaid = dbo.get_id("media") medianame = "%d%s" % (mediaid, ext) dbo.insert( "media", { "ID": mediaid, "DBFSID": 0, "MediaSize": 0, "MediaName": medianame, "MediaMimeType": asm3.media.mime_type(medianame), "MediaType": me.mediatype, "MediaNotes": me.medianotes, "WebsitePhoto": me.websitephoto, "WebsiteVideo": me.websitevideo, "DocPhoto": me.docphoto, "ExcludeFromPublish": me.excludefrompublish, # ASM2_COMPATIBILITY "NewSinceLastPublish": 1, "UpdatedSinceLastPublish": 0, # ASM2_COMPATIBILITY "LinkID": nextid, "LinkTypeID": asm3.media.ANIMAL, "Date": me.date, "CreatedDate": me.createddate, "RetainUntil": me.retainuntil }, generateID=False) # Now clone the dbfs item pointed to by this media item if it's a file if me.mediatype == asm3.media.MEDIATYPE_FILE: filedata = asm3.dbfs.get_string(dbo, me.medianame) dbfsid = asm3.dbfs.put_string(dbo, medianame, "/animal/%d" % nextid, filedata) dbo.execute( "UPDATE media SET DBFSID = ?, MediaSize = ? WHERE ID = ?", (dbfsid, len(filedata), mediaid)) # Logs for lo in dbo.query("SELECT * FROM log WHERE LinkType = ? AND LinkID = ?", (asm3.log.WAITINGLIST, wlid)): dbo.insert( "log", { "LinkID": nextid, "LinkType": asm3.log.ANIMAL, "LogTypeID": lo.LOGTYPEID, "Date": lo.DATE, "Comments": lo.COMMENTS }, username) return nextid