Ejemplo n.º 1
0
def update_remote_package(table, pkg, cursor):
    """Insert or replace information on a package into "table".
    "pkg" is assumed to be a dictionary in the form given by each package
    listed in the given repository."""
    # Assume package ID and URI exist.  Not much to do if they don't.
    id = pkg['id']
    uri = pkg['uri']

    # Assemble complete version number.
    # If any components are missing, set them to '0'.
    v = ['0','0','0','0','release']
    for i,j in enumerate(('major','minor','release','build','type')):
        try: v[i] = pkg['version'][j]
        except: pass
    # If type is "release", that's not very interesting, so ignore it.
    v = v if v[-1] != 'release' else v[:-1]
    version = '.'.join(v)

    # Get title and description.
    # First search for most preferred language available.
    l = dict()
    for lang in options.get_locale():
        try:
            l = pkg['localizations'][lang]
            break
        except: pass
    # Then get try to get title and description in that language.
    title=None; description=None
    try:
        title = l['title']
        description = l['description']
    except: pass

    # Straightforward optional fields.
    opt_field = {'info':None, 'size':None, 'md5':None, 'modified-time':None,
        'rating':None, 'vendor':None, 'icon':None}
    for i in opt_field.iterkeys():
        try: opt_field[i] = pkg[i]
        except: pass

    # Optional author information fields.
    author = {'name':None, 'website':None, 'email':None}
    for i in author.iterkeys():
        try: author[i] = pkg['author'][i]
        except: pass

    # Optional array fields.  Database cannot hold arrays, so combine all
    # elements of each array with a separator character.
    opt_list = {'previewpics':None, 'licenses':None, 'source':None,
        'categories':None}
    for i in opt_list.iterkeys():
        try: opt_list[i] = SEPCHAR.join(pkg[i])
        except: pass

    # Insert extracted data into table.
    cursor.execute("""Insert Or Replace Into "%s" Values
        (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""" % table,
        ( id,
        uri,
        version,
        title,
        description,
        opt_field['info'],
        opt_field['size'],
        opt_field['md5'],
        opt_field['modified-time'],
        opt_field['rating'],
        author['name'],
        author['website'],
        author['email'],
        opt_field['vendor'],
        opt_field['icon'],
        opt_list['previewpics'],
        opt_list['licenses'],
        opt_list['source'],
        opt_list['categories'],
        None, None ) )
