def harvest (options):
    """Méthode de moissonage pour PMB utilisant la fonction d'export de 
    l'interface admin.

    *options*
       *options* est un dictionnaire, et doit contenir au moins les attributs 
       suivants:

       *host*
          Nom du serveur distant.
       *base_url*
          Racine de l'acces OAI.
       *user*
          Nom d'utilisateur ayant droit d'accès a l'admin
       *password*
          Mot de passe de l'utilisateur
       *database*
          Nom de la base de données que PMB utilise

    """
    c = PmbClient ()
    c.connect (options['host'])
    login_script = options['base_url'] + "main.php"
    export_script = options['base_url'] + "admin/convert/start_export.php"

    params = {'user': options['username'], 
            'password': options['password'], 
            'database': options['db']}
    c.login (params, login_script)

    params = {'export_type': '14', 'lender': 'x'}
    content = c.export (params, export_script)

    root = etree.XML (content.encode("utf-8"))
    article_nodes = root.findall (".//notice")
    nodes = []
    for node in article_nodes:
        meta = {}
        for c in node.getchildren ():
            if c.text:
                c.text = c.text.strip ()

            if c.tag == 'idNotice':
                meta_set (meta, IDENTIFIER, c.text)

            elif c.tag == 'zoneTitre':
                for t in c.getchildren ():
                    if t.tag == 'titrePrincipal':
                        meta_set (meta, TITLE, t.text)
                    else:
                        meta_set (meta, ALT_TITLE, t.text)

            elif c.tag == 'zoneAuteurPrincipal':
                meta_set (meta, CREATOR, read_person (c))

            elif c.tag == 'zoneAuteursAutres':
                meta_set (meta, CONTRIBUTOR, read_person (c))

            elif c.tag == 'zoneNotes':
                meta_set (meta, ABSTRACT, find_text (c, "noteResume"))

            elif c.tag == 'zoneEditeur':
                meta_set (meta, PUBLISHER, read_publisher (c))
                meta_set (meta, DATE_ISSUED, find_text (c, "annee"))

            elif c.tag == 'prixISBN':
                meta_set (meta, ISBN, find_text (c, "ISBN"))

            elif c.tag == 'zoneLiens':
                meta_set (meta, SOURCE, find_text (c, "lien"))

            elif c.tag == 'zoneLangue':
                for t in c.getchildren ():
                    if t.tag == 'langueDocument':
                        meta_set (meta, LANGUAGE, t.text)
                    elif t.tag == 'langueOriginale':
                        meta_set (meta, ORIG_LANG, t.text)

            elif c.tag == 'zoneCategories':
                meta_set (meta, SUBJECT, find_text (c, "categorie"))

        meta_set (meta, URI, "http://%s%scatalog.php?id=%s" % \
                (options['host'], options['base_url'], meta[IDENTIFIER]))

        nodes.append (meta)
    return nodes
def harvest (options):
    """Méthode de moissonage générique pour un système capable d'exporter ses 
    données au format `OAI <http://www.openarchives.org/>`_.

    *options*
       *options* est un dictionnaire, et doit contenir au moins les attributs 
       suivants:

       *server*
          Nom du serveur distant.
       *port*
          Port du service http.
       *base_url*
          Racine de l'acces OAI.

    La méthode retourne une liste d'éléments correspondant au format de 
    metadonnées.
    """
    oai2ns = "{http://www.openarchives.org/OAI/2.0/}"
    oaidc  = "{http://www.openarchives.org/OAI/2.0/oai_dc/}dc"
    metans = "{http://purl.org/dc/elements/1.1/}"

    # récupère les listsets du serveur
    store_listsets(options)

    url = find_location (options['url'])

    records = []
    root = load_xml (url.geturl() + "?verb=ListRecords&metadataPrefix=oai_dc")
    records.extend (root.findall (".//%srecord" % oai2ns))
    token = root.find (".//%sresumptiontoken" % oai2ns)
    if token is not None:
        print "total du serveur %s " % token.get("completeListSize")

    while token is not None:
        root = load_xml (url.geturl() + "?verb=ListRecords&resumptionToken=%s" % token.text)
        records.extend (root.findall (".//%srecord" % oai2ns))
        token = root.find (".//%sresumptiontoken" % oai2ns)


    nodes = []
    for record in records:
        meta = {}
        node = record.find (".//%sheader/%sdatestamp" % (oai2ns, oai2ns))

        meta[DATE_MODIFIED] = node.text

        dcnode = record.find (".//%s" % oaidc)
        if dcnode is not None:
            for c in dcnode.getchildren ():
                if c.text:
                    c.text = c.text.strip ()
                else:
                    c.text = ""
    
                if len (c.text) > 0:
                    match = map.get (c.tag.replace (metans, ""), [])
                    if c.tag.replace(metans, "") == "identifier" \
                       and not c.text.startswith("http"):
                        pass
                    else:
                        for field in match:
                            meta_set (meta, field, c.text)

            #print meta, etree.tostring(record, pretty_print = True)
            if meta.get("uri") is None and meta.get("source") is not None:
                meta['uri'] = meta['source']

            #un identifier doit être présent, s'il ne commence pas par http, prendre l'uri
            #(i.e : aide-en-ligne fournit un identifier sous forme de titre)
            if meta.get("identifier") is None:
                meta['identifier'] = meta.get('uri')

            # récupère les listsets associés
            listsets = record.findall (".//%sheader/%ssetspec" % (oai2ns, oai2ns))
            meta['listsets'] = [l.text for l in listsets]

            if meta.get("uri") is not None:
                nodes.append (meta)
    print "total récupérés %s" % len(nodes)       
    return nodes