コード例 #1
0
ファイル: service.py プロジェクト: rutaq/asm3
def handler(post, path, remoteip, referer, querystring):
    """ Handles the various service method types.
    post:        The GET/POST parameters
    path:        The current system path/code.PATH
    remoteip:    The IP of the caller
    referer:     The referer HTTP header
    querystring: The complete querystring
    return value is a tuple containing MIME type, max-age, content
    """
    # Get service parameters
    account = post["account"]
    username = post["username"]
    password = post["password"]
    method = post["method"]
    animalid = post.integer("animalid")
    formid = post.integer("formid")
    seq = post.integer("seq")
    title = post["title"]
    strip_personal = post.integer("sensitive") == 0

    cache_key = querystring.replace(" ", "")

    # Do we have a cached response for these parameters?
    cached_response = get_cached_response(cache_key)
    if cached_response is not None:
        al.debug("cache hit for %s" % (cache_key), "service.handler")
        return cached_response

    # Are we dealing with multiple databases, but no account was specified?
    if account == "" and MULTIPLE_DATABASES:
        return ("text/plain", 0, 0, "ERROR: No database/alias specified")

    dbo = db.get_database(account)

    if dbo.database in ("FAIL", "DISABLED", "WRONGSERVER"):
        al.error(
            "auth failed - invalid smaccount %s from %s (%s)" %
            (account, remoteip, dbo.database), "service.handler", dbo)
        return ("text/plain", 0, 0,
                "ERROR: Invalid database (%s)" % dbo.database)

    # If the database has disabled the service API, stop now
    if not configuration.service_enabled(dbo):
        al.error("Service API is disabled (%s)" % method, "service.handler",
                 dbo)
        return ("text/plain", 0, 0, "ERROR: Service API is disabled")

    # Do any database updates need doing in this db?
    dbo.installpath = path
    if dbupdate.check_for_updates(dbo):
        dbupdate.perform_updates(dbo)

    # Does the method require us to authenticate? If so, do it.
    user = None
    securitymap = ""
    if method in AUTH_METHODS:
        # If the database has authenticated service methods disabled, stop now
        if not configuration.service_auth_enabled(dbo):
            al.error("Service API for auth methods is disabled (%s)" % method,
                     "service.handler", dbo)
            return ("text/plain", 0, 0,
                    "ERROR: Service API for authenticated methods is disabled")
        user = users.authenticate(dbo, username, password)
        if user is None:
            al.error(
                "auth failed - %s/%s is not a valid username/password from %s"
                % (username, password, remoteip), "service.handler", dbo)
            return ("text/plain", 0, 0, "ERROR: Invalid username and password")
        securitymap = users.get_security_map(dbo, user["USERNAME"])

    # Get the preferred locale and timezone for the site
    l = configuration.locale(dbo)
    dbo.locale = l
    dbo.timezone = configuration.timezone(dbo)
    al.info("call %s->%s [%s %s]" % (username, method, str(animalid), title),
            "service.handler", dbo)

    if method == "animal_image":
        hotlink_protect("animal_image", referer)
        if utils.cint(animalid) == 0:
            al.error(
                "animal_image failed, %s is not an animalid" % str(animalid),
                "service.handler", dbo)
            return ("text/plain", 0, 0, "ERROR: Invalid animalid")
        else:
            mediadate, data = media.get_image_file_data(
                dbo, "animal", utils.cint(animalid), seq)
            if data == "NOPIC":
                mediadate, data = media.get_image_file_data(dbo, "nopic", 0)
            return set_cached_response(cache_key, "image/jpeg", 86400, 3600,
                                       data)

    elif method == "animal_thumbnail":
        if utils.cint(animalid) == 0:
            al.error(
                "animal_thumbnail failed, %s is not an animalid" %
                str(animalid), "service.handler", dbo)
            return ("text/plain", 0, 0, "ERROR: Invalid animalid")
        else:
            mediadate, data = media.get_image_file_data(
                dbo, "animalthumb", utils.cint(animalid), seq)
            if data == "NOPIC":
                mediadate, data = media.get_image_file_data(dbo, "nopic", 0)
            return set_cached_response(cache_key, "image/jpeg", 86400, 86400,
                                       data)

    elif method == "animal_view":
        if utils.cint(animalid) == 0:
            al.error(
                "animal_view failed, %s is not an animalid" % str(animalid),
                "service.handler", dbo)
            return ("text/plain", 0, 0, "ERROR: Invalid animalid")
        else:
            return set_cached_response(
                cache_key, "text/html", 86400, 120,
                publishers.html.get_animal_view(dbo, utils.cint(animalid)))

    elif method == "animal_view_adoptable_js":
        return set_cached_response(
            cache_key, "application/javascript", 10800, 600,
            publishers.html.get_animal_view_adoptable_js(dbo))

    elif method == "animal_view_adoptable_html":
        return set_cached_response(
            cache_key, "text/html", 86400, 120,
            publishers.html.get_animal_view_adoptable_html(dbo))

    elif method == "dbfs_image":
        hotlink_protect("dbfs_image", referer)
        return set_cached_response(
            cache_key, "image/jpeg", 86400, 86400,
            utils.iif(title.startswith("/"),
                      dbfs.get_string_filepath(dbo, title),
                      dbfs.get_string(dbo, title)))

    elif method == "extra_image":
        hotlink_protect("extra_image", referer)
        return set_cached_response(cache_key, "image/jpeg", 86400, 86400,
                                   dbfs.get_string(dbo, title, "/reports"))

    elif method == "json_adoptable_animal":
        if utils.cint(animalid) == 0:
            al.error(
                "json_adoptable_animal failed, %s is not an animalid" %
                str(animalid), "service.handler", dbo)
            return ("text/plain", 0, 0, "ERROR: Invalid animalid")
        else:
            users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                       users.VIEW_ANIMAL)
            rs = publishers.base.get_animal_data(
                dbo,
                None,
                utils.cint(animalid),
                include_additional_fields=True)
            return set_cached_response(cache_key, "application/json", 3600,
                                       3600, utils.json(rs))

    elif method == "html_adoptable_animals":
        return set_cached_response(cache_key, "text/html", 10800, 1800, \
            publishers.html.get_adoptable_animals(dbo, style=post["template"], \
                speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid"), locationid=post.integer("locationid")))

    elif method == "html_adopted_animals":
        return set_cached_response(cache_key, "text/html", 10800, 1800, \
            publishers.html.get_adopted_animals(dbo, daysadopted=post.integer("days"), style=post["template"], \
                speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid")))

    elif method == "html_deceased_animals":
        return set_cached_response(cache_key, "text/html", 10800, 1800, \
            publishers.html.get_deceased_animals(dbo, daysdeceased=post.integer("days"), style=post["template"], \
                speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid")))

    elif method == "html_held_animals":
        return set_cached_response(cache_key, "text/html", 10800, 1800, \
            publishers.html.get_held_animals(dbo, style=post["template"], \
                speciesid=post.integer("speciesid"), animaltypeid=post.integer("animaltypeid")))

    elif method == "json_adoptable_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        rs = publishers.base.get_animal_data(dbo,
                                             None,
                                             include_additional_fields=True)
        if strip_personal: rs = strip_personal_data(rs)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   utils.json(rs))

    elif method == "jsonp_adoptable_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        rs = publishers.base.get_animal_data(dbo,
                                             None,
                                             include_additional_fields=True)
        if strip_personal: rs = strip_personal_data(rs)
        return ("application/javascript", 0, 0,
                "%s(%s);" % (post["callback"], utils.json(rs)))

    elif method == "xml_adoptable_animal":
        if utils.cint(animalid) == 0:
            al.error(
                "xml_adoptable_animal failed, %s is not an animalid" %
                str(animalid), "service.handler", dbo)
            return ("text/plain", 0, 0, "ERROR: Invalid animalid")
        else:
            users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                       users.VIEW_ANIMAL)
            rs = publishers.base.get_animal_data(
                dbo,
                None,
                utils.cint(animalid),
                include_additional_fields=True)
            return set_cached_response(cache_key, "application/xml", 3600,
                                       3600, html.xml(rs))

    elif method == "xml_adoptable_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        rs = publishers.base.get_animal_data(dbo,
                                             None,
                                             include_additional_fields=True)
        if strip_personal: rs = strip_personal_data(rs)
        return set_cached_response(cache_key, "application/xml", 3600, 3600,
                                   html.xml(rs))

    elif method == "json_found_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_FOUND_ANIMAL)
        rs = lostfound.get_foundanimal_last_days(dbo)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   utils.json(rs))

    elif method == "jsonp_found_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_FOUND_ANIMAL)
        rs = lostfound.get_foundanimal_last_days(dbo)
        return ("application/javascript", 0, 0,
                "%s(%s);" % (post["callback"], utils.json(rs)))

    elif method == "xml_found_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_FOUND_ANIMAL)
        rs = lostfound.get_foundanimal_last_days(dbo)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   html.xml(rs))

    elif method == "json_lost_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_LOST_ANIMAL)
        rs = lostfound.get_lostanimal_last_days(dbo)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   utils.json(rs))

    elif method == "jsonp_lost_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_LOST_ANIMAL)
        rs = lostfound.get_lostanimal_last_days(dbo)
        return ("application/javascript", 0, 0,
                "%s(%s);" % (post["callback"], utils.json(rs)))

    elif method == "xml_lost_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_LOST_ANIMAL)
        rs = lostfound.get_lostanimal_last_days(dbo)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   html.xml(rs))

    elif method == "json_recent_adoptions":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        rs = movement.get_recent_adoptions(dbo)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   utils.json(rs))

    elif method == "jsonp_recent_adoptions":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        rs = movement.get_recent_adoptions(dbo)
        return ("application/javascript", 0, 0,
                "%s(%s);" % (post["callback"], utils.json(rs)))

    elif method == "xml_recent_adoptions":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        rs = movement.get_recent_adoptions(dbo)
        return set_cached_response(cache_key, "application/xml", 3600, 3600,
                                   html.xml(rs))

    elif method == "html_report":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_REPORT)
        crid = reports.get_id(dbo, title)
        p = reports.get_criteria_params(dbo, crid, post)
        rhtml = reports.execute(dbo, crid, username, p)
        return set_cached_response(cache_key, "text/html", 600, 600, rhtml)

    elif method == "csv_mail" or method == "csv_report":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_REPORT)
        crid = reports.get_id(dbo, title)
        p = reports.get_criteria_params(dbo, crid, post)
        rows, cols = reports.execute_query(dbo, crid, username, p)
        mcsv = utils.csv(l, rows, cols, True)
        return set_cached_response(cache_key, "text/csv", 600, 600, mcsv)

    elif method == "jsonp_recent_changes":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        sa = animal.get_recent_changes(dbo)
        return ("application/javascript", 0, 0,
                "%s(%s);" % (post["callback"], utils.json(sa)))

    elif method == "json_recent_changes":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        sa = animal.get_recent_changes(dbo)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   utils.json(sa))

    elif method == "xml_recent_changes":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        sa = animal.get_recent_changes(dbo)
        return set_cached_response(cache_key, "application/xml", 3600, 3600,
                                   html.xml(sa))

    elif method == "jsonp_shelter_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        sa = animal.get_shelter_animals(dbo)
        if strip_personal: sa = strip_personal_data(sa)
        return ("application/javascript", 0, 0,
                "%s(%s);" % (post["callback"], utils.json(sa)))

    elif method == "json_shelter_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        sa = animal.get_shelter_animals(dbo)
        if strip_personal: sa = strip_personal_data(sa)
        return set_cached_response(cache_key, "application/json", 3600, 3600,
                                   utils.json(sa))

    elif method == "xml_shelter_animals":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        sa = animal.get_shelter_animals(dbo)
        if strip_personal: sa = strip_personal_data(sa)
        return set_cached_response(cache_key, "application/xml", 3600, 3600,
                                   html.xml(sa))

    elif method == "rss_timeline":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.VIEW_ANIMAL)
        return set_cached_response(cache_key, "application/rss+xml", 3600,
                                   3600, html.timeline_rss(dbo))

    elif method == "upload_animal_image":
        users.check_permission_map(l, user["SUPERUSER"], securitymap,
                                   users.ADD_MEDIA)
        media.attach_file_from_form(dbo, username, media.ANIMAL, int(animalid),
                                    post)
        return ("text/plain", 0, 0, "OK")

    elif method == "online_form_html":
        if formid == 0:
            raise utils.ASMError(
                "method online_form_html requires a valid formid")
        return set_cached_response(cache_key, "text/html; charset=utf-8", 120,
                                   120,
                                   onlineform.get_onlineform_html(dbo, formid))

    elif method == "online_form_json":
        if formid == 0:
            raise utils.ASMError(
                "method online_form_json requires a valid formid")
        return set_cached_response(cache_key,
                                   "application/json; charset=utf-8", 30, 30,
                                   onlineform.get_onlineform_json(dbo, formid))

    elif method == "online_form_post":
        flood_protect("online_form_post", remoteip, 15)
        onlineform.insert_onlineformincoming_from_form(dbo, post, remoteip)
        redirect = post["redirect"]
        if redirect == "":
            redirect = BASE_URL + "/static/pages/form_submitted.html"
        return ("redirect", 0, 0, redirect)

    elif method == "sign_document":
        if formid == 0:
            raise utils.ASMError(
                "method sign_document requires a valid formid")
        if post["sig"] == "":
            return set_cached_response(cache_key, "text/html", 2, 2,
                                       sign_document_page(dbo, formid))
        else:
            media.sign_document(dbo, "service", formid, post["sig"],
                                post["signdate"])
            media.create_log(dbo, "service", formid, "ES02",
                             _("Document signed", l))
            return ("text/plain", 0, 0, "OK")

    else:
        al.error("invalid method '%s'" % method, "service.handler", dbo)
        raise utils.ASMError("Invalid method '%s'" % method)
