Exemple #1
0
 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)
Exemple #2
0
def flood_protect(method, remoteip):
    """ 
    Implements flood protection for methods.
    Keeps a list of timestamps in an in memory cache for the method and IP address.
    If this IP makes more than the request limit for the period, the request is rejected 
        and the IP banned for a period.
    method: The service method we're protecting
    remoteip: The ip address of the caller
    """
    CACHE_TTL = 120  # Flood protection only operates for a minute or so keep entry alive for a couple
    remoteip = str(remoteip).replace(
        ", ", "")  # X-FORWARDED-FOR can be a list, remove commas
    cache_key = "m%sr%s" % (method, remoteip)
    # Get the entry for this IP
    v = asm3.cachemem.get(cache_key)
    if v is None:
        v = {
            "b": None,
            "h": [asm3.i18n.now()]
        }  # b = banned until, h = list of hits as timestamps
        asm3.cachemem.put(cache_key, v, CACHE_TTL)
    else:
        # asm3.al.debug("protecting '%s' from '%s': cache: %s" % (method, remoteip, v), "service.flood_protect")
        # Is this IP banned?
        if v["b"] is not None and now() < v["b"]:
            asm3.al.error(
                "%s is banned from calling '%s' until '%s'" %
                (remoteip, method, v["b"]), "service.flood_protect")
            message = "You cannot call '%s' until '%s'" % (method, v["b"])
            raise asm3.utils.ASMError(message)
        # Add a hit for now
        v["h"].append(now())
        request_limit, periods, banneds = FLOOD_PROTECT_METHODS[method]
        # Calculate how long ago period in s was and how many requests this IP has made in the period
        cutoff = subtract_seconds(now(), periods)
        requests_in_period = 0
        for d in v["h"]:
            if d > cutoff: requests_in_period += 1
        # Are we over the limit?
        if requests_in_period > request_limit:
            v["b"] = add_seconds(
                now(), banneds
            )  # Mark this IP banned for this method for the ban period
            asm3.cachemem.put(cache_key, v, CACHE_TTL)
            asm3.al.error(
                "%s has called '%s', %s times in the last %d seconds. Banning until '%s' (%s seconds)"
                % (remoteip, method, request_limit, periods, v["b"], banneds),
                "service.flood_protect")
            message = "You have already called '%s', %s times in the last %d seconds, please wait %d seconds before trying again." % (
                method, request_limit, periods, banneds)
            raise asm3.utils.ASMError(message)
        else:
            # Update the cache with the new hit and continue
            asm3.cachemem.put(cache_key, v, CACHE_TTL)
Exemple #3
0
def auto_update_urgencies(dbo):
    """
    Finds all animals where the next UrgencyUpdateDate field is greater
    than or equal to today and the urgency is larger than High (so we
    can never reach Urgent).
    """
    update_period_days = asm3.configuration.waiting_list_urgency_update_period(
        dbo)
    if update_period_days == 0:
        asm3.al.debug(
            "urgency update period is 0, not updating waiting list entries",
            "waitinglist.auto_update_urgencies", dbo)
        return
    rows = dbo.query("SELECT a.* " \
        "FROM animalwaitinglist a WHERE UrgencyUpdateDate <= ? " \
        "AND Urgency > 2", [dbo.today()])
    updates = []
    for r in rows:
        asm3.al.debug("increasing urgency of waitinglist entry %d" % r.ID,
                      "waitinglist.auto_update_urgencies", dbo)
        updates.append((now(dbo.timezone),
                        add_days(r.URGENCYUPDATEDATE,
                                 update_period_days), r.URGENCY - 1, r.ID))
    if len(updates) > 0:
        dbo.execute_many("UPDATE animalwaitinglist SET " \
            "UrgencyLastUpdatedDate=?, " \
            "UrgencyUpdateDate=?, " \
            "Urgency=? " \
            "WHERE ID=? ", updates)
