Renommé auf_sep
authorCyril Robert <Cyril Robert crobert@inverse.ca>
Mon, 1 Mar 2010 22:17:30 +0000 (17:17 -0500)
committerCyril Robert <Cyril Robert crobert@inverse.ca>
Mon, 1 Mar 2010 22:17:30 +0000 (17:17 -0500)
32 files changed:
Apps/auf_savoirs_backend/__init__.py [new file with mode: 0644]
Apps/auf_savoirs_backend/globals.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/__init__.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/harvest.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/harvesters/__init__.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/harvesters/oai/__init__.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/harvesters/oai/generic.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/harvesters/pmb/__init__.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/harvesters/pmb/export.py [new file with mode: 0644]
Apps/auf_savoirs_backend/harvest/harvesters/pmb/pmbclient.py [new file with mode: 0644]
Apps/auf_savoirs_backend/sep/__init__.py [new file with mode: 0644]
Apps/auf_savoirs_backend/sep/io.py [new file with mode: 0644]
Apps/auf_savoirs_backend/sep/utils.py [new file with mode: 0644]
Apps/auf_savoirs_backend/setup.py [new file with mode: 0644]
Apps/auf_savoirs_backend/storage/__init__.py [new file with mode: 0644]
Apps/auf_savoirs_backend/storage/json.py [new file with mode: 0644]
Apps/auf_sep/__init__.py [deleted file]
Apps/auf_sep/globals.py [deleted file]
Apps/auf_sep/harvest/__init__.py [deleted file]
Apps/auf_sep/harvest/harvest.py [deleted file]
Apps/auf_sep/harvest/harvesters/__init__.py [deleted file]
Apps/auf_sep/harvest/harvesters/oai/__init__.py [deleted file]
Apps/auf_sep/harvest/harvesters/oai/generic.py [deleted file]
Apps/auf_sep/harvest/harvesters/pmb/__init__.py [deleted file]
Apps/auf_sep/harvest/harvesters/pmb/export.py [deleted file]
Apps/auf_sep/harvest/harvesters/pmb/pmbclient.py [deleted file]
Apps/auf_sep/sep/__init__.py [deleted file]
Apps/auf_sep/sep/io.py [deleted file]
Apps/auf_sep/sep/utils.py [deleted file]
Apps/auf_sep/setup.py [deleted file]
Apps/auf_sep/storage/__init__.py [deleted file]
Apps/auf_sep/storage/json.py [deleted file]

