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 ) )
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])