コード例 #2
0
def csvexport_animals(dbo, dataset, animalids = "", includephoto = False):
    """
    Export CSV data for a set of animals.
    dataset: The named set of data to use
    animalids: If dataset == selshelter, a comma separated list of animals to export
    includephoto: Output a base64 encoded version of the animal's photo if True
    """
    l = dbo.locale
    q = ""
    if dataset == "all": q = "SELECT ID FROM animal ORDER BY ID"
    elif dataset == "shelter": q = "SELECT ID FROM animal WHERE Archived=0 ORDER BY ID"
    elif dataset == "nonshelter": q = "SELECT ID FROM animal WHERE NonShelterAnimal=1 ORDER BY ID"
    elif dataset == "selshelter": q = "SELECT ID FROM animal WHERE ID IN (%s) ORDER BY ID" % animalids
    ids = dbo.query(q)
    rows = []
    for aid in ids:
        row = collections.OrderedDict()
        a = animal.get_animal(dbo, aid.ID)
        if a is None: continue
        row["ANIMALCODE"] = a["SHELTERCODE"]
        row["ANIMALNAME"] = a["ANIMALNAME"]
        if includephoto: 
            row["ANIMALIMAGE"] = ""
        if a["WEBSITEIMAGECOUNT"] > 0 and includephoto:
            mdate, mdata = media.get_image_file_data(dbo, "animal", a["ID"])
            row["ANIMALIMAGE"] = "data:image/jpg;base64,%s" % base64.b64encode(mdata)
        row["ANIMALSEX"] = a["SEXNAME"]
        row["ANIMALTYPE"] = a["ANIMALTYPENAME"]
        row["ANIMALCOLOR"] = a["BASECOLOURNAME"]
        row["ANIMALBREED1"] = a["BREEDNAME1"]
        row["ANIMALBREED2"] = a["BREEDNAME2"]
        row["ANIMALDOB"] = i18n.python2display(l, a["DATEOFBIRTH"])
        row["ANIMALLOCATION"] = a["SHELTERLOCATIONNAME"]
        row["ANIMALUNIT"] = a["SHELTERLOCATIONUNIT"]
        row["ANIMALSPECIES"] = a["SPECIESNAME"]
        row["ANIMALCOMMENTS"] = a["ANIMALCOMMENTS"]
        row["ANIMALHIDDENDETAILS"] = a["HIDDENANIMALDETAILS"]
        row["ANIMALHEALTHPROBLEMS"] = a["HEALTHPROBLEMS"]
        row["ANIMALMARKINGS"] = a["MARKINGS"]
        row["ANIMALREASONFORENTRY"] = a["REASONFORENTRY"]
        row["ANIMALNEUTERED"] = a["NEUTERED"]
        row["ANIMALNEUTEREDDATE"] = i18n.python2display(l, a["NEUTEREDDATE"])
        row["ANIMALMICROCHIP"] = a["IDENTICHIPNUMBER"]
        row["ANIMALMICROCHIPDATE"] = i18n.python2display(l, a["IDENTICHIPDATE"])
        row["ANIMALENTRYDATE"] = i18n.python2display(l, a["DATEBROUGHTIN"])
        row["ANIMALDECEASEDDATE"] = i18n.python2display(l, a["DECEASEDDATE"])
        row["ANIMALNOTFORADOPTION"] = a["ISNOTAVAILABLEFORADOPTION"]
        row["ANIMALNONSHELTER"] = a["NONSHELTERANIMAL"]
        row["ANIMALGOODWITHCATS"] = a["ISGOODWITHCATSNAME"]
        row["ANIMALGOODWITHDOGS"] = a["ISGOODWITHDOGSNAME"]
        row["ANIMALGOODWITHKIDS"] = a["ISGOODWITHCHILDRENNAME"]
        row["ANIMALHOUSETRAINED"] = a["ISHOUSETRAINEDNAME"]
        row["ORIGINALOWNERTITLE"] = a["ORIGINALOWNERTITLE"]
        row["ORIGINALOWNERINITIALS"] = a["ORIGINALOWNERINITIALS"]
        row["ORIGINALOWNERFIRSTNAME"] = a["ORIGINALOWNERFORENAMES"]
        row["ORIGINALOWNERLASTNAME"] = a["ORIGINALOWNERSURNAME"]
        row["ORIGINALOWNERADDRESS"] = a["ORIGINALOWNERADDRESS"]
        row["ORIGINALOWNERCITY"] = a["ORIGINALOWNERTOWN"]
        row["ORIGINALOWNERSTATE"] = a["ORIGINALOWNERCOUNTY"]
        row["ORIGINALOWNERZIPCODE"] = a["ORIGINALOWNERPOSTCODE"]
        row["ORIGINALOWNERHOMEPHONE"] = a["ORIGINALOWNERHOMETELEPHONE"]
        row["ORIGINALOWNERWORKPHONE"] = a["ORIGINALOWNERWORKTELEPHONE"]
        row["ORIGINALOWNERCELLPHONE"] = a["ORIGINALOWNERMOBILETELEPHONE"]
        row["ORIGINALOWNEREMAIL"] = a["ORIGINALOWNEREMAILADDRESS"]
        row["MOVEMENTTYPE"] = a["ACTIVEMOVEMENTTYPE"]
        row["MOVEMENTDATE"] = i18n.python2display(l, a["ACTIVEMOVEMENTDATE"])
        row["PERSONTITLE"] = a["CURRENTOWNERTITLE"]
        row["PERSONINITIALS"] = a["CURRENTOWNERINITIALS"]
        row["PERSONFIRSTNAME"] = a["CURRENTOWNERFORENAMES"]
        row["PERSONLASTNAME"] = a["CURRENTOWNERSURNAME"]
        row["PERSONADDRESS"] = a["CURRENTOWNERADDRESS"]
        row["PERSONCITY"] = a["CURRENTOWNERTOWN"]
        row["PERSONSTATE"] = a["CURRENTOWNERCOUNTY"]
        row["PERSONZIPCODE"] = a["CURRENTOWNERPOSTCODE"]
        row["PERSONFOSTERER"] = a["ACTIVEMOVEMENTTYPE"] == 2 and 1 or 0
        row["PERSONHOMEPHONE"] = a["CURRENTOWNERHOMETELEPHONE"]
        row["PERSONWORKPHONE"] = a["CURRENTOWNERWORKTELEPHONE"]
        row["PERSONCELLPHONE"] = a["CURRENTOWNERMOBILETELEPHONE"]
        row["PERSONEMAIL"] = a["CURRENTOWNEREMAILADDRESS"]
        row["VACCINATIONTYPE"] = ""
        row["VACCINATIONDUEDATE"] = ""
        row["VACCINATIONGIVENDATE"] = ""
        row["VACCINATIONEXPIRESDATE"] = ""
        row["VACCINATIONMANUFACTURER"] = ""
        row["VACCINATIONBATCHNUMBER"] = ""
        row["VACCINATIONCOMMENTS"] = ""
        row["MEDICALNAME"] = ""
        row["MEDICALDOSAGE"] = ""
        row["MEDICALGIVENDATE"] = ""
        row["MEDICALCOMMENTS"] = ""
        rows.append(row)
        for v in medical.get_vaccinations(dbo, a["ID"]):
            row = collections.OrderedDict()
            row["VACCINATIONTYPE"] = v["VACCINATIONTYPE"]
            row["VACCINATIONDUEDATE"] = i18n.python2display(l, v["DATEREQUIRED"])
            row["VACCINATIONGIVENDATE"] = i18n.python2display(l, v["DATEOFVACCINATION"])
            row["VACCINATIONEXPIRESDATE"] = i18n.python2display(l, v["DATEEXPIRES"])
            row["VACCINATIONMANUFACTURER"] = v["MANUFACTURER"]
            row["VACCINATIONBATCHNUMBER"] = v["BATCHNUMBER"]
            row["VACCINATIONCOMMENTS"] = v["COMMENTS"]
            row["ANIMALCODE"] = a["SHELTERCODE"]
            row["ANIMALNAME"] = a["ANIMALNAME"]
            rows.append(row)
        for m in medical.get_regimens(dbo, a["ID"]):
            row = collections.OrderedDict()
            row["MEDICALNAME"] = m["TREATMENTNAME"]
            row["MEDICALDOSAGE"] = m["DOSAGE"]
            row["MEDICALGIVENDATE"] = i18n.python2display(l, m["STARTDATE"])
            row["MEDICALCOMMENTS"] = m["COMMENTS"]
            row["ANIMALCODE"] = a["SHELTERCODE"]
            row["ANIMALNAME"] = a["ANIMALNAME"]
            rows.append(row)
        del a
    if len(rows) == 0: return ""
    keys = rows[0].keys()
    out = StringIO()
    dict_writer = csv.DictWriter(out, keys)
    dict_writer.writeheader()
    dict_writer.writerows(rows)
    return out.getvalue()