diff --git a/Apps/auf_savoirs_backend/__init__.py b/Apps/auf_savoirs_backend/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Apps/auf_savoirs_backend/globals.py b/Apps/auf_savoirs_backend/globals.py
new file mode 100644 (file)
index 0000000..724f51e
--- /dev/null
@@ -0,0 +1,25 @@
+# -*- encoding: utf-8 -*-
+
+
+#####
+# Meta fields
+TITLE           = 'title'       #X str
+ALT_TITLE       = 'alt_title'   #X str
+CREATOR         = 'creator'     #X str
+CONTRIBUTOR     = 'contributor' #X array (str)
+DESCRIPTION     = 'description' #  str
+ABSTRACT        = 'abstract'    #X str
+SUBJECT         = 'subject'     #X str
+PUBLISHER       = 'publisher'   #X str
+DATE_CREATION   = 'creation'    #  date
+DATE_ISSUED     = 'issued'      #X date
+DATE_MODIFIED   = 'modified'    #  date
+TYPE            = 'type'        #  str
+FORMAT          = 'format'      #  str
+IDENTIFIER      = 'identifier'  #X str (int?)
+ISBN            = 'isbn'        #X str
+URI             = 'uri'         #X str (url)
+SOURCE          = 'source'      #X str (url)
+LANGUAGE        = 'language'    #X str
+ORIG_LANG       = 'orig_lang'   #X str
+
diff --git a/Apps/auf_savoirs_backend/harvest/__init__.py b/Apps/auf_savoirs_backend/harvest/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Apps/auf_savoirs_backend/harvest/harvest.py b/Apps/auf_savoirs_backend/harvest/harvest.py
new file mode 100644 (file)
index 0000000..4aa65f3
--- /dev/null
@@ -0,0 +1,20 @@
+import sys
+
+sys.path.append ("../")
+from conf import RESOURCES
+from sep.io import SEP
+
+
+if __name__ == "__main__":
+    sep = SEP ()
+
+    for name in RESOURCES.keys ():
+        print "Import:", name
+        options = RESOURCES[name]
+        module = 'harvesters.%s.%s' % (options['type'], options['acces'])
+        __import__ (module)
+        harvester = sys.modules[module]
+        nodes = harvester.harvest (options)
+        print "Ajout de", len(nodes), "references"
+        for node in nodes:
+            sep.add (node)
diff --git a/Apps/auf_savoirs_backend/harvest/harvesters/__init__.py b/Apps/auf_savoirs_backend/harvest/harvesters/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Apps/auf_savoirs_backend/harvest/harvesters/oai/__init__.py b/Apps/auf_savoirs_backend/harvest/harvesters/oai/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Apps/auf_savoirs_backend/harvest/harvesters/oai/generic.py b/Apps/auf_savoirs_backend/harvest/harvesters/oai/generic.py
new file mode 100644 (file)
index 0000000..51d735b
--- /dev/null
@@ -0,0 +1,111 @@
+# -*- encoding: utf-8 -*-
+from lxml import etree
+import sys, urllib, httplib, re
+
+sys.path.append ("../../../")
+from globals import *
+from sep.utils import safe_append, print_structure
+
+
+
+def find_location (server, port, base_url):
+    rc = None
+    possible = ("perl/oai2", "cgi/oai2", "cgi-bin/oaiserver")
+    for test in possible:
+        url = base_url + test
+        handle = httplib.HTTPConnection (server, port)
+        handle.request ("GET", url + "?verb=Identify")
+        r = handle.getresponse ()
+        if r.status == 200:
+            rc = "http://" + server + url
+            break
+    return rc
+
+def load_xml (url):
+    ud = urllib.urlopen (url)
+    content = ud.read ().decode('iso-8859-1')
+    # Greenstone crap
+    content = content.replace ("xmlns=\"http://www.openarchives.com/OAI/2.0\"",
+            "xmlns=\"http://www.openarchives.org/OAI/2.0/\"")
+    ud.close ()
+    root = etree.XML (content.encode("utf-8"))
+    return root
+
+
+def cleanup (node):
+    node.tag = re.sub (r'^\{[a-zA-Z0-9_:.-\/]+\}', '', node.tag)
+    return node
+
+
+def harvest (options):
+    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/}"
+
+    uri = find_location (options['server'], 
+            options['port'], 
+            options['base_url'])
+
+    records = []
+    root = load_xml (uri + "?verb=ListRecords&metadataPrefix=oai_dc")
+    #print_structure (root)
+    records.extend (root.findall (".//%srecord" % oai2ns))
+    token = root.find (".//%sresumptionToken" % oai2ns)
+
+    while token is not None:
+        root = load_xml (uri + "?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)
+        for c in dcnode.getchildren ():
+            if c.text:
+                c.text = c.text.strip ()
+            else:
+                c.text = ""
+
+            if c.tag == "%stitle" % metans:
+                meta[TITLE] = c.text
+
+            elif c.tag == "%screator" % metans:
+                meta[CREATOR] = c.text
+
+            elif c.tag == "%scontributor" % metans:
+                safe_append (meta, CONTRIBUTOR, c.text)
+
+            elif c.tag == "%ssubject" % metans:
+                safe_append (meta, SUBJECT, c.text)
+
+            elif c.tag == "%sdescription" % metans:
+                meta[DESCRIPTION] = c.text
+
+            elif c.tag == "%spublisher" % metans:
+                if len (c.text) > 0:
+                    meta[PUBLISHER] = c.text
+
+            elif c.tag == "%sdate" % metans:
+                meta[DATE_CREATION] = c.text
+
+            elif c.tag == "%stype" % metans:
+                safe_append (meta, TYPE, c.text)
+
+            elif c.tag == "%sidentifier" % metans:
+                meta[IDENTIFIER] = c.text
+                meta[URI] = c.text
+
+            elif c.tag == '%sformat' % metans:
+                safe_append (meta, FORMAT, c.text)
+
+            elif c.tag == '%srelation' % metans:
+                meta[SOURCE] = c.text
+
+        nodes.append (meta)
+
+    return nodes
diff --git a/Apps/auf_savoirs_backend/harvest/harvesters/pmb/__init__.py b/Apps/auf_savoirs_backend/harvest/harvesters/pmb/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Apps/auf_savoirs_backend/harvest/harvesters/pmb/export.py b/Apps/auf_savoirs_backend/harvest/harvesters/pmb/export.py
new file mode 100644 (file)
index 0000000..f91fbe1
--- /dev/null
@@ -0,0 +1,92 @@
+# -*- encoding: utf-8 -*-
+from pmbclient import PmbClient
+from lxml import etree
+import sys, re
+
+sys.path.append ("../../../")
+from globals import *
+from sep.utils import safe_append, print_structure, find_text
+
+
+def read_person (node):
+    rc = u"%s %s" % (find_text(node, "prenom"), 
+            find_text(node, "nom"))
+    dates = find_text(node, "dates")
+    if len (dates) > 0:
+        rc += " (%s)" % dates
+    return rc
+
+def read_publisher (node):
+    return u"%s, %s" % (find_text(node, "nom"), 
+            find_text(node, "ville"))
+    
+
+
+def harvest (options):
+    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[IDENTIFIER] = c.text
+
+            elif c.tag == 'zoneTitre':
+                for t in c.getchildren ():
+                    if t.tag == 'titrePrincipal':
+                        meta[TITLE] = t.text
+                    else:
+                        safe_append (meta, ALT_TITLE, t.text)
+
+            elif c.tag == 'zoneAuteurPrincipal':
+                meta[CREATOR] = read_person (c)
+
+            elif c.tag == 'zoneAuteursAutres':
+                safe_append (meta, CONTRIBUTOR, read_person (c))
+
+            elif c.tag == 'zoneNotes':
+                meta[ABSTRACT] = find_text (c, "noteResume")
+
+            elif c.tag == 'zoneEditeur':
+                safe_append (meta, PUBLISHER, read_publisher (c))
+                meta[DATE_ISSUED] = find_text (c, "annee")
+
+            elif c.tag == 'prixISBN':
+                meta[ISBN] = find_text (c, "ISBN")
+
+            elif c.tag == 'zoneLiens':
+                meta[SOURCE] = find_text (c, "lien")
+
+            elif c.tag == 'zoneLangue':
+                for t in c.getchildren ():
+                    if t.tag == 'langueDocument':
+                        safe_append (meta, LANGUAGE, t.text)
+                    elif t.tag == 'langueOriginale':
+                        safe_append (meta, ORIG_LANG, t.text)
+
+            elif c.tag == 'zoneCategories':
+                safe_append (meta, SUBJECT, find_text (c, "categorie"))
+
+        meta[URI] = "http://%s%scatalog.php?id=%s" % \
+                (options['host'], options['base_url'], meta[IDENTIFIER])
+        nodes.append (meta)
+    return nodes
+
+
diff --git a/Apps/auf_savoirs_backend/harvest/harvesters/pmb/pmbclient.py b/Apps/auf_savoirs_backend/harvest/harvesters/pmb/pmbclient.py
new file mode 100644 (file)
index 0000000..a301da8
--- /dev/null
@@ -0,0 +1,122 @@
+import httplib, urllib, Cookie, time
+from exceptions import Exception
+from lxml import etree
+
+
+class PmbClient:
+    handle = None
+    cookies = ""
+
+    def __init__ (self):
+        pass
+
+    def __del__ (self):
+        if self.handle:
+            self.handle.close ()
+
+    def connect (self, server, port=80):
+        if self.handle:
+            self.handle.close ()
+
+        self.handle = httplib.HTTPConnection (server, port)
+
+    def login (self, params, script):
+        params = urllib.urlencode(params)
+        headers = {"Content-type": "application/x-www-form-urlencoded",
+                "Accept": "text/plain"}
+
+        self.handle.request ("POST", script, params, headers)
+        r = self.handle.getresponse ()
+
+        if r.status != 200:
+            raise Exception ("Login failed: %s %s" % (r.status, r.reason))
+        r.read ()
+
+        bc = Cookie.BaseCookie ()
+        bc.load (r.getheader ("set-cookie"))
+        tmp = bc.output (header="Cookie:").replace (",", "").split("\n")
+        cookies = []
+        for line in tmp:
+            cookie = line.replace ("Cookie: ", "").strip ()
+            cookies.append (cookie)
+        self.cookies = "; ".join (cookies)
+
+    def get_response (self):
+        r = None
+        while r is None:
+            try:
+                r = self.handle.getresponse ()
+            except:
+                r = None
+            time.sleep (1)
+        return r
+
+    def find_next_location (self, buffer):
+        rc = ""
+
+        root = etree.HTML (buffer)
+        tmp = root.findall (".//script")
+        script = tmp[len(tmp)-1]
+
+        buffer = script.text
+        match = "document.location='"
+        i = buffer.rfind (match)
+        if i >= 0:
+            i += len (match)
+            tmp = buffer[i:]
+            j = tmp.find ("'\"")
+            if j >= 0:
+                rc = tmp[0:j]
+        return rc
+
+    def make_url (self, old, script):
+        tmp = old.split ("/")
+        tmp[len(tmp)-1] = script
+        nextscript = "/".join(tmp)
+        return nextscript
+
+    def read_form (self, buffer):
+        script = ""
+        params = {}
+
+        root = etree.HTML(buffer)
+        form = root.find (".//form")
+        script = form.attrib['action']
+        
+        inputs = form.findall (".//input")
+        for input in inputs:
+            try:
+                params[input.attrib['name']] = input.attrib['value']
+            except:
+                pass
+
+        return (params, script)
+
+    def export (self, params, script):
+        params = urllib.urlencode(params)
+        headers = {"Content-type": "application/x-www-form-urlencoded",
+                "Accept": "text/plain",
+                "Cookie": self.cookies}
+
+        self.handle.request ("POST", script, params, headers)
+        r = self.get_response ()
+        next = self.find_next_location (r.read ())
+        nextscript = self.make_url (script, next)
+
+        self.handle.request ("GET", nextscript, headers={"Cookie": self.cookies})
+        r = self.get_response ()
+        next = self.find_next_location (r.read ())
+        nextscript = self.make_url (script, next)
+
+        self.handle.request ("GET", nextscript, headers={"Cookie": self.cookies})
+        r = self.get_response ()
+        (params, next) = self.read_form (r.read ())
+
+        nextscript = self.make_url (script, next)
+        params = urllib.urlencode(params)
+        self.handle.request ("POST", nextscript, params, headers)
+        r = self.get_response ()
+        content = r.read ()
+
+        # Saloperie de PMB force le retour en iso crap
+        return content.decode('iso-8859-1')
diff --git a/Apps/auf_savoirs_backend/sep/__init__.py b/Apps/auf_savoirs_backend/sep/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Apps/auf_savoirs_backend/sep/io.py b/Apps/auf_savoirs_backend/sep/io.py
new file mode 100644 (file)
index 0000000..b4862aa
--- /dev/null
@@ -0,0 +1,68 @@
+# -*- encoding: utf-8 -*-
+from exceptions import Exception
+import sys
+sys.path.append ("../")
+import conf
+from globals import *
+
+
+class SEP:
+    backend = None
+    required_methods = ['open', 'close', 'add', 'delete', 'search', 'update']
+
+    def __init__ (self):
+        # Charge le backend de stockage
+        module = 'storage.%s' % conf.BACKEND['plugin']
+        __import__ (module)
+        backend_module = sys.modules[module]
+        self.backend = backend_module.Backend (conf.BACKEND['options'])
+
+        # Vérifier qu'il répond à l'API
+        available = dir (self.backend)
+        for method in self.required_methods:
+            if not method in available:
+                msg = "Backend incomplet\nManque %s\nexiste %s" \
+                        % (method, available)
+                raise Exception(msg)
+
+        # Charge
+        self.backend.open ()
+
+
+    def __del__ (self):
+        self.backend.close ()
+
+
+#############
+# API public
+    def search (self, q = None):
+        return self.backend.search (q)
+
+    def get (self, id):
+        return self.backend.get (id)
+
+    def add (self, metadata):
+        exists = self.search (q = {URI: metadata[URI]})
+        if len (exists) > 0:
+            id = exists[0]
+            return self.update (id, metadata)
+        else:
+            return self.backend.add (metadata)
+
+    def update (self, id, metadata):
+        if len (self.search (id = id)) > 0:
+            self.backend.update (id, metadata)
+        else:
+            raise Exception ("Objet inexistant")
+
+    def delete (self, id):
+        self.backend.delete (id)
+
+
+
+
+if __name__ == '__main__':
+    s = SEP()
+    print s.get(5)
+    print s.search (q = {'description': 'démontrer'})
+
diff --git a/Apps/auf_savoirs_backend/sep/utils.py b/Apps/auf_savoirs_backend/sep/utils.py
new file mode 100644 (file)
index 0000000..3413433
--- /dev/null
@@ -0,0 +1,27 @@
+# -*- encoding: utf-8 -*-
+
+
+def safe_append (dict, key, value):
+    try:
+        list = dict[key]
+    except:
+        dict[key] = []
+    dict[key].append (value)
+
+def print_structure(element, tab=""):
+    if element.text:
+        element.text = element.text.strip ()
+    if element.tail:
+        element.tail = element.tail.strip ()
+    line = u"%s<%s>: %s, %s, %s" % (tab, element.tag, element.text, 
+        element.tail, element.attrib)
+    print line.encode ("utf-8")
+    for x in element.getchildren():
+        print_structure(x, tab+" ")
+
+def find_text (node, tag):
+    rc = ""
+    n = node.find (".//%s" % tag)
+    if n is not None:
+        rc = n.text
+    return rc
diff --git a/Apps/auf_savoirs_backend/setup.py b/Apps/auf_savoirs_backend/setup.py
new file mode 100644 (file)
index 0000000..a78276e
--- /dev/null
@@ -0,0 +1,22 @@
+# -*- encoding: utf-8 -*-
+from setuptools import setup, find_packages
+import sys, os
+
+version = '0.1'
+
+setup(
+        name='auf_sep',
+        version=version,
+        description="Savoirs en partage, module de stockage/indexation",
+        classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+        keywords='',
+        author='Cyril Robert',
+        author_email='cyril.robert@auf.org',
+        url='',
+        license='GPL',
+        packages=['auf_sep',],
+        package_dir={'auf_sep': '.',},
+        include_package_data=True,
+        zip_safe=False,
+        install_requires=[],
+        )
diff --git a/Apps/auf_savoirs_backend/storage/__init__.py b/Apps/auf_savoirs_backend/storage/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Apps/auf_savoirs_backend/storage/json.py b/Apps/auf_savoirs_backend/storage/json.py
new file mode 100644 (file)
index 0000000..366b462
--- /dev/null
@@ -0,0 +1,139 @@
+# -*- encoding: utf-8 -*-
+import os
+import simplejson as json
+import sys
+sys.path.append ("../")
+from globals import *
+
+####
+# Attention: pas efficace
+#
+# Trucs a faire pour l'optimiser:
+#  - Ajouter un index par url, trié
+#  - Ajouter un index (full inverted) pour les champs texte
+
+INDEX_NAME = 'index.json'
+DATA_FMT = '%s.json'
+
+
+class File:
+    def __init__ (self, path, mode):
+        self.fp = open (path, mode)
+
+    def __del__ (self):
+        if self.fp:
+            self.fp.close ()
+
+    def read (self):
+        return json.load (self.fp)
+
+    def write (self, object):
+        json.dump (object, self.fp)
+
+
+class Backend:
+    options = {}
+    data = {}
+    needs_write = False
+    remove = []
+    last_index = 0
+
+    def __init__ (self, options):
+        self.options = options
+        # juste pour lancer une exception si jamais on a pas le path
+        p = self.options['path']
+
+    def open (self):
+        p = self.options['path']
+        ip = os.path.join (p, INDEX_NAME)
+
+        if not os.path.exists (p):
+            os.makedirs (p)
+
+        elif os.path.exists (ip):
+            index = File (ip, 'r').read ()
+            for id in index:
+                fp = os.path.join (p, DATA_FMT % id)
+                meta = File (fp, 'r').read ()
+                meta['needs_write'] = False
+                self.data[id] = meta
+                if id > self.last_index:
+                    self.last_index = id
+
+    def close (self):
+        if self.needs_write:
+            p = self.options['path']
+            
+            # Enleve les vieux trucs
+            if len (self.remove) > 0:
+                for id in self.remove:
+                    fp = os.path.join (p, DATA_FMT % id)
+                    os.remove (fp)
+
+            # Ecrit l'index
+            File(os.path.join(p, INDEX_NAME), 'w').write(self.data.keys ())
+
+            # Ecrit les données
+            for id in self.data.keys ():
+                record = self.data[id]
+                if record['needs_write']:
+                    del (record['needs_write'])
+                    fp = os.path.join (p, DATA_FMT % id)
+                    File (fp, 'w').write (record)
+
+    def add (self, metadata):
+        self.last_index += 1
+        metadata['needs_write'] = True
+        self.data[self.last_index] = metadata
+        self.needs_write = True
+        return self.last_index
+
+    def delete (self, id):
+        self.needs_write = True
+        del (self.data[id])
+        self.remove.append (id)
+
+    def update (self, id, metadata):
+        metadata['needs_write'] = True
+        self.needs_write = True
+        for k in metadata.keys():
+            self.data[id][k] = metadata[k]
+
+    def get (self, id):
+        rc = None
+        if id in self.data.keys ():
+            rc = self.data[id]
+        return rc
+
+    def filter_string_contains (self, set, q, key):
+        rc = []
+        words = q.get (key)
+        if words:
+            for k in set:
+                if self.data[k][key].encode("utf-8").find (words) > -1:
+                    rc.append (k)
+        else:
+            rc = set
+        return rc
+
+    def filter_string_equals (self, set, q, key):
+        rc = []
+        string = q.get (key)
+        if string:
+            for k in set:
+                if self.data[k][key].encode("utf-8") == string:
+                    rc.append (k)
+        else:
+            rc = set
+        return rc
+
+    def search (self, q):
+        rc = self.data.keys ()
+
+        # Très incomplet
+        rc = self.filter_string_equals (rc, q, URI)
+        rc = self.filter_string_contains (rc, q, TITLE)
+        rc = self.filter_string_contains (rc, q, DESCRIPTION)
+
+        return rc
+
diff --git a/Apps/auf_sep/__init__.py b/Apps/auf_sep/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Apps/auf_sep/globals.py b/Apps/auf_sep/globals.py
deleted file mode 100644 (file)
index 724f51e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-
-#####
-# Meta fields
-TITLE           = 'title'       #X str
-ALT_TITLE       = 'alt_title'   #X str
-CREATOR         = 'creator'     #X str
-CONTRIBUTOR     = 'contributor' #X array (str)
-DESCRIPTION     = 'description' #  str
-ABSTRACT        = 'abstract'    #X str
-SUBJECT         = 'subject'     #X str
-PUBLISHER       = 'publisher'   #X str
-DATE_CREATION   = 'creation'    #  date
-DATE_ISSUED     = 'issued'      #X date
-DATE_MODIFIED   = 'modified'    #  date
-TYPE            = 'type'        #  str
-FORMAT          = 'format'      #  str
-IDENTIFIER      = 'identifier'  #X str (int?)
-ISBN            = 'isbn'        #X str
-URI             = 'uri'         #X str (url)
-SOURCE          = 'source'      #X str (url)
-LANGUAGE        = 'language'    #X str
-ORIG_LANG       = 'orig_lang'   #X str
-
diff --git a/Apps/auf_sep/harvest/__init__.py b/Apps/auf_sep/harvest/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Apps/auf_sep/harvest/harvest.py b/Apps/auf_sep/harvest/harvest.py
deleted file mode 100644 (file)
index 4aa65f3..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-import sys
-
-sys.path.append ("../")
-from conf import RESOURCES
-from sep.io import SEP
-
-
-if __name__ == "__main__":
-    sep = SEP ()
-
-    for name in RESOURCES.keys ():
-        print "Import:", name
-        options = RESOURCES[name]
-        module = 'harvesters.%s.%s' % (options['type'], options['acces'])
-        __import__ (module)
-        harvester = sys.modules[module]
-        nodes = harvester.harvest (options)
-        print "Ajout de", len(nodes), "references"
-        for node in nodes:
-            sep.add (node)
diff --git a/Apps/auf_sep/harvest/harvesters/__init__.py b/Apps/auf_sep/harvest/harvesters/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Apps/auf_sep/harvest/harvesters/oai/__init__.py b/Apps/auf_sep/harvest/harvesters/oai/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Apps/auf_sep/harvest/harvesters/oai/generic.py b/Apps/auf_sep/harvest/harvesters/oai/generic.py
deleted file mode 100644 (file)
index 51d735b..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-# -*- encoding: utf-8 -*-
-from lxml import etree
-import sys, urllib, httplib, re
-
-sys.path.append ("../../../")
-from globals import *
-from sep.utils import safe_append, print_structure
-
-
-
-def find_location (server, port, base_url):
-    rc = None
-    possible = ("perl/oai2", "cgi/oai2", "cgi-bin/oaiserver")
-    for test in possible:
-        url = base_url + test
-        handle = httplib.HTTPConnection (server, port)
-        handle.request ("GET", url + "?verb=Identify")
-        r = handle.getresponse ()
-        if r.status == 200:
-            rc = "http://" + server + url
-            break
-    return rc
-
-def load_xml (url):
-    ud = urllib.urlopen (url)
-    content = ud.read ().decode('iso-8859-1')
-    # Greenstone crap
-    content = content.replace ("xmlns=\"http://www.openarchives.com/OAI/2.0\"",
-            "xmlns=\"http://www.openarchives.org/OAI/2.0/\"")
-    ud.close ()
-    root = etree.XML (content.encode("utf-8"))
-    return root
-
-
-def cleanup (node):
-    node.tag = re.sub (r'^\{[a-zA-Z0-9_:.-\/]+\}', '', node.tag)
-    return node
-
-
-def harvest (options):
-    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/}"
-
-    uri = find_location (options['server'], 
-            options['port'], 
-            options['base_url'])
-
-    records = []
-    root = load_xml (uri + "?verb=ListRecords&metadataPrefix=oai_dc")
-    #print_structure (root)
-    records.extend (root.findall (".//%srecord" % oai2ns))
-    token = root.find (".//%sresumptionToken" % oai2ns)
-
-    while token is not None:
-        root = load_xml (uri + "?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)
-        for c in dcnode.getchildren ():
-            if c.text:
-                c.text = c.text.strip ()
-            else:
-                c.text = ""
-
-            if c.tag == "%stitle" % metans:
-                meta[TITLE] = c.text
-
-            elif c.tag == "%screator" % metans:
-                meta[CREATOR] = c.text
-
-            elif c.tag == "%scontributor" % metans:
-                safe_append (meta, CONTRIBUTOR, c.text)
-
-            elif c.tag == "%ssubject" % metans:
-                safe_append (meta, SUBJECT, c.text)
-
-            elif c.tag == "%sdescription" % metans:
-                meta[DESCRIPTION] = c.text
-
-            elif c.tag == "%spublisher" % metans:
-                if len (c.text) > 0:
-                    meta[PUBLISHER] = c.text
-
-            elif c.tag == "%sdate" % metans:
-                meta[DATE_CREATION] = c.text
-
-            elif c.tag == "%stype" % metans:
-                safe_append (meta, TYPE, c.text)
-
-            elif c.tag == "%sidentifier" % metans:
-                meta[IDENTIFIER] = c.text
-                meta[URI] = c.text
-
-            elif c.tag == '%sformat' % metans:
-                safe_append (meta, FORMAT, c.text)
-
-            elif c.tag == '%srelation' % metans:
-                meta[SOURCE] = c.text
-
-        nodes.append (meta)
-
-    return nodes
diff --git a/Apps/auf_sep/harvest/harvesters/pmb/__init__.py b/Apps/auf_sep/harvest/harvesters/pmb/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Apps/auf_sep/harvest/harvesters/pmb/export.py b/Apps/auf_sep/harvest/harvesters/pmb/export.py
deleted file mode 100644 (file)
index f91fbe1..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-# -*- encoding: utf-8 -*-
-from pmbclient import PmbClient
-from lxml import etree
-import sys, re
-
-sys.path.append ("../../../")
-from globals import *
-from sep.utils import safe_append, print_structure, find_text
-
-
-def read_person (node):
-    rc = u"%s %s" % (find_text(node, "prenom"), 
-            find_text(node, "nom"))
-    dates = find_text(node, "dates")
-    if len (dates) > 0:
-        rc += " (%s)" % dates
-    return rc
-
-def read_publisher (node):
-    return u"%s, %s" % (find_text(node, "nom"), 
-            find_text(node, "ville"))
-    
-
-
-def harvest (options):
-    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[IDENTIFIER] = c.text
-
-            elif c.tag == 'zoneTitre':
-                for t in c.getchildren ():
-                    if t.tag == 'titrePrincipal':
-                        meta[TITLE] = t.text
-                    else:
-                        safe_append (meta, ALT_TITLE, t.text)
-
-            elif c.tag == 'zoneAuteurPrincipal':
-                meta[CREATOR] = read_person (c)
-
-            elif c.tag == 'zoneAuteursAutres':
-                safe_append (meta, CONTRIBUTOR, read_person (c))
-
-            elif c.tag == 'zoneNotes':
-                meta[ABSTRACT] = find_text (c, "noteResume")
-
-            elif c.tag == 'zoneEditeur':
-                safe_append (meta, PUBLISHER, read_publisher (c))
-                meta[DATE_ISSUED] = find_text (c, "annee")
-
-            elif c.tag == 'prixISBN':
-                meta[ISBN] = find_text (c, "ISBN")
-
-            elif c.tag == 'zoneLiens':
-                meta[SOURCE] = find_text (c, "lien")
-
-            elif c.tag == 'zoneLangue':
-                for t in c.getchildren ():
-                    if t.tag == 'langueDocument':
-                        safe_append (meta, LANGUAGE, t.text)
-                    elif t.tag == 'langueOriginale':
-                        safe_append (meta, ORIG_LANG, t.text)
-
-            elif c.tag == 'zoneCategories':
-                safe_append (meta, SUBJECT, find_text (c, "categorie"))
-
-        meta[URI] = "http://%s%scatalog.php?id=%s" % \
-                (options['host'], options['base_url'], meta[IDENTIFIER])
-        nodes.append (meta)
-    return nodes
-
-
diff --git a/Apps/auf_sep/harvest/harvesters/pmb/pmbclient.py b/Apps/auf_sep/harvest/harvesters/pmb/pmbclient.py
deleted file mode 100644 (file)
index a301da8..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-import httplib, urllib, Cookie, time
-from exceptions import Exception
-from lxml import etree
-
-
-class PmbClient:
-    handle = None
-    cookies = ""
-
-    def __init__ (self):
-        pass
-
-    def __del__ (self):
-        if self.handle:
-            self.handle.close ()
-
-    def connect (self, server, port=80):
-        if self.handle:
-            self.handle.close ()
-
-        self.handle = httplib.HTTPConnection (server, port)
-
-    def login (self, params, script):
-        params = urllib.urlencode(params)
-        headers = {"Content-type": "application/x-www-form-urlencoded",
-                "Accept": "text/plain"}
-
-        self.handle.request ("POST", script, params, headers)
-        r = self.handle.getresponse ()
-
-        if r.status != 200:
-            raise Exception ("Login failed: %s %s" % (r.status, r.reason))
-        r.read ()
-
-        bc = Cookie.BaseCookie ()
-        bc.load (r.getheader ("set-cookie"))
-        tmp = bc.output (header="Cookie:").replace (",", "").split("\n")
-        cookies = []
-        for line in tmp:
-            cookie = line.replace ("Cookie: ", "").strip ()
-            cookies.append (cookie)
-        self.cookies = "; ".join (cookies)
-
-    def get_response (self):
-        r = None
-        while r is None:
-            try:
-                r = self.handle.getresponse ()
-            except:
-                r = None
-            time.sleep (1)
-        return r
-
-    def find_next_location (self, buffer):
-        rc = ""
-
-        root = etree.HTML (buffer)
-        tmp = root.findall (".//script")
-        script = tmp[len(tmp)-1]
-
-        buffer = script.text
-        match = "document.location='"
-        i = buffer.rfind (match)
-        if i >= 0:
-            i += len (match)
-            tmp = buffer[i:]
-            j = tmp.find ("'\"")
-            if j >= 0:
-                rc = tmp[0:j]
-        return rc
-
-    def make_url (self, old, script):
-        tmp = old.split ("/")
-        tmp[len(tmp)-1] = script
-        nextscript = "/".join(tmp)
-        return nextscript
-
-    def read_form (self, buffer):
-        script = ""
-        params = {}
-
-        root = etree.HTML(buffer)
-        form = root.find (".//form")
-        script = form.attrib['action']
-        
-        inputs = form.findall (".//input")
-        for input in inputs:
-            try:
-                params[input.attrib['name']] = input.attrib['value']
-            except:
-                pass
-
-        return (params, script)
-
-    def export (self, params, script):
-        params = urllib.urlencode(params)
-        headers = {"Content-type": "application/x-www-form-urlencoded",
-                "Accept": "text/plain",
-                "Cookie": self.cookies}
-
-        self.handle.request ("POST", script, params, headers)
-        r = self.get_response ()
-        next = self.find_next_location (r.read ())
-        nextscript = self.make_url (script, next)
-
-        self.handle.request ("GET", nextscript, headers={"Cookie": self.cookies})
-        r = self.get_response ()
-        next = self.find_next_location (r.read ())
-        nextscript = self.make_url (script, next)
-
-        self.handle.request ("GET", nextscript, headers={"Cookie": self.cookies})
-        r = self.get_response ()
-        (params, next) = self.read_form (r.read ())
-
-        nextscript = self.make_url (script, next)
-        params = urllib.urlencode(params)
-        self.handle.request ("POST", nextscript, params, headers)
-        r = self.get_response ()
-        content = r.read ()
-
-        # Saloperie de PMB force le retour en iso crap
-        return content.decode('iso-8859-1')
diff --git a/Apps/auf_sep/sep/__init__.py b/Apps/auf_sep/sep/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Apps/auf_sep/sep/io.py b/Apps/auf_sep/sep/io.py
deleted file mode 100644 (file)
index 7890109..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- encoding: utf-8 -*-
-from exceptions import Exception
-import sys
-sys.path.append ("../")
-import conf
-from globals import *
-
-
-class SEP:
-    backend = None
-    required_methods = ['open', 'close', 'add', 'delete', 'search', 'update']
-
-    def __init__ (self):
-        # Charge le backend de stockage
-        module = 'storage.%s' % conf.BACKEND['plugin']
-        __import__ (module)
-        backend_module = sys.modules[module]
-        self.backend = backend_module.Backend (conf.BACKEND['options'])
-
-        # Vérifier qu'il répond à l'API
-        available = dir (self.backend)
-        for method in self.required_methods:
-            if not method in available:
-                msg = "Backend incomplet\nManque %s\nexiste %s" \
-                        % (method, available)
-                raise Exception(msg)
-
-        # Charge
-        self.backend.open ()
-
-
-    def __del__ (self):
-        self.backend.close ()
-
-
-#############
-# API public
-    def search (self, id = None, q = None):
-        return self.backend.search (id, q)
-
-    def get (self, id):
-        return self.backend.get (id)
-
-    def add (self, metadata):
-        exists = self.search (q = {URI: metadata[URI]})
-        if len (exists) > 0:
-            id = exists[0]
-            return self.update (id, metadata)
-        else:
-            return self.backend.add (metadata)
-
-    def delete (self, id):
-        self.backend.delete (id)
-
-    # Pas vraiment besoin d'etre public, mais bon
-    def update (self, id, metadata):
-        if len (self.search (id = id)) > 0:
-            self.backend.update (id, metadata)
-        else:
-            raise Exception ("Objet inexistant")
-
-
-
-if __name__ == '__main__':
-    s = SEP()
-    print s.search (id=0)
-
diff --git a/Apps/auf_sep/sep/utils.py b/Apps/auf_sep/sep/utils.py
deleted file mode 100644 (file)
index 3413433..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-
-def safe_append (dict, key, value):
-    try:
-        list = dict[key]
-    except:
-        dict[key] = []
-    dict[key].append (value)
-
-def print_structure(element, tab=""):
-    if element.text:
-        element.text = element.text.strip ()
-    if element.tail:
-        element.tail = element.tail.strip ()
-    line = u"%s<%s>: %s, %s, %s" % (tab, element.tag, element.text, 
-        element.tail, element.attrib)
-    print line.encode ("utf-8")
-    for x in element.getchildren():
-        print_structure(x, tab+" ")
-
-def find_text (node, tag):
-    rc = ""
-    n = node.find (".//%s" % tag)
-    if n is not None:
-        rc = n.text
-    return rc
diff --git a/Apps/auf_sep/setup.py b/Apps/auf_sep/setup.py
deleted file mode 100644 (file)
index a78276e..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- encoding: utf-8 -*-
-from setuptools import setup, find_packages
-import sys, os
-
-version = '0.1'
-
-setup(
-        name='auf_sep',
-        version=version,
-        description="Savoirs en partage, module de stockage/indexation",
-        classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
-        keywords='',
-        author='Cyril Robert',
-        author_email='cyril.robert@auf.org',
-        url='',
-        license='GPL',
-        packages=['auf_sep',],
-        package_dir={'auf_sep': '.',},
-        include_package_data=True,
-        zip_safe=False,
-        install_requires=[],
-        )
diff --git a/Apps/auf_sep/storage/__init__.py b/Apps/auf_sep/storage/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Apps/auf_sep/storage/json.py b/Apps/auf_sep/storage/json.py
deleted file mode 100644 (file)
index f9be889..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-# -*- encoding: utf-8 -*-
-import os
-import simplejson as json
-import sys
-sys.path.append ("../")
-from globals import *
-
-####
-# Attention: pas efficace
-#
-# Trucs a faire pour l'optimiser:
-#  - Ajouter un index par url, trié
-#  - Ajouter un index (full inverted) pour les champs texte
-
-INDEX_NAME = 'index.json'
-DATA_FMT = '%s.json'
-
-
-class File:
-    def __init__ (self, path, mode):
-        self.fp = open (path, mode)
-
-    def __del__ (self):
-        if self.fp:
-            self.fp.close ()
-
-    def read (self):
-        return json.load (self.fp)
-
-    def write (self, object):
-        json.dump (object, self.fp)
-
-
-class Backend:
-    options = {}
-    data = {}
-    needs_write = False
-    remove = []
-    last_index = 0
-
-    def __init__ (self, options):
-        self.options = options
-        # juste pour lancer une exception si jamais on a pas le path
-        p = self.options['path']
-
-    def open (self):
-        p = self.options['path']
-        ip = os.path.join (p, INDEX_NAME)
-
-        if not os.path.exists (p):
-            os.makedirs (p)
-
-        elif os.path.exists (ip):
-            index = File (ip, 'r').read ()
-            for id in index:
-                fp = os.path.join (p, DATA_FMT % id)
-                meta = File (fp, 'r').read ()
-                meta['needs_write'] = False
-                self.data[id] = meta
-                if id > self.last_index:
-                    self.last_index = id
-
-    def close (self):
-        if self.needs_write:
-            p = self.options['path']
-            
-            # Enleve les vieux trucs
-            if len (self.remove) > 0:
-                for id in self.remove:
-                    fp = os.path.join (p, DATA_FMT % id)
-                    os.remove (fp)
-
-            # Ecrit l'index
-            File(os.path.join(p, INDEX_NAME), 'w').write(self.data.keys ())
-
-            # Ecrit les données
-            for id in self.data.keys ():
-                record = self.data[id]
-                if record['needs_write']:
-                    del (record['needs_write'])
-                    fp = os.path.join (p, DATA_FMT % id)
-                    File (fp, 'w').write (record)
-
-    def add (self, metadata):
-        self.last_index += 1
-        metadata['needs_write'] = True
-        self.data[self.last_index] = metadata
-        self.needs_write = True
-        return self.last_index
-
-    def delete (self, id):
-        self.needs_write = True
-        del (self.data[id])
-        self.remove.append (id)
-
-    def update (self, id, metadata):
-        metadata['needs_write'] = True
-        self.needs_write = True
-        for k in metadata.keys():
-            self.data[id][k] = metadata[k]
-
-    def get (self, id):
-        rc = None
-        if id in self.data.keys ():
-            rc = self.data[id]
-        return rc
-
-    def search (self, id, q):
-        rc = []
-
-        if id is not None and id in self.data.keys():
-            rc.append (id)
-
-        elif q is not None:
-            rc = self.data.keys ()
-
-            # Incomplet
-            url = q.get (URI)
-            if url:
-                rr = []
-                for k in rc:
-                    if self.data[k][URI] == url:
-                        rr.append(k)
-                rc = rr
-
-        return rc
-