unlinking Metaweb objects

[ permalink ] [ download ]
import sys, getopt    # Command-line parsing
import metaweb        # login, read, and write utilities

# A cache that maps type ids to arrays of property information
typecache = {}

# Primitive types other than /type/text and /type/key, which are
# handled specially
primitives = set(["/type/boolean", "/type/datetime", "/type/float",
                  "/type/id", "/type/int", "/type/rawstring", "/type/uri"])

# Return information about the properties of type t.
# Use a cache to avoid repeated queries
def getPropertiesOfType(t):
    if (t not in typecache):
        props = metaweb.read({'type':'/type/type',
                              'id':t,
                              'properties':[{'id':None,
                                             'expected_type':None}]})
        typecache[t] = props['properties']

    return typecache[t]

#
# Return a MQL write query that will unlink the object with the specified id
#
def makeUnlinkQuery(id):
    # Find all types of the object
    types = metaweb.read({ 'id':id, 'type':[] })['type']

    # Make a list of all properties of all types of the object
    props = []
    for t in types:
        props.extend(getPropertiesOfType(t))

    # We start off with queries for the well-known properties of /type/object
    q = { 'id': id,
          'name': [{'value':None, 'lang':None, 'optional':True}],
          'type': [{'id':None, 'optional':True}],
          'key': [{'value':None, 'namespace':None, 'optional':True}]}

    # Add properties to this query to ask for the id or value of all
    # other properties of all types.  Note that the way we ask for the value
    # of a property depends on the expected type of the property
    for p in props:
        propid = p['id']
        proptype = p['expected_type']
        # We now need to add propid to the query.
        # The value of the property depends on its expected type.
        if proptype == '/type/text':
            q[propid] = [{ 'value':None, 'lang':None, 'optional':True }]
        elif proptype == '/type/key':
            q[propid] = [{ 'value':None, 'namespace':None, 'optional':True }]
        elif proptype in primitives:
            q[propid] = [{ 'value':None, 'optional':True }]
        else:
            q[propid] = [{ 'id': None, 'optional':True }]
            
    # Get the result of this query
    r = metaweb.read(q)

    # The query is structured in such a way that the result is almost ready
    # to be reused as a write query.  We loop through the properties of the
    # result, and for any that are non-empty, we copy them to a query object
    # adding "connect":"delete" to transform it into a MQL write query
    q = {}
    for p,v in r.iteritems():
        if p == 'id': # leave the id of the query alone
            q[p] = v
            continue  
        # if the property is not id, then the value is an array
        # if the array is empty, then skip this property; don't copy to query
        if len(v) == 0: continue
        # otherwise, iterate through the elements of the array
        # and add the connect:delete directive to each one
        for elt in v: elt['connect'] = 'delete'
        # and copy to the query
        q[p] = v


    # Return the MQL write query that we can use to unlink the object
    return q

    
def main():
    # Use the getopt module to parse the command line arguments
    try:
        opts, args = getopt.getopt(sys.argv[1:],
                                   "u:p:n:t:",
                               ["user=","password=","name=","type=","debug"])
    except getopt.error, msg:
        print msg
        sys.exit(0)
        
    # Now process each of the parsed arguments
    username, password, name, type, debug = (None, None, None, None, None)
    for o,a in opts:
        if (o in ('-u', '--user')): username = a
        elif (o in ('-p', '--password')): password = a
        elif (o in ('-n', '--name')): name = a
        elif (o in ('-t', '--type')): type = a
        elif (o == '--debug'): debug = True

    if username is None or password is None:
        sys.exit("username and password must be specified")

    # Use the username and password to login to metaweb
    credentials = metaweb.login(username, password)

    # Start building a query to find objects to be unlinked
    # We will only attempt to unlink objects that we created ourselves.
    # By default we will unlink all objects we've created
    query = [{'creator': '/user/' + username,
              'id': None }]

    # If the name argument was specified, then we only want to unlink
    # objects with the specified name
    if (name is not None):
        query[0]['name'] = name

    # If the type argument was specified then we only want to unlink
    # objects with the specified type
    if (type is not None):
        query[0]['type'] = type

    # Run the query to get the objects we're going to delete
    objects = metaweb.read(query)

    # If we didn't find any, quit now
    if len(objects) == 0:
        sys.exit("Nothing to unlink")

    # Now unlink each of the objects we found
    for o in objects:
        id = o['id']
        print "Unlinking: %s" % id
        # build the query
        q = makeUnlinkQuery(id)
        if (debug): print "Unlink query: " + repr(q)
        # run the query
        result = metaweb.write(q, credentials)
        if (debug): print "Unlink result: " + repr(result)
        

# If we're executing this file from the command-line, run the main() method
if __name__ == "__main__": main()
hits counter