Exemple #4
0
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
Exemple #5
0
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)
Exemple #6
0
def get_waitinglist(dbo, priorityfloor = 5, species = -1, size = -1, addresscontains = "", includeremoved = 0, namecontains = "", descriptioncontains = "", siteid = 0):
    """
    Retrieves the waiting list
    priorityfloor: The lowest urgency to show (1 = urgent, 5 = lowest)
    species: A species filter or -1 for all
    size: A size filter or -1 for all
    addresscontains: A partial address
    includeremoved: Whether or not to include removed entries
    namecontains: A partial name
    descriptioncontains: A partial description
    """
    l = dbo.locale

    ands = []
    values = []
    def add(a, v = None):
        ands.append(a)
        if v: values.append(v)
    
    add("a.Urgency <= ?", priorityfloor)
    if includeremoved == 0: add("a.DateRemovedFromList Is Null")
    if species != -1: add("a.SpeciesID = ?", species)
    if size != -1: add("a.Size = ?", size)
    if addresscontains != "":
        ands.append("(%s OR %s)" % (dbo.sql_ilike("OwnerAddress"), dbo.sql_ilike("OwnerTown")))
        v = "%%%s%%" % addresscontains.lower()
        values.append(v)
        values.append(v)
    if namecontains != "": add(dbo.sql_ilike("OwnerName"), "%%%s%%" % namecontains.lower())
    if descriptioncontains != "": add(dbo.sql_ilike("AnimalDescription"), "%%%s%%" % descriptioncontains.lower())
    if siteid != 0: add("(o.SiteID = 0 OR o.SiteID = ?)", siteid)

    sql = "%s WHERE %s ORDER BY a.Urgency, a.DatePutOnList" % (get_waitinglist_query(dbo), " AND ".join(ands))
    rows = dbo.query(sql, values)

    wlh = asm3.configuration.waiting_list_highlights(dbo).split(" ")
    ranks = get_waitinglist_ranks(dbo)
    for r in rows:
        r.HIGHLIGHT = ""
        for hi in wlh:
            if hi != "":
                if hi.find("|") == -1:
                    wid = hi
                    h = "1"
                else:
                    wid, h = hi.split("|")
                if wid == str(r.WLID).strip():
                    r.HIGHLIGHT = h
                    break
        if r.WLID in ranks:
            r.RANK = ranks[r.WLID]
        else:
            r.RANK = ""
        r.TIMEONLIST = date_diff(l, r.DATEPUTONLIST, now(dbo.timezone), asm3.configuration.date_diff_cutoffs(dbo) )
    return rows
Exemple #7
0
def reports_email(dbo):
    """
    Batch email reports
    """
    try:
        # Email any daily reports for local time of now
        extreports.email_daily_reports(dbo, i18n.now(dbo.timezone))
    except:
        em = str(sys.exc_info()[0])
        al.error("FAIL: running daily email of reports_email: %s" % em,
                 "cron.reports_email", dbo, sys.exc_info())
Exemple #8
0
def get_waitinglist_by_id(dbo, wid):
    """
    Returns a single waitinglist record for the ID given
    """
    l = dbo.locale
    r = dbo.first_row( dbo.query( get_waitinglist_query(dbo) + " WHERE a.ID = ?", [wid]) )
    if not r: return None
    ranks = get_waitinglist_ranks(dbo)
    if r.WLID in ranks:
        r.RANK = ranks[r.WLID]
    else:
        r.RANK = ""
    r.TIMEONLIST = date_diff(l, r.DATEPUTONLIST, now(dbo.timezone))
    return r
Exemple #9
0
def create_waitinglist_from_found(dbo, username, aid):
    """
    Creates a waiting list entry 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 = {
        "dateputon":            python2display(l, now(dbo.timezone)),
        "description":          str(a["DISTFEAT"]),
        "species":              str(a["ANIMALTYPEID"]),
        "comments":             str(a["COMMENTS"]),
        "owner":                str(a["OWNERID"]),
        "breed1":               a["BREEDID"],
        "breed2":               a["BREEDID"],
        "basecolour":           str(a["BASECOLOURID"]),
        "urgency":              str(asm3.configuration.waiting_list_default_urgency(dbo))
    }
    nextid = asm3.waitinglist.insert_waitinglist_from_form(dbo, asm3.utils.PostedData(data, dbo.locale), username)
    return nextid
Exemple #10
0
def stock_take_from_mobile_form(dbo, username, post):
    """
    Post should contain sl{ID} values for new balances.
    """
    if post.integer("usagetype") == 0:
        raise asm3.utils.ASMValidationError("No usage type passed")

    for k in post.data.keys():
        if k.startswith("sl"):
            slid = asm3.utils.cint(k.replace("sl", ""))
            sl = get_stocklevel(dbo, slid)
            slb = asm3.utils.cfloat(sl.BALANCE)  # balance
            sln = post.floating(k)  # new balance
            # If the balance hasn't changed, do nothing
            if slb == sln: continue
            # Update the level
            dbo.update("stocklevel", slid, {"Balance": sln})
            # Write a stock usage record for the difference
            insert_stockusage(dbo, username, slid, sln - slb,
                              now(dbo.timezone), post.integer("usagetype"), "")
Exemple #11
0
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