Пример #1
0
 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
Пример #2
0
 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
Пример #3
0
    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)
Пример #4
0
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
Пример #5
0
    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()
Пример #6
0
    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()
Пример #7
0
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
Пример #8
0
    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
Пример #10
0
    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("\"", "&ldquo;")
                notes = notes.replace("\'", "&lsquo;")
                notes = notes.replace("\`", "&lsquo;")
                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()
Пример #11
0
    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()
Пример #12
0
    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()