Ejemplo n.º 2
0
def update_local_file(path, db_conn):
    """Adds an entry to the local database based on the PND found at "path"."""
    apps = libpnd.pxml_get_by_path(path)
    if not apps:
        raise ValueError("%s doesn't seem to be a real PND file." % path)

    m = md5()
    #with open(path, 'rb') as p:
    #    for chunk in iter(lambda: p.read(128*m.block_size), ''):
    #        m.update(chunk)

    # Extract all the useful information from the PND and add it to the table.
    # NOTE: libpnd doesn't yet have functions to look at the package element of
    # a PND.  Instead, extract the PXML and parse that element manually.
    pxml_buffer = ctypes.create_string_buffer(libpnd.PXML_MAXLEN)
    f = libpnd.libc.fopen(path, 'r')
    if not libpnd.pnd_seek_pxml(f):
        raise PNDError('PND file has no starting PXML tag.')
    if not libpnd.pnd_accrue_pxml(f, pxml_buffer, libpnd.PXML_MAXLEN):
        raise PNDError('PND file has no ending PXML tag.')

    try:
        # Strip extra trailing characters from the icon.  Remove them!
        end_tag = pxml_buffer.value.rindex('>')
        pxml = etree.XML(pxml_buffer.value[:end_tag+1])
        # Search for package element.
        pkg = pxml.find(xml_child('package'))
    except: pass

    # May need to fall back on first app element, assuming it's representative
    # of the package as a whole.
    app = apps[0]

    try:
        pkgid = pkg.attrib['id']
    except:
        pkgid = libpnd.pxml_get_unique_id(app)

    try:
        v = pkg.find(xml_child('version'))
        # TODO: Add support for 'type' attribute.
        # NOTE: Using attrib instead of get will be fragile on non standards-
        # compliant PNDs.
        version = '.'.join( (
            v.attrib['major'],
            v.attrib['minor'],
            v.attrib['release'],
            v.attrib['build'], ) )
    except:
        version = '.'.join( (
            str(libpnd.pxml_get_version_major(app)),
            str(libpnd.pxml_get_version_minor(app)),
            str(libpnd.pxml_get_version_release(app)),
            str(libpnd.pxml_get_version_build(app)), ) )

    try:
        author = pkg.find(xml_child('author'))
        author_name = author.get('name')
        author_website = author.get('website')
        author_email = author.get('email')
    except:
        author_name = libpnd.pxml_get_author_name(app)
        author_website = libpnd.pxml_get_author_website(app)
        author_email = None # NOTE: libpnd has no pxml_get_author_email?

    try:
        # Get title and description in the most preferred language available.
        titles = {}
        for t in pkg.find(xml_child('titles')):
            titles[t.attrib['lang']] = t.text
        for l in options.get_locale():
            try:
                title = titles[l]
                break
            except KeyError: pass
        title # Trigger NameError if it's not yet set.
    except:
        title = libpnd.pxml_get_app_name(app, options.get_locale()[0])

    try:
        descs = {}
        for d in pkg.find(xml_child('descriptions')):
            descs[d.attrib['lang']] = d.text
        for l in options.get_locale():
            try:
                description = descs[l]
                break
            except KeyError: pass
        description # Trigger NameError if it's not yet set.
    except:
        description = libpnd.pxml_get_description(app, options.get_locale()[0])

    try:
        icon = pkg.find(xml_child('icon')).get('src')
    except:
        icon = libpnd.pxml_get_icon(app)

    # Find out how many apps are in the PXML, so we can iterate over them.
    n_apps = 0
    for i in apps:
        if i is None: break
        n_apps += 1

    # Create the full list of contained applications.
    applications = SEPCHAR.join([ libpnd.pxml_get_unique_id( apps[i] )
        for i in xrange(n_apps) ])

    # Get all previewpics.  libpnd only supports two per application.
    previewpics = []
    for i in xrange(n_apps):
        p = libpnd.pxml_get_previewpic1( apps[i] )
        if p is not None: previewpics.append(p)
        p = libpnd.pxml_get_previewpic2( apps[i] )
        if p is not None: previewpics.append(p)
    if previewpics:
        previewpics = SEPCHAR.join(previewpics)
    else: previewpics = None

    # TODO: Get licenses and source urls once libpnd has that functionality.

    # Combine all categories in all apps.  libpnd supports two categories, each
    # with two subcategories in each app.  No effort is made to uniquify the
    # completed list.
    categories = []
    for i in xrange(n_apps):
        c = libpnd.pxml_get_main_category( apps[i] )
        if c is not None: categories.append(c)
        c = libpnd.pxml_get_subcategory1( apps[i] )
        if c is not None: categories.append(c)
        c = libpnd.pxml_get_subcategory2( apps[i] )
        if c is not None: categories.append(c)
        c = libpnd.pxml_get_altcategory( apps[i] )
        if c is not None: categories.append(c)
        c = libpnd.pxml_get_altsubcategory1( apps[i] )
        if c is not None: categories.append(c)
        c = libpnd.pxml_get_altsubcategory2( apps[i] )
        if c is not None: categories.append(c)
    if categories:
        categories = SEPCHAR.join(categories)
    else: categories = None

    # Output from libpnd gives encoded bytestrings, not Unicode strings.
    db_conn.text_factory = str
    db_conn.execute("""Insert Or Replace Into "%s" Values
        (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""" % LOCAL_TABLE,
        ( pkgid,
        path,
        version,
        title,
        description,
        None, # Likely no use for "info" on installed packages.
        None, # TODO: size field
        m.hexdigest(), # TODO: Just None?
        None, # TODO: modified_time field?
        None, # No use for "rating" either.
        author_name,
        author_website,
        author_email,
        None, # No use for "vendor" either.
        icon,
        previewpics,
        None, # TODO: Licenses once libpnd can pull them.
        None, # TODO: Sources once libpnd can pull them.
        categories,
        applications,
        None ) )

    # Clean up the pxml handle.
    for i in xrange(n_apps):
        libpnd.pxml_delete(apps[i])