def plcAgeYears(self, agegroup="", dob=None): """ Returns an age in years as a float/string from either an agegroup or a date of birth. """ years = 1 if dob is not None: days = i18n.date_diff_days(dob, i18n.now(self.dbo.timezone)) years = days / 365.0 else: years = configuration.age_group_for_name(self.dbo, agegroup) if years == 0: years = 1 return "%0.1f" % years
def getAge(self, dob, speciesid): """ Returns an age banding based on date of birth and species """ # Kitten (0-8 weeks) = 1, Kitten/Juvenile (9 weeks- 5 months) = 2, Adult Cat (6 months - 8 years) =3, # Senior Cat (9 years) = 4, Puppy (0-8 weeks) = 5, Puppy/Juvenile (9 weeks 11-months) =6, Adult Dog (1 # year - 7 years) =7, Senior Dog (8 years) = 8 ageinyears = i18n.date_diff_days(dob, i18n.now()) ageinyears /= 365.0 age = 0 # Cats if speciesid == 2: if ageinyears < 0.15: age = 1 elif ageinyears < 0.5: age = 2 elif ageinyears < 8: age = 3 else: age = 4 # Dogs elif speciesid == 1: if ageinyears < 0.15: age = 5 elif ageinyears < 0.9: age = 6 elif ageinyears < 8: age = 7 else: age = 8 return age
def executeAgeSpecies(self, user, childadult=True, species=True): """ Publisher that puts animals on pages by age and species childadult: True if we should split up pages by animals under/over 6 months species: True if we should split up pages by species """ self.log("HTMLPublisher (age/species pages) starting...") l = self.dbo.locale normHeader = self.getHeader() normFooter = self.getFooter() body = self.getBody() header = self.substituteHFTag(normHeader, 0, user, i18n._("Available for adoption", l)) footer = self.substituteHFTag(normFooter, 0, user, i18n._("Available for adoption", l)) # Calculate the number of days old an animal has to be to # count as an adult childAdultSplitDays = self.pc.childAdultSplit * 7 # Open FTP socket, bail if it fails if not self.openFTPSocket(): self.setLastError("Failed opening FTP socket.") return try: animals = self.getMatchingAnimals() self.totalAnimals = len(animals) anCount = 0 pages = {} # Create default pages for every possible permutation defaultpages = [] if childadult and species: spec = lookups.get_species(self.dbo) for sp in spec: defaultpages.append("adult%s" % sp["SPECIESNAME"]) defaultpages.append("baby%s" % sp["SPECIESNAME"]) elif childadult: defaultpages = ["adult", "baby"] elif species: spec = lookups.get_species(self.dbo) for sp in spec: defaultpages.append(sp["SPECIESNAME"]) for dp in defaultpages: pages["%s.%s" % (dp, self.pc.extension)] = header # Create an all page allpage = "all.%s" % self.pc.extension pages[allpage] = header except Exception as err: self.logError("Error setting up page: %s" % err, sys.exc_info()) self.setLastError("Error setting up page: %s" % err) return for an in animals: try: anCount += 1 self.log("Processing: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, self.totalAnimals)) self.updatePublisherProgress( self.getProgress(anCount, len(animals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() self.cleanup() return # upload all images for this animal to our current FTP self.uploadImages(an, True) # Calculate the new page name pagename = ".%s" % self.pc.extension if species: pagename = "%s%s" % (an["SPECIESNAME"], pagename) if childadult: days = i18n.date_diff_days(an["DATEOFBIRTH"], i18n.now(self.dbo.timezone)) if days < childAdultSplitDays: pagename = "baby%s" % pagename else: pagename = "adult%s" % pagename # Does this page exist? if pagename not in pages: # No, create it and add the header page = header else: page = pages[pagename] # Add this item to the page page += self.substituteBodyTags(body, an) pages[pagename] = page self.log("%s -> %s" % (an["SHELTERCODE"], pagename)) # Add this item to our magic "all" page page = pages[allpage] page += self.substituteBodyTags(body, an) pages[allpage] = page # Mark success in the log self.log("Processed: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) except Exception as err: self.logError( "Failed processing animal: %s, %s" % (str(an["SHELTERCODE"]), err), sys.exc_info()) # Mark published self.markAnimalsPublished(animals) # Upload the pages for k, v in pages.iteritems(): self.log("Saving page to disk: %s (%d bytes)" % (k, len(v + footer))) self.saveFile( os.path.join(self.publishDir, self.escapePageName(k)), v + footer) self.log("Saved page to disk: %s" % k) if self.pc.uploadDirectly: self.log("Uploading page: %s" % k) self.upload(self.escapePageName(k)) self.log("Uploaded page: %s" % k)
def match(dbo, lostanimalid = 0, foundanimalid = 0, animalid = 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 returns a list LostFoundMatch objects """ l = dbo.locale matches = [] matchspecies = configuration.match_species(dbo) matchbreed = configuration.match_breed(dbo) matchage = configuration.match_age(dbo) matchsex = configuration.match_sex(dbo) matcharealost = configuration.match_area_lost(dbo) matchfeatures = configuration.match_features(dbo) matchpostcode = configuration.match_postcode(dbo) matchcolour = configuration.match_colour(dbo) matchdatewithin2weeks = configuration.match_within2weeks(dbo) matchmax = matchspecies + matchbreed + matchage + matchsex + \ matcharealost + matchfeatures + matchpostcode + matchcolour + \ matchdatewithin2weeks matchpointfloor = configuration.match_point_floor(dbo) includeshelter = configuration.match_include_shelter(dbo) # Ignore records older than 6 months to keep things useful giveup = subtract_days(now(dbo.timezone), 180) # Get our set of lost animals lostanimals = None if lostanimalid == 0: lostanimals = db.query(dbo, get_lostanimal_query() + \ " WHERE a.DateFound Is Null AND a.DateLost > %s ORDER BY a.DateLost" % db.dd(giveup)) else: lostanimals = db.query(dbo, get_lostanimal_query() + \ " WHERE a.ID = %d" % 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 = db.query(dbo, get_foundanimal_query() + \ " WHERE a.ReturnToOwnerDate Is Null" \ " AND a.DateFound >= %s " % db.dd(oldestdate)) else: foundanimals = db.query(dbo, get_foundanimal_query() + " WHERE a.ID = %d" % foundanimalid) # Get the set of shelter animals for comparison shelteranimals = None if includeshelter: if animalid == 0: shelteranimals = db.query(dbo, animal.get_animal_query() + " WHERE " + \ "a.DateBroughtIn > %s" % db.dd(oldestdate)) else: shelteranimals = db.query(dbo, animal.get_animal_query() + " WHERE a.ID = %d" % animalid) for la in lostanimals: # 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["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() m.lid = la["ID"] m.lcontactname = la["OWNERNAME"] m.lcontactnumber = la["HOMETELEPHONE"] m.larealost = la["AREALOST"] m.lareapostcode = la["AREAPOSTCODE"] m.lagegroup = la["AGEGROUP"] m.lsexname = la["SEXNAME"] m.lspeciesname = la["SPECIESNAME"] m.lbreedname = la["BREEDNAME"] m.ldistinguishingfeatures = la["DISTFEAT"] m.lbasecolourname = la["BASECOLOURNAME"] m.ldatelost = la["DATELOST"] m.fid = str(fa["ID"]) m.fcontactname = fa["OWNERNAME"] m.fcontactnumber = fa["HOMETELEPHONE"] m.fareafound = fa["AREAFOUND"] m.fareapostcode = fa["AREAPOSTCODE"] m.fagegroup = fa["AGEGROUP"] m.fsexname = fa["SEXNAME"] m.fspeciesname = fa["SPECIESNAME"] m.fbreedname = fa["BREEDNAME"] m.fdistinguishingfeatures = fa["DISTFEAT"] m.fbasecolourname = fa["BASECOLOURNAME"] m.fdatefound = fa["DATEFOUND"] m.matchpoints = int((float(matchpoints) / float(matchmax)) * 100.0) matches.append(m) # Shelter animals if includeshelter: for a in shelteranimals: matchpoints = 0 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["AREALOST"], a["ORIGINALOWNERADDRESS"], matcharealost) matchpoints += words(la["DISTFEAT"], a["MARKINGS"], matchfeatures) if str(a["ORIGINALOWNERPOSTCODE"]).find(la["AREAPOSTCODE"]): matchpoints += matchpostcode if date_diff_days(la["DATELOST"], a["DATEBROUGHTIN"]) <= 14: matchpoints += matchdatewithin2weeks if matchpoints > matchmax: matchpoints = matchmax if matchpoints >= matchpointfloor: m = LostFoundMatch() m.lid = la["ID"] m.lcontactname = la["OWNERNAME"] m.lcontactnumber = la["HOMETELEPHONE"] m.larealost = la["AREALOST"] m.lareapostcode = la["AREAPOSTCODE"] m.lagegroup = la["AGEGROUP"] m.lsexname = la["SEXNAME"] m.lspeciesname = la["SPECIESNAME"] m.lbreedname = la["BREEDNAME"] m.ldistinguishingfeatures = la["DISTFEAT"] m.lbasecolourname = la["BASECOLOURNAME"] m.ldatelost = la["DATELOST"] m.fid = a["CODE"] m.fcontactname = _("Shelter animal {0} '{1}'", l).format(a["CODE"], a["ANIMALNAME"]) m.fcontactnumber = a["SPECIESNAME"] m.fareafound = _("On Shelter", l) m.fareapostcode = a["ORIGINALOWNERPOSTCODE"] m.fagegroup = a["AGEGROUP"] m.fsexname = a["SEXNAME"] m.fspeciesname = a["SPECIESNAME"] m.fbreedname = a["BREEDNAME"] m.fdistinguishingfeatures = a["MARKINGS"] m.fbasecolourname = a["BASECOLOURNAME"] m.fdatefound = a["DATEBROUGHTIN"] m.matchpoints = int((float(matchpoints) / float(matchmax)) * 100.0) matches.append(m) return matches
def run(self): self.log("PetFinderPublisher starting...") if self.isPublisherExecuting(): return self.updatePublisherProgress(0) self.setLastError("") self.setStartPublishing() if not self.checkMappedSpecies(): self.setLastError("Not all species have been mapped.") self.cleanup() return if not self.checkMappedBreeds(): self.setLastError("Not all breeds have been mapped.") self.cleanup() return shelterid = configuration.petfinder_user(self.dbo) if shelterid == "": self.setLastError("No PetFinder.com shelter id has been set.") self.cleanup() return animals = self.getMatchingAnimals() if len(animals) == 0: self.setLastError("No animals found to publish.") self.cleanup() return # Build a list of age bands for petfinder ages. It's # a list of integers in days for each band. # The defaults are 6 months, 2 years and 9 years. agebands = configuration.petfinder_age_bands(self.dbo) if agebands == "" or len(agebands.split(",")) != 3: agebands = "182,730,3285" agebands = [int(i) for i in agebands.split(",")] if not self.openFTPSocket(): self.setLastError("Failed opening FTP socket.") if self.logSearch("530 Login") != -1: self.log( "Found 530 Login incorrect: disabling PetFinder publisher." ) configuration.publishers_enabled_disable(self.dbo, "pf") self.cleanup() return # Do the images first self.mkdir("import") self.chdir("import") self.mkdir("photos") self.chdir("photos", "import/photos") csv = [] anCount = 0 for an in animals: try: line = [] anCount += 1 self.log("Processing: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) self.updatePublisherProgress( self.getProgress(anCount, len(animals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() self.cleanup() return # Upload images for this animal self.uploadImages(an, False, 3) # Mapped species line.append("\"%s\"" % an["PETFINDERSPECIES"]) # Breed 1 line.append("\"%s\"" % an["PETFINDERBREED"]) # Age, one of Adult, Baby, Senior and Young ageindays = i18n.date_diff_days(an["DATEOFBIRTH"], i18n.now(self.dbo.timezone)) agename = "Adult" if ageindays < agebands[0]: agename = "Baby" elif ageindays < agebands[1]: agename = "Young" elif ageindays < agebands[2]: agename = "Adult" else: agename = "Senior" line.append("\"%s\"" % agename) # Name line.append("\"%s\"" % an["ANIMALNAME"].replace("\"", "\"\"")) # Size, one of S, M, L, XL ansize = "M" if an["SIZE"] == 0: ansize = "XL" elif an["SIZE"] == 1: ansize = "L" elif an["SIZE"] == 2: ansize = "M" elif an["SIZE"] == 3: ansize = "S" line.append("\"%s\"" % ansize) # Sex, one of M or F sexname = "M" if an["SEX"] == 0: sexname = "F" line.append("\"%s\"" % sexname) # Description line.append("\"%s\"" % self.getDescription(an, False, True)) # Special needs if an["CRUELTYCASE"] == 1: line.append("\"1\"") elif an["HASSPECIALNEEDS"] == 1: line.append("\"1\"") else: line.append("\"\"") # Has shots line.append( self.pfYesNo( medical.get_vaccinated(self.dbo, int(an["ID"])))) # Altered line.append(self.pfYesNo(an["NEUTERED"] == 1)) # No Dogs line.append(self.pfYesNo(an["ISGOODWITHDOGS"] == 1)) # No Cats line.append(self.pfYesNo(an["ISGOODWITHCATS"] == 1)) # No Kids line.append(self.pfYesNo(an["ISGOODWITHCHILDREN"] == 1)) # No Claws line.append(self.pfYesNo(an["DECLAWED"] == 1)) # Housebroken line.append(self.pfYesNo(an["ISHOUSETRAINED"] == 0)) # ID line.append("\"%s\"" % an["SHELTERCODE"]) # Breed 2 line.append("\"%s\"" % self.getPublisherBreed(an, 2)) # Mix line.append(self.pfYesNo(an["CROSSBREED"] == 1)) # Add to our CSV file csv.append(",".join(line)) # Mark success in the log self.logSuccess("Processed: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) except Exception as err: self.logError( "Failed processing animal: %s, %s" % (str(an["SHELTERCODE"]), err), sys.exc_info()) # Mark published self.markAnimalsPublished(animals, first=True) # Upload the datafiles mapfile = "; PetFinder import map. This file was autogenerated by\n" \ "; Animal Shelter Manager. http://sheltermanager.com\n" \ "; The FREE, open source solution for animal sanctuaries and rescue shelters.\n\n" \ "#SHELTERID:%s\n" \ "#0:Animal=Animal\n" \ "#1:Breed=Breed\n" \ "#2:Age=Age\n" \ "#3:Name=Name\n" \ "#4:Size=Size\n" \ "#5:Sex=Sex\n" \ "Female=F\n" \ "Male=M\n" \ "#6:Description=Dsc\n" \ "#7:SpecialNeeds=SpecialNeeds\n" \ "#8:HasShots=HasShots\n" \ "#9:Altered=Altered\n" \ "#10:NoDogs=NoDogs\n" \ "#11:NoCats=NoCats\n" \ "#12:NoKids=NoKids\n" \ "#13:Declawed=Declawed\n" \ "#14:HouseBroken=HouseBroken\n" \ "#15:Id=Id\n" \ "#16:Breed2=Breed2\n" \ "#ALLOWUPDATE:Y\n" \ "#HEADER:N" % shelterid self.saveFile(os.path.join(self.publishDir, shelterid + "import.cfg"), mapfile) self.saveFile(os.path.join(self.publishDir, shelterid), "\n".join(csv)) self.log("Uploading datafile and map, %s %s" % (shelterid, shelterid + "import.cfg")) self.chdir("..", "import") self.upload(shelterid) self.upload(shelterid + "import.cfg") self.log("Uploaded %s %s" % (shelterid, shelterid + "import.cfg")) self.log("-- FILE DATA -- (csv)") self.log("\n".join(csv)) self.log("-- FILE DATA -- (map)") self.log(mapfile) self.cleanup()
def run(self): self.log("PetRescuePublisher starting...") if self.isPublisherExecuting(): return self.updatePublisherProgress(0) self.setLastError("") self.setStartPublishing() token = configuration.petrescue_token(self.dbo) all_desexed = configuration.petrescue_all_desexed(self.dbo) interstate = configuration.petrescue_interstate(self.dbo) postcode = configuration.organisation_postcode(self.dbo) suburb = configuration.organisation_town(self.dbo) state = configuration.organisation_county(self.dbo) contact_name = configuration.organisation(self.dbo) contact_email = configuration.petrescue_email(self.dbo) if contact_email == "": contact_email = configuration.email(self.dbo) contact_number = configuration.organisation_telephone(self.dbo) if token == "": self.setLastError("No PetRescue auth token has been set.") return if postcode == "" or contact_email == "": self.setLastError("You need to set your organisation postcode and contact email under Settings->Options->Shelter Details->Email") return animals = self.getMatchingAnimals(includeAdditionalFields=True) processed = [] if len(animals) == 0: self.setLastError("No animals found to publish.") self.cleanup() return headers = { "Authorization": "Token token=%s" % token, "Accept": "*/*" } anCount = 0 for an in animals: try: anCount += 1 self.log("Processing: %s: %s (%d of %d)" % ( an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) self.updatePublisherProgress(self.getProgress(anCount, len(animals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() self.cleanup() return isdog = an.SPECIESID == 1 iscat = an.SPECIESID == 2 ageinyears = i18n.date_diff_days(an.DATEOFBIRTH, i18n.now()) size = "" if an.SIZE == 2: size = "medium" elif an.SIZE < 2: size = "large" else: size = "small" coat = "" if an.COATTYPE == 0: coat = "short" elif an.COATTYPE == 1: coat = "long" else: coat = "medium_coat" origin = "" if an.ISTRANSFER == 1 and str(an.BROUGHTINBYOWNERNAME).lower().find("pound") == -1: origin = "shelter_transfer" elif an.ISTRANSFER == 1 and str(an.BROUGHTINBYOWNERNAME).lower().find("pound") != -1: origin = "pound_transfer" elif an.ORIGINALOWNERID > 0: origin = "owner_surrender" else: origin = "community_cat" best_feature = "Looking for love" if "BESTFEATURE" in an and an.BESTFEATURE != "": best_feature = an.BESTFEATURE breeder_id = "" if "BREEDERID" in an and an.BREEDERID != "": breeder_id = an.BREEDERID needs_constant_care = False if "NEEDSCONSTANTCARE" in an and an.NEEDSCONSTANTCARE != "" and an.NEEDSCONSTANTCARE != "0": needs_constant_care = True # Check whether we've been vaccinated, wormed and hw treated vaccinated = medical.get_vaccinated(self.dbo, an.ID) sixmonths = self.dbo.today(offset=-182) hwtreated = isdog and self.dbo.query_int("SELECT COUNT(*) FROM animalmedical WHERE LOWER(TreatmentName) LIKE ? " \ "AND LOWER(TreatmentName) LIKE ? AND StartDate>? AND AnimalID=?", ("%heart%", "%worm%", sixmonths, an.ID)) > 0 wormed = (isdog or iscat) and self.dbo.query_int("SELECT COUNT(*) FROM animalmedical WHERE LOWER(TreatmentName) LIKE ? " \ "AND LOWER(TreatmentName) NOT LIKE ? AND StartDate>? AND AnimalID=?", ("%worm%", "%heart%", sixmonths, an.ID)) > 0 # PR want a null value to hide never-treated animals, so we # turn False into a null. if not hwtreated: hwtreated = None if not wormed: wormed = None # Use the fosterer's postcode, state and suburb if available location_postcode = postcode location_state_abbr = state location_suburb = suburb if an.ACTIVEMOVEMENTID and an.ACTIVEMOVEMENTTYPE == 2: fr = self.dbo.first_row(self.dbo.query("SELECT OwnerTown, OwnerCounty, OwnerPostcode FROM adoption m " \ "INNER JOIN owner o ON m.OwnerID = o.ID WHERE m.ID=?", [ an.ACTIVEMOVEMENTID ])) if fr is not None and fr.OWNERPOSTCODE: location_postcode = fr.OWNERPOSTCODE if fr is not None and fr.OWNERCOUNTY: location_state_abbr = fr.OWNERCOUNTY if fr is not None and fr.OWNERTOWN: location_suburb = fr.OWNERTOWN # Build a list of immutable photo URLs photo_urls = [] photos = self.dbo.query("SELECT MediaName FROM media " \ "WHERE LinkTypeID = 0 AND LinkID = ? AND MediaMimeType = 'image/jpeg' " \ "AND (ExcludeFromPublish = 0 OR ExcludeFromPublish Is Null) " \ "ORDER BY WebsitePhoto DESC, ID", [an.ID]) for m in photos: photo_urls.append("%s?account=%s&method=dbfs_image&title=%s" % (SERVICE_URL, self.dbo.database, m.MEDIANAME)) # Only send microchip_number for locations with a Victoria postcode 3xxx microchip_number = "" if location_postcode.startswith("3"): microchip_number = utils.iif(an.IDENTICHIPPED == 1, an.IDENTICHIPNUMBER, "") # Construct a dictionary of info for this animal data = { "remote_id": str(an.ID), # animal identifier in ASM "remote_source": "SM%s" % self.dbo.database, # system/database identifier "name": an.ANIMALNAME.title(), # animal name (title case, they validate against caps) "shelter_code": an.SHELTERCODE, "adoption_fee": i18n.format_currency_no_symbol(self.locale, an.FEE), "species_name": an.SPECIESNAME, "breed_names": self.get_breed_names(an), # [breed1,breed2] or [breed1] "breeder_id": breeder_id, # mandatory for QLD dogs born after 2017-05-26 "mix": an.CROSSBREED == 1, # true | false "date_of_birth": i18n.format_date("%Y-%m-%d", an.DATEOFBIRTH), # iso "gender": an.SEXNAME.lower(), # male | female "personality": self.replace_html_entities(self.getDescription(an)), # 20-4000 chars of free type "best_feature": best_feature, # 25 chars free type, defaults to "Looking for love" requires BESTFEATURE additional field "location_postcode": location_postcode, # shelter/fosterer postcode "location_state_abbr": location_state_abbr, # shelter/fosterer state "location_suburb": location_suburb, # shelter/fosterer suburb "microchip_number": microchip_number, "desexed": an.NEUTERED == 1 or all_desexed, # true | false, validates to always true according to docs "contact_method": "email", # email | phone "size": utils.iif(isdog, size, ""), # dogs only - small | medium | high "senior": isdog and ageinyears > (7 * 365), # dogs only, true | false "vaccinated": vaccinated, # cats, dogs, rabbits, true | false "wormed": wormed, # cats & dogs, true | false "heart_worm_treated": hwtreated, # dogs only, true | false "coat": coat, # Only applies to cats and guinea pigs, but we send for everything: short | medium_coat | long "intake_origin": utils.iif(iscat, origin, ""), # cats only, community_cat | owner_surrender | pound_transfer | shelter_transfer "incompatible_with_cats": an.ISGOODWITHCATS == 1, "incompatible_with_dogs": an.ISGOODWITHDOGS == 1, "incompatible_with_kids_under_5": an.ISGOODWITHCHILDREN == 1, "incompatible_with_kids_6_to_12": an.ISGOODWITHCHILDREN == 1, "needs_constant_care": needs_constant_care, "adoption_process": "", # 4,000 chars how to adopt "contact_details_source": "self", # self | user | group "contact_preferred_method": "email", # email | phone "contact_name": contact_name, # name of contact details owner "contact_number": contact_number, # number to enquire about adoption "contact_email": contact_email, # email to enquire about adoption "foster_needed": False, # true | false "interstate": interstate, # true | false - can the animal be flown to another state for adoption "medical_notes": "", # DISABLED an.HEALTHPROBLEMS, # 4,000 characters medical notes "multiple_animals": an.BONDEDANIMALID > 0 or an.BONDEDANIMAL2ID > 0, # More than one animal included in listing true | false "photo_urls": photo_urls, # List of photo URL strings "status": "active" # active | removed | on_hold | rehomed | suspended | group_suspended } # PetRescue will insert/update accordingly based on whether remote_id/remote_source exists url = PETRESCUE_URL + "listings" jsondata = utils.json(data) self.log("Sending POST to %s to create/update listing: %s" % (url, jsondata)) r = utils.post_json(url, jsondata, headers=headers) if r["status"] != 200: self.logError("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], self.utf8_to_ascii(r["response"]))) else: self.log("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], self.utf8_to_ascii(r["response"]))) self.logSuccess("Processed: %s: %s (%d of %d)" % ( an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) processed.append(an) except Exception as err: self.logError("Failed processing animal: %s, %s" % (str(an["SHELTERCODE"]), err), sys.exc_info()) try: # Get a list of all animals that we sent to PR recently (14 days) prevsent = self.dbo.query("SELECT AnimalID FROM animalpublished WHERE SentDate>=? AND PublishedTo='petrescue'", [self.dbo.today(offset=-14)]) # Build a list of IDs we just sent, along with a list of ids for animals # that we previously sent and are not in the current sent list. # This identifies the listings we need to cancel animalids_just_sent = set([ x.ID for x in animals ]) animalids_to_cancel = set([ str(x.ANIMALID) for x in prevsent if x.ANIMALID not in animalids_just_sent]) # Get the animal records for the ones we need to cancel if len(animalids_to_cancel) == 0: animals = [] else: animals = self.dbo.query("SELECT ID, ShelterCode, AnimalName, ActiveMovementDate, ActiveMovementType, DeceasedDate " \ "FROM animal a WHERE ID IN (%s)" % ",".join(animalids_to_cancel)) except Exception as err: self.logError("Failed finding listings to cancel: %s" % err, sys.exc_info()) # Cancel the inactive listings for an in animals: try: status = "on_hold" if an.ACTIVEMOVEMENTDATE is not None and an.ACTIVEMOVEMENTTYPE == 1: status = "rehomed" if an.DECEASEDDATE is not None: status = "removed" data = { "status": status } jsondata = utils.json(data) url = PETRESCUE_URL + "listings/%s/SM%s" % (an.ID, self.dbo.database) self.log("Sending PATCH to %s to update existing listing: %s" % (url, jsondata)) r = utils.patch_json(url, jsondata, headers=headers) if r["status"] == 200: self.log("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], self.utf8_to_ascii(r["response"]))) self.logSuccess("%s - %s: Marked with new status %s" % (an.SHELTERCODE, an.ANIMALNAME, status)) # It used to be that we updated animalpublished for this animal to get sentdate to today # we don't do this now so that we'll update dead listings every day for however many days we # look back, but that's it else: self.logError("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], self.utf8_to_ascii(r["response"]))) except Exception as err: self.logError("Failed closing listing for %s - %s: %s" % (an.SHELTERCODE, an.ANIMALNAME, err), sys.exc_info()) # Mark sent animals published self.markAnimalsPublished(processed, first=True) self.cleanup()
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 = configuration.match_species(dbo) matchbreed = configuration.match_breed(dbo) matchage = configuration.match_age(dbo) matchsex = configuration.match_sex(dbo) matcharealost = configuration.match_area_lost(dbo) matchfeatures = configuration.match_features(dbo) matchpostcode = configuration.match_postcode(dbo) matchcolour = configuration.match_colour(dbo) matchdatewithin2weeks = configuration.match_within2weeks(dbo) matchmax = matchspecies + matchbreed + matchage + matchsex + \ matcharealost + matchfeatures + matchpostcode + matchcolour + \ matchdatewithin2weeks matchpointfloor = configuration.match_point_floor(dbo) includeshelter = 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(animal.get_animal_query(dbo) + " WHERE " + \ "(a.Archived = 0 OR a.ActiveMovementType IN (3,4,7)) " \ "AND a.DateBroughtIn > ?", [oldestdate]) else: shelteranimals = dbo.query(animal.get_animal_query(dbo) + " WHERE a.ID = ?", [animalid]) asynctask.set_progress_max(dbo, len(lostanimals)) for la in lostanimals: 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["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.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.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 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["AREALOST"], a["ORIGINALOWNERADDRESS"], matcharealost) matchpoints += words(la["DISTFEAT"], a["MARKINGS"], matchfeatures) if utils.nulltostr(a["ORIGINALOWNERPOSTCODE"]).find(la["AREAPOSTCODE"]) != -1: matchpoints += matchpostcode 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.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.fcontactnumber = a["SPECIESNAME"] m.fareafound = _("On Shelter", l) m.fareapostcode = a["ORIGINALOWNERPOSTCODE"] 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, " \ "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 run(self): self.log("AdoptAPetPublisher starting...") if self.isPublisherExecuting(): return self.updatePublisherProgress(0) self.setLastError("") self.setStartPublishing() if not self.checkMappedSpecies(): self.setLastError("Not all species have been mapped.") self.cleanup() return if not self.checkMappedBreeds(): self.setLastError("Not all breeds have been mapped.") self.cleanup() return if self.pc.includeColours and not self.checkMappedColours(): self.setLastError( "Not all colours have been mapped and sending colours is enabled" ) self.cleanup() return shelterid = configuration.adoptapet_user(self.dbo) if shelterid == "": self.setLastError("No AdoptAPet.com shelter id has been set.") self.cleanup() return animals = self.getMatchingAnimals() if len(animals) == 0: self.setLastError("No animals found to publish.") self.cleanup() return if not self.openFTPSocket(): self.setLastError("Failed opening FTP socket.") if self.logSearch("530 Login") != -1: self.log( "Found 530 Login incorrect: disabling AdoptAPet publisher." ) configuration.publishers_enabled_disable(self.dbo, "ap") self.cleanup() return # Do the images first self.mkdir("photos") self.chdir("photos") csv = [] anCount = 0 for an in animals: try: line = [] anCount += 1 self.log("Processing: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) self.updatePublisherProgress( self.getProgress(anCount, len(animals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() self.cleanup() return # Upload images for this animal self.uploadImages(an) # Id line.append("\"%s\"" % an["SHELTERCODE"]) # Species line.append("\"%s\"" % an["PETFINDERSPECIES"]) # Breed 1 line.append("\"%s\"" % an["PETFINDERBREED"]) # Breed 2 line.append("\"%s\"" % self.getPublisherBreed(an, 2)) # Purebred line.append(self.apYesNo(an["CROSSBREED"] == 0)) # Age, one of Adult, Baby, Senior and Young ageinyears = i18n.date_diff_days(an["DATEOFBIRTH"], i18n.now(self.dbo.timezone)) ageinyears /= 365.0 agename = "Adult" if ageinyears < 0.5: agename = "Baby" elif ageinyears < 2: agename = "Young" elif ageinyears < 9: agename = "Adult" else: agename = "Senior" line.append("\"%s\"" % agename) # Name line.append("\"%s\"" % an["ANIMALNAME"].replace("\"", "\"\"")) # Size, one of S, M, L, XL ansize = "M" if an["SIZE"] == 0: ansize = "XL" elif an["SIZE"] == 1: ansize = "L" elif an["SIZE"] == 2: ansize = "M" elif an["SIZE"] == 3: ansize = "S" # If the animal is not a dog or cat, leave size blank as # adoptapet will throw errors otherwise if an["PETFINDERSPECIES"] != "Dog" and an[ "PETFINDERSPECIES"] != "Cat": ansize = "" line.append("\"%s\"" % ansize) # Sex, one of M or F sexname = "M" if an["SEX"] == 0: sexname = "F" line.append("\"%s\"" % sexname) # Colour if self.pc.includeColours: line.append("\"%s\"" % an["ADOPTAPETCOLOUR"]) # Description line.append("\"%s\"" % self.getDescription(an, crToBr=True)) # Status, one of Available, Adopted or Delete line.append("\"Available\"") # Good with Kids line.append(self.apYesNoUnknown(an["ISGOODWITHCHILDREN"])) # Good with Cats line.append(self.apYesNoUnknown(an["ISGOODWITHCATS"])) # Good with Dogs line.append(self.apYesNoUnknown(an["ISGOODWITHDOGS"])) # Spayed/Neutered line.append(self.apYesNo(an["NEUTERED"] == 1)) # Shots current line.append( self.apYesNo( medical.get_vaccinated(self.dbo, int(an["ID"])))) # Housetrained line.append(self.apYesNoUnknown(an["ISHOUSETRAINED"])) # Declawed line.append(self.apYesNo(an["DECLAWED"] == 1)) # Special needs line.append( self.apYesNo(an["CRUELTYCASE"] == 1 or an["HASSPECIALNEEDS"] == 1)) # Hair Length line.append(self.apHairLength(an)) # YouTube Video URL line.append(self.apYouTubeURL(an["WEBSITEVIDEOURL"])) # Add to our CSV file csv.append(",".join(line)) # Mark success in the log self.logSuccess("Processed: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) except Exception as err: self.logError( "Failed processing animal: %s, %s" % (str(an["SHELTERCODE"]), err), sys.exc_info()) # Mark published self.markAnimalsPublished(animals, first=True) # Upload the datafiles mapfile = self.apMapFile(self.pc.includeColours) self.saveFile(os.path.join(self.publishDir, "import.cfg"), mapfile) self.saveFile(os.path.join(self.publishDir, "pets.csv"), "\n".join(csv)) self.log("Saving datafile and map, %s %s" % ("pets.csv", "import.cfg")) self.chdir("..", "") self.log("Uploading pets.csv") self.upload("pets.csv") if not self.pc.noImportFile: self.log("Uploading import.cfg") self.upload("import.cfg") else: self.log("import.cfg upload is DISABLED") self.log("-- FILE DATA --(csv)") self.log("\n".join(csv)) self.log("-- FILE DATA --(map)") self.log(mapfile) self.cleanup()
def match(dbo, lostanimalid=0, foundanimalid=0, animalid=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 returns a list LostFoundMatch objects """ l = dbo.locale matches = [] matchspecies = configuration.match_species(dbo) matchbreed = configuration.match_breed(dbo) matchage = configuration.match_age(dbo) matchsex = configuration.match_sex(dbo) matcharealost = configuration.match_area_lost(dbo) matchfeatures = configuration.match_features(dbo) matchpostcode = configuration.match_postcode(dbo) matchcolour = configuration.match_colour(dbo) matchdatewithin2weeks = configuration.match_within2weeks(dbo) matchmax = matchspecies + matchbreed + matchage + matchsex + \ matcharealost + matchfeatures + matchpostcode + matchcolour + \ matchdatewithin2weeks matchpointfloor = configuration.match_point_floor(dbo) includeshelter = configuration.match_include_shelter(dbo) # Ignore records older than 6 months to keep things useful giveup = subtract_days(now(dbo.timezone), 180) # Get our set of lost animals lostanimals = None if lostanimalid == 0: lostanimals = db.query(dbo, get_lostanimal_query(dbo) + \ " WHERE a.DateFound Is Null AND a.DateLost > %s ORDER BY a.DateLost" % db.dd(giveup)) else: lostanimals = db.query(dbo, get_lostanimal_query(dbo) + \ " WHERE a.ID = %d" % 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 = db.query(dbo, get_foundanimal_query(dbo) + \ " WHERE a.ReturnToOwnerDate Is Null" \ " AND a.DateFound >= %s " % db.dd(oldestdate)) else: foundanimals = db.query( dbo, get_foundanimal_query(dbo) + " WHERE a.ID = %d" % foundanimalid) # Get the set of shelter animals for comparison shelteranimals = None if includeshelter: if animalid == 0: shelteranimals = db.query(dbo, animal.get_animal_query(dbo) + " WHERE " + \ "a.DateBroughtIn > %s" % db.dd(oldestdate)) else: shelteranimals = db.query( dbo, animal.get_animal_query(dbo) + " WHERE a.ID = %d" % animalid) for la in lostanimals: # 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["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() m.lid = la["ID"] m.lcontactname = la["OWNERNAME"] m.lcontactnumber = la["HOMETELEPHONE"] m.larealost = la["AREALOST"] m.lareapostcode = la["AREAPOSTCODE"] m.lagegroup = la["AGEGROUP"] m.lsexname = la["SEXNAME"] m.lspeciesname = la["SPECIESNAME"] m.lbreedname = la["BREEDNAME"] m.ldistinguishingfeatures = la["DISTFEAT"] m.lbasecolourname = la["BASECOLOURNAME"] m.ldatelost = la["DATELOST"] m.fid = str(fa["ID"]) m.fcontactname = fa["OWNERNAME"] m.fcontactnumber = fa["HOMETELEPHONE"] m.fareafound = fa["AREAFOUND"] m.fareapostcode = fa["AREAPOSTCODE"] m.fagegroup = fa["AGEGROUP"] m.fsexname = fa["SEXNAME"] m.fspeciesname = fa["SPECIESNAME"] m.fbreedname = fa["BREEDNAME"] m.fdistinguishingfeatures = fa["DISTFEAT"] m.fbasecolourname = fa["BASECOLOURNAME"] m.fdatefound = fa["DATEFOUND"] m.matchpoints = int( (float(matchpoints) / float(matchmax)) * 100.0) matches.append(m) # Shelter animals if includeshelter: for a in shelteranimals: matchpoints = 0 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["AREALOST"], a["ORIGINALOWNERADDRESS"], matcharealost) matchpoints += words(la["DISTFEAT"], a["MARKINGS"], matchfeatures) if str(a["ORIGINALOWNERPOSTCODE"]).find(la["AREAPOSTCODE"]): matchpoints += matchpostcode if date_diff_days(la["DATELOST"], a["DATEBROUGHTIN"]) <= 14: matchpoints += matchdatewithin2weeks if matchpoints > matchmax: matchpoints = matchmax if matchpoints >= matchpointfloor: m = LostFoundMatch() m.lid = la["ID"] m.lcontactname = la["OWNERNAME"] m.lcontactnumber = la["HOMETELEPHONE"] m.larealost = la["AREALOST"] m.lareapostcode = la["AREAPOSTCODE"] m.lagegroup = la["AGEGROUP"] m.lsexname = la["SEXNAME"] m.lspeciesname = la["SPECIESNAME"] m.lbreedname = la["BREEDNAME"] m.ldistinguishingfeatures = la["DISTFEAT"] m.lbasecolourname = la["BASECOLOURNAME"] m.ldatelost = la["DATELOST"] m.fid = a["CODE"] m.fcontactname = _("Shelter animal {0} '{1}'", l).format(a["CODE"], a["ANIMALNAME"]) m.fcontactnumber = a["SPECIESNAME"] m.fareafound = _("On Shelter", l) m.fareapostcode = a["ORIGINALOWNERPOSTCODE"] m.fagegroup = a["AGEGROUP"] m.fsexname = a["SEXNAME"] m.fspeciesname = a["SPECIESNAME"] m.fbreedname = a["BREEDNAME"] m.fdistinguishingfeatures = a["MARKINGS"] m.fbasecolourname = a["BASECOLOURNAME"] m.fdatefound = a["DATEBROUGHTIN"] m.matchpoints = int( (float(matchpoints) / float(matchmax)) * 100.0) matches.append(m) return matches
def run(self): if self.isPublisherExecuting(): return self.updatePublisherProgress(0) self.setLastError("") self.setStartPublishing() shelterid = configuration.helpinglostpets_orgid(self.dbo) if shelterid == "": self.setLastError( "No helpinglostpets.com organisation ID has been set.") return foundanimals = lostfound.get_foundanimal_find_simple(self.dbo) animals = self.getMatchingAnimals() if len(animals) == 0 and len(foundanimals) == 0: self.setLastError("No animals found to publish.") self.cleanup() return if not self.openFTPSocket(): self.setLastError("Failed opening FTP socket.") if self.logSearch("530 Login") != -1: self.log( "Found 530 Login incorrect: disabling HelpingLostPets publisher." ) configuration.publishers_enabled_disable(self.dbo, "hlp") self.cleanup() return csv = [] # Found Animals anCount = 0 for an in foundanimals: try: line = [] anCount += 1 self.log( "Processing Found Animal: %d: %s (%d of %d)" % (an["ID"], an["COMMENTS"], anCount, len(foundanimals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() return # OrgID line.append("\"%s\"" % shelterid) # PetID line.append("\"F%d\"" % an["ID"]) # Status line.append("\"Found\"") # Name line.append("\"%d\"" % an["ID"]) # Species line.append("\"%s\"" % an["SPECIESNAME"]) # Sex line.append("\"%s\"" % an["SEXNAME"]) # PrimaryBreed line.append("\"%s\"" % an["BREEDNAME"]) # SecondaryBreed line.append("\"\"") # Age, one of Baby, Young, Adult, Senior - just happens to match our default age groups line.append("\"%s\"" % an["AGEGROUP"]) # Altered - don't have line.append("\"\"") # Size, one of Small, Medium or Large or X-Large - also don't have line.append("\"\"") # ZipPostal line.append("\"%s\"" % an["AREAPOSTCODE"]) # Description notes = str(an["DISTFEAT"]) + "\n" + str( an["COMMENTS"]) + "\n" + str(an["AREAFOUND"]) # Strip carriage returns notes = notes.replace("\r\n", "<br />") notes = notes.replace("\r", "<br />") notes = notes.replace("\n", "<br />") notes = notes.replace("\"", "“") notes = notes.replace("\'", "‘") notes = notes.replace("\`", "‘") line.append("\"%s\"" % notes) # Photo line.append("\"\"") # Colour line.append("\"%s\"" % an["BASECOLOURNAME"]) # MedicalConditions line.append("\"\"") # LastUpdated line.append("\"%s\"" % i18n.python2unix(an["LASTCHANGEDDATE"])) # Add to our CSV file csv.append(",".join(line)) # Mark success in the log self.logSuccess( "Processed Found Animal: %d: %s (%d of %d)" % (an["ID"], an["COMMENTS"], anCount, len(foundanimals))) except Exception as err: self.logError( "Failed processing found animal: %s, %s" % (str(an["ID"]), err), sys.exc_info()) # Animals anCount = 0 for an in animals: try: line = [] anCount += 1 self.log("Processing: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) self.updatePublisherProgress( self.getProgress(anCount, len(animals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() return # Upload one image for this animal self.uploadImage(an, an["WEBSITEMEDIANAME"], an["SHELTERCODE"] + ".jpg") # OrgID line.append("\"%s\"" % shelterid) # PetID line.append("\"A%d\"" % an["ID"]) # Status line.append("\"Adoptable\"") # Name line.append("\"%s\"" % an["ANIMALNAME"]) # Species line.append("\"%s\"" % an["SPECIESNAME"]) # Sex line.append("\"%s\"" % an["SEXNAME"]) # PrimaryBreed line.append("\"%s\"" % an["BREEDNAME1"]) # SecondaryBreed if an["CROSSBREED"] == 1: line.append("\"%s\"" % an["BREEDNAME2"]) else: line.append("\"\"") # Age, one of Baby, Young, Adult, Senior ageinyears = i18n.date_diff_days(an["DATEOFBIRTH"], i18n.now(self.dbo.timezone)) ageinyears /= 365.0 agename = "Adult" if ageinyears < 0.5: agename = "Baby" elif ageinyears < 2: agename = "Young" elif ageinyears < 9: agename = "Adult" else: agename = "Senior" line.append("\"%s\"" % agename) # Altered line.append("%s" % self.hlpYesNo(an["NEUTERED"] == 1)) # Size, one of Small, Medium or Large or X-Large ansize = "Medium" if an["SIZE"] == 0: ansize = "X-Large" elif an["SIZE"] == 1: ansize = "Large" elif an["SIZE"] == 2: ansize = "Medium" elif an["SIZE"] == 3: ansize = "Small" line.append("\"%s\"" % ansize) # ZipPostal line.append("\"%s\"" % configuration.helpinglostpets_postal(self.dbo)) # Description line.append("\"%s\"" % self.getDescription(an, True)) # Photo line.append("\"%s.jpg\"" % an["SHELTERCODE"]) # Colour line.append("\"%s\"" % an["BASECOLOURNAME"]) # MedicalConditions line.append("\"%s\"" % an["HEALTHPROBLEMS"]) # LastUpdated line.append("\"%s\"" % i18n.python2unix(an["LASTCHANGEDDATE"])) # Add to our CSV file csv.append(",".join(line)) # Mark success in the log self.logSuccess("Processed: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) except Exception as err: self.logError( "Failed processing animal: %s, %s" % (str(an["SHELTERCODE"]), err), sys.exc_info()) # Mark published self.markAnimalsPublished(animals) header = "OrgID, PetID, Status, Name, Species, Sex, PrimaryBreed, SecondaryBreed, Age, Altered, Size, ZipPostal, Description, Photo, Colour, MedicalConditions, LastUpdated\n" filename = shelterid + ".txt" self.saveFile(os.path.join(self.publishDir, filename), header + "\n".join(csv)) self.log("Uploading datafile %s" % filename) self.upload(filename) self.log("Uploaded %s" % filename) self.log("-- FILE DATA --") self.log(header + "\n".join(csv)) # Clean up self.closeFTPSocket() self.deletePublishDirectory() self.saveLog() self.setPublisherComplete()
def run(self): self.log("PetRescuePublisher starting...") if self.isPublisherExecuting(): return self.updatePublisherProgress(0) self.setLastError("") self.setStartPublishing() token = configuration.petrescue_token(self.dbo) postcode = configuration.organisation_postcode(self.dbo) contact_name = configuration.organisation(self.dbo) contact_email = configuration.email(self.dbo) contact_number = configuration.organisation_telephone(self.dbo) if token == "": self.setLastError("No PetRescue auth token has been set.") return if postcode == "" or contact_email == "": self.setLastError( "You need to set your organisation postcode and contact email under Settings->Options->Shelter Details->Email" ) return animals = self.getMatchingAnimals() processed = [] if len(animals) == 0: self.setLastError("No animals found to publish.") self.cleanup() return headers = {"Authorization": "Token token=%s" % token, "Accept": "*/*"} anCount = 0 for an in animals: try: anCount += 1 self.log("Processing: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) self.updatePublisherProgress( self.getProgress(anCount, len(animals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() self.cleanup() return isdog = an.SPECIESID == 1 iscat = an.SPECIESID == 2 ageinyears = i18n.date_diff_days(an.DATEOFBIRTH, i18n.now()) vaccinated = medical.get_vaccinated(self.dbo, an.ID) size = "" if an.SIZE == 2: size = "medium" elif an.SIZE < 2: size = "large" else: size = "small" coat = "" if an.COATTYPE == 0: coat = "short" elif an.COATTYPE == 1: coat = "long" else: coat = "medium_coat" origin = "" if an.ISTRANSFER == 1 and an.BROUGHTINBYOWNERNAME.lower().find( "pound") == -1: origin = "shelter_transfer" elif an.ISTRANSFER == 1 and an.BROUGHTINBYOWNERNAME.lower( ).find("pound") != -1: origin = "pound_transfer" elif an.ORIGINALOWNERID > 0: origin = "owner_surrender" else: origin = "community_cat" photo_url = "%s?account=%s&method=animal_image&animalid=%d" % ( SERVICE_URL, self.dbo.database, an.ID) # Construct a dictionary of info for this animal data = { "remote_id": str(an.ID), # animal identifier in ASM "remote_source": "SM%s" % self.dbo.database, # system/database identifier "name": an.ANIMALNAME, # animal name "adoption_fee": i18n.format_currency_no_symbol(self.locale, an.FEE), "species_name": an.SPECIESNAME, "breed_names": self.get_breed_names(an), # breed1,breed2 or breed1 "mix": an.CROSSBREED == 1, # true | false "date_of_birth": i18n.format_date("%Y-%m-%d", an.DATEOFBIRTH), # iso "gender": an.SEXNAME.lower(), # male | female "personality": an.WEBSITEMEDIANOTES, # 20-4000 chars of free type "location_postcode": postcode, # shelter postcode "postcode": postcode, # shelter postcode "microchip_number": utils.iif(an.IDENTICHIPPED == 1, an.IDENTICHIPNUMBER, ""), "desexed": an.NEUTERED == 1, # true | false, validates to always true according to docs "contact_method": "email", # email | phone "size": utils.iif(isdog, size, ""), # dogs only - small | medium | high "senior": isdog and ageinyears > 7, # dogs only, true | false "vaccinated": vaccinated, # cats, dogs, rabbits, true | false "wormed": vaccinated, # cats & dogs, true | false "heart_worm_treated": vaccinated, # dogs only, true | false "coat": utils.iif(iscat, coat, ""), # cats only, short | medium_coat | long "intake_origin": utils.iif( iscat, origin, "" ), # cats only, community_cat | owner_surrender | pound_transfer | shelter_transfer "adoption_process": "", # 4,000 chars how to adopt "contact_details_source": "self", # self | user | group "contact_preferred_method": "email", # email | phone "contact_name": contact_name, # name of contact details owner "contact_number": contact_number, # number to enquire about adoption "contact_email": contact_email, # email to enquire about adoption "foster_needed": False, # true | false "interstate": True, # true | false - can the animal be adopted to another state "medical_notes": an.HEALTHPROBLEMS, # 4,000 characters medical notes "multiple_animals": False, # More than one animal included in listing true | false "photo_urls": [photo_url], # List of photo URL strings "status": "active" # active | removed | on_hold | rehomed | suspended | group_suspended } # PetRescue will insert/update accordingly based on whether remote_id/remote_source exists url = PETRESCUE_URL + "listings" jsondata = utils.json(data) self.log("Sending POST to %s to create/update listing: %s" % (url, jsondata)) r = utils.post_json(url, jsondata, headers=headers) if r["status"] != 200: self.logError("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], r["response"])) else: self.log("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], r["response"])) self.logSuccess("Processed: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) processed.append(an) except Exception as err: self.logError( "Failed processing animal: %s, %s" % (str(an["SHELTERCODE"]), err), sys.exc_info()) # Next, identify animals we've previously sent who: # 1. Have an active exit movement in the last month or died in the last month # 2. Have an entry in animalpublished/petrescue where the sent date is older than the active movement # 3. Have an entry in animalpublished/petrescue where the sent date is older than the deceased date animals = self.dbo.query("SELECT a.ID, a.ShelterCode, a.AnimalName, p.SentDate, a.ActiveMovementDate, a.DeceasedDate FROM animal a " \ "INNER JOIN animalpublished p ON p.AnimalID = a.ID AND p.PublishedTo='petrescue' " \ "WHERE Archived = 1 AND ((DeceasedDate Is Not Null AND DeceasedDate >= ?) OR " \ "(ActiveMovementDate Is Not Null AND ActiveMovementDate >= ? AND ActiveMovementType NOT IN (2,8))) " \ "ORDER BY a.ID", [self.dbo.today(offset=-30), self.dbo.today(offset=-30)]) for an in animals: if (an.ACTIVEMOVEMENTDATE and an.SENTDATE < an.ACTIVEMOVEMENTDATE ) or (an.DECEASEDDATE and an.SENTDATE < an.DECEASEDDATE): status = utils.iif(an.DECEASEDDATE is not None, "removed", "rehomed") data = {"status": status} jsondata = utils.json(data) url = PETRESCUE_URL + "listings/%s/SM%s" % (an.ID, self.dbo.database) self.log("Sending PATCH to %s to update existing listing: %s" % (url, jsondata)) r = utils.patch_json(url, jsondata, headers=headers) if r["status"] != 200: self.logError("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], r["response"])) else: self.log("HTTP %d, headers: %s, response: %s" % (r["status"], r["headers"], r["response"])) self.logSuccess("%s - %s: Marked with new status %s" % (an.SHELTERCODE, an.ANIMALNAME, status)) # By marking these animals in the processed list again, their SentDate # will become today, which should exclude them from sending these status # updates to close the listing again in future processed.append(an) # Mark sent animals published self.markAnimalsPublished(processed, first=True) self.cleanup()
def run(self): self.log("RescueGroupsPublisher starting...") if self.isPublisherExecuting(): return self.updatePublisherProgress(0) self.setLastError("") self.setStartPublishing() if not self.checkMappedSpecies(): self.setLastError("Not all species have been mapped.") self.cleanup() return if not self.checkMappedBreeds(): self.setLastError("Not all breeds have been mapped.") self.cleanup() return shelterid = configuration.rescuegroups_user(self.dbo) if shelterid == "": self.setLastError("No RescueGroups.org shelter id has been set.") self.cleanup() return animals = self.getMatchingAnimals() if len(animals) == 0: self.setLastError("No animals found to publish.") self.cleanup() return if not self.openFTPSocket(): self.setLastError("Failed opening FTP socket.") if self.logSearch("530 Login") != -1: self.log( "Found 530 Login incorrect: disabling RescueGroups publisher." ) configuration.publishers_enabled_disable(self.dbo, "rg") self.cleanup() return # Do the images first self.mkdir("import") self.chdir("import") self.mkdir("pictures") self.chdir("pictures", "import/pictures") csv = [] anCount = 0 for an in animals: try: line = [] anCount += 1 self.log("Processing: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) self.updatePublisherProgress( self.getProgress(anCount, len(animals))) # If the user cancelled, stop now if self.shouldStopPublishing(): self.log("User cancelled publish. Stopping.") self.resetPublisherProgress() self.cleanup() return # Upload images for this animal totalimages = self.uploadImages(an, False, 4) # orgID line.append("\"%s\"" % shelterid) # ID line.append("\"%s\"" % str(an["ID"])) # Status line.append("\"Available\"") # Last updated (Unix timestamp) line.append( "\"%s\"" % str(time.mktime(an["LASTCHANGEDDATE"].timetuple()))) # rescue ID (ID of animal at the rescue) line.append("\"%s\"" % an["SHELTERCODE"]) # Name line.append("\"%s\"" % an["ANIMALNAME"].replace("\"", "\"\"")) # Summary (no idea what this is for) line.append("\"\"") # Species line.append("\"%s\"" % an["PETFINDERSPECIES"]) # Readable breed line.append("\"%s\"" % an["BREEDNAME"]) # Primary breed line.append("\"%s\"" % an["PETFINDERBREED"]) # Secondary breed line.append("\"%s\"" % self.getPublisherBreed(an, 2)) # Sex line.append("\"%s\"" % an["SEXNAME"]) # Mixed line.append("\"%s\"" % self.rgYesNo(an["CROSSBREED"] == 1)) # dogs (good with) line.append("\"%s\"" % self.rgYesNoBlank(an["ISGOODWITHDOGS"])) # cats (good with) line.append("\"%s\"" % self.rgYesNoBlank(an["ISGOODWITHCATS"])) # kids (good with) line.append("\"%s\"" % self.rgYesNoBlank(an["ISGOODWITHCHILDREN"])) # declawed line.append("\"%s\"" % self.rgYesNo(an["DECLAWED"] == 1)) # housetrained line.append("\"%s\"" % self.rgYesNoBlank(an["ISHOUSETRAINED"])) # Age, one of Adult, Baby, Senior and Young ageinyears = i18n.date_diff_days(an["DATEOFBIRTH"], i18n.now(self.dbo.timezone)) ageinyears /= 365.0 agename = "Adult" if ageinyears < 0.5: agename = "Baby" elif ageinyears < 2: agename = "Young" elif ageinyears < 9: agename = "Adult" else: agename = "Senior" line.append("\"%s\"" % agename) # Special needs if an["CRUELTYCASE"] == 1: line.append("\"1\"") elif an["HASSPECIALNEEDS"] == 1: line.append("\"1\"") else: line.append("\"\"") # Altered line.append("\"%s\"" % self.rgYesNo(an["NEUTERED"] == 1)) # Size, one of S, M, L, XL ansize = "M" if an["SIZE"] == 0: ansize = "XL" elif an["SIZE"] == 1: ansize = "L" elif an["SIZE"] == 2: ansize = "M" elif an["SIZE"] == 3: ansize = "S" line.append("\"%s\"" % ansize) # uptodate (Has shots) line.append("\"%s\"" % self.rgYesNo( medical.get_vaccinated(self.dbo, int(an["ID"])))) # colour line.append("\"%s\"" % an["BASECOLOURNAME"]) # coatLength (not implemented) line.append("\"\"") # pattern (not implemented) line.append("\"\"") # courtesy if an["ISCOURTESY"] == 1: line.append("\"Yes\"") else: line.append("\"\"") # Description line.append("\"%s\"" % self.getDescription(an, crToBr=True)) # pic1-pic4 if totalimages > 0: # UploadAll isn't on, there was just one image with sheltercode == name if not self.pc.uploadAllImages: line.append("\"%s.jpg\",\"\",\"\",\"\"" % an["SHELTERCODE"]) else: # Output an entry for each image we uploaded, # upto a maximum of 4 for i in range(1, 5): if totalimages >= i: line.append("\"%s-%d.jpg\"" % (an["SHELTERCODE"], i)) else: line.append("\"\"") else: line.append("\"\",\"\",\"\",\"\"") # Add to our CSV file csv.append(",".join(line)) # Mark success in the log self.logSuccess("Processed: %s: %s (%d of %d)" % (an["SHELTERCODE"], an["ANIMALNAME"], anCount, len(animals))) except Exception as err: self.logError( "Failed processing animal: %s, %s" % (str(an["SHELTERCODE"]), err), sys.exc_info()) # Mark published self.markAnimalsPublished(animals, first=True) header = "orgID, animalID, status, lastUpdated, rescueID, name, summary, species, breed, " \ "primaryBreed, secondaryBreed, sex, mixed, dogs, cats, kids, declawed, housetrained, age, " \ "specialNeeds, altered, size, uptodate, color, coatLength, pattern, courtesy, description, pic1, " \ "pic2, pic3, pic4\n" self.saveFile(os.path.join(self.publishDir, "pets.csv"), header + "\n".join(csv)) self.log("Uploading datafile %s" % "pets.csv") self.chdir("..", "import") self.upload("pets.csv") self.log("Uploaded %s" % "pets.csv") self.log("-- FILE DATA --") self.log(header + "\n".join(csv)) self.cleanup()