From 57c5bcefb1eac66d1db27db28a0c6d3b3b52a407 Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Wed, 27 Mar 2013 09:19:33 +0100 Subject: [PATCH] we embed the (currently raw) results from manifold API in a ManifoldResult dict this needs to be analyzed after each API call --- auth/manifoldbackend.py | 19 ++++-- manifold/js/manifold.js | 11 +++ manifold/manifoldapi.py | 28 ++++---- manifold/manifoldproxy.py | 12 +--- manifold/manifoldresult.py | 30 +++++++++ manifold/metadata.py | 16 +++-- plugins/hazelnut/DataTables.php | 116 ++++++++++++++++++++++++++++++++ plugins/hazelnut/demo_page.css1 | 93 +++++++++++++++++++++++++ 8 files changed, 294 insertions(+), 31 deletions(-) create mode 100644 manifold/manifoldresult.py create mode 100644 plugins/hazelnut/DataTables.php create mode 100644 plugins/hazelnut/demo_page.css1 diff --git a/auth/manifoldbackend.py b/auth/manifoldbackend.py index 96697b3f..2b1bde59 100644 --- a/auth/manifoldbackend.py +++ b/auth/manifoldbackend.py @@ -2,7 +2,7 @@ import time from django.contrib.auth.models import User -from manifold.manifoldapi import ManifoldAPI +from manifold.manifoldapi import ManifoldAPI, ManifoldResult # Name my backend 'ManifoldBackend' class ManifoldBackend: @@ -21,10 +21,13 @@ class ManifoldBackend: auth = {'AuthMethod': 'password', 'Username': username, 'AuthString': password} api = ManifoldAPI(auth) # Authenticate user and get session key - session = api.GetSession() - if not session : - return None + session_result = api.GetSession() + session = session_result.ok_value() + if not session: + print "GetSession failed",session_result.error() + return + print 'DEALING with session',session #self.session = session # Change GetSession() at some point to return expires as well expires = time.time() + (24 * 60 * 60) @@ -34,8 +37,12 @@ class ManifoldBackend: self.api = api # Get account details - person = api.GetPersons(auth)[0] - self.person = person + persons_result = api.GetPersons(auth) + persons = persons_result.ok_value() + if not persons: + print "GetPersons failed",persons_result.error() + return + person = persons[0] request.session['manifold'] = {'auth': api.auth, 'person': person, 'expires': expires} except: diff --git a/manifold/js/manifold.js b/manifold/js/manifold.js index 6b2d7288..449f735f 100644 --- a/manifold/js/manifold.js +++ b/manifold/js/manifold.js @@ -67,6 +67,17 @@ var manifold = { }, asynchroneous_success : function (data, query, id) { + if (manifold.asynchroneous_debug) console.log ("received manifold result with code " + data.code); + // xxx should have a nicer declaration of that enum in sync with the python code somehow + if (data.code == 1) { + alert("Your session has expired, please log in again"); + window.location="/logout/"; + return; + } elif (data.code != 0) { + alert("Error received from manifold backend at " + MANIFOLD_URL + " [" + data.output + "]"); + return; + } + data=data.value; if (data) { if (!!id) { /* Directly inform the requestor */ diff --git a/manifold/manifoldapi.py b/manifold/manifoldapi.py index 6a0e1d23..6c942b4f 100644 --- a/manifold/manifoldapi.py +++ b/manifold/manifoldapi.py @@ -3,13 +3,10 @@ import xmlrpclib from myslice.config import Config -debug=True +from manifoldresult import ManifoldResult, ManifoldCode -class SessionExpired (Exception): - def __init__ (self,message): - self.message=message - def __repr__ (self): - return ""%self.message +debug=False +debug=True class ManifoldAPI: @@ -25,36 +22,43 @@ class ManifoldAPI: self.url = config.manifold_url self.server = xmlrpclib.Server(self.url, verbose=False, allow_none=True) + def __repr__ (self): return "ManifoldAPI[%s]"%self.url + def __getattr__(self, methodName): def func(*args, **kwds): + if (debug): + print "entering ManifoldAPI.%s"%methodName, + print "args",args, + print "kwds",kwds try: result=getattr(self.server, methodName)(self.auth, *args, **kwds) if debug: print '===> backend call',methodName, self.auth, self.url,'->', - if not result: print "no/empty result" + if not result: print "[no/empty result]" elif isinstance (result,str): print "result is '%s'"%result elif isinstance (result,list): print "result is a %d-elts list"%len(result) - else: print "dont know how to display result" - return result + else: print "[dont know how to display result]" + return ManifoldResult (code=ManifoldCode.SUCCESS, value=result) except xmlrpclib.Fault, error: ### xxx this is very rough for now # until we have some agreement about how the API calls should return error conditions # in some less unpolite way than this anoanymous exception, we assume it's a problem with the session # that needs to be refreshed + if debug: print "Session Expired" if error.faultCode == 8002: reason="most likely your session has expired" reason += " (the manifold API has no unambiguous error reporting mechanism yet)" - raise SessionExpired(reason) + return ManifoldResult (code=ManifoldCode.SESSION_EXPIRED, output=reason) except Exception,error: print "ManifoldAPI: unexpected exception",error - raise + return ManifoldResult (code=ManifoldResult.UNKNOWN_ERROR, output="%s"%error) return func def send_manifold_query (self, manifold_query): (action,subject)= (manifold_query.action,manifold_query.subject) if action=='get': # use self.Get rather than self.server.Get so we catch exceptions as per __getattr__ - return self.Get(self.auth, subject, manifold_query.filters, {}, manifold_query.fields) + return self.Get(subject, manifold_query.filters, {}, manifold_query.fields) # xxx... else: print "WARNING: ManifoldAPI.send_manifold_query: only 'get' implemented for now" diff --git a/manifold/manifoldproxy.py b/manifold/manifoldproxy.py index c444a379..d0636c88 100644 --- a/manifold/manifoldproxy.py +++ b/manifold/manifoldproxy.py @@ -4,7 +4,7 @@ import json from django.http import HttpResponse, HttpResponseForbidden from manifold.manifoldquery import ManifoldQuery -from manifold.manifoldapi import ManifoldAPI, SessionExpired +from manifold.manifoldapi import ManifoldAPI debug=False debug=True @@ -63,14 +63,8 @@ with the query passed using POST""" # actually forward manifold_api= ManifoldAPI(auth=manifold_api_session_auth) if debug: print 'manifoldproxy.proxy: sending to backend', manifold_query - # xxx we should embed the values inside a geni-like wrapper - try: - answer=manifold_api.send_manifold_query (manifold_query) - if debug: - try: print "received answer from backend with %d rows"%len(answer) - except: print "received answer from backend - can't say len" - except SessionExpired,error: - answer=[ error.message ] + answer=manifold_api.send_manifold_query (manifold_query) + if debug: print 'manifoldproxy.proxy: received from backend with code', answer['code'] json_answer=json.dumps(answer) if (debug): with (file(offline_filename,"w")) as f: diff --git a/manifold/manifoldresult.py b/manifold/manifoldresult.py new file mode 100644 index 00000000..c99d861e --- /dev/null +++ b/manifold/manifoldresult.py @@ -0,0 +1,30 @@ +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + return type('Enum', (), enums) + +ManifoldCode = enum ( + SUCCESS=0, + SESSION_EXPIRED=1, + OTHERS=2, +) + +# being a dict this can be used with json.dumps +class ManifoldResult (dict): + def __init__ (self, code=ManifoldCode.SUCCESS, value=None, output=""): + self['code']=code + self['value']=value + self['output']=output + + def from_json (self, json_string): + d=json.dumps(json_string) + for k in ['code','value','output']: + self[k]=d[k] + + # this returns None if there's a problem, the value otherwise + def ok_value (self): + if self['code']==ManifoldCode.SUCCESS: + return self['value'] + + def error (self): + return "code=%s -- %s"%(self['code'],self['output']) + diff --git a/manifold/metadata.py b/manifold/metadata.py index e8578c43..c092dfdc 100644 --- a/manifold/metadata.py +++ b/manifold/metadata.py @@ -1,7 +1,11 @@ import json +from manifold.manifoldresult import ManifoldResult from manifold.manifoldapi import ManifoldAPI +debug=False +debug=True + # turn this on if you want to work offline work_offline=False #work_offline=True @@ -28,11 +32,15 @@ class MetaData: 'column.resource_type', 'column.value_type', 'column.allowed_values', 'column.platforms.platform', 'column.platforms.platform_url'] - results = manifold_api.Get('metadata:table', [], [], fields) - self.hash_by_subject = dict ( [ (result['table'], result) for result in results ] ) + rows_result = manifold_api.Get('metadata:table', [], [], fields) + rows = rows_result.ok_value() + if not rows: + print "Failed to retrieve metadata",rows_result.error() + self.hash_by_subject = dict ( [ (row['table'], row) for row in rows ] ) # save for next time we use offline mode - with file(offline_filename,'w') as f: - f.write(json.dumps(self.hash_by_subject)) + if debug: + with file(offline_filename,'w') as f: + f.write(json.dumps(self.hash_by_subject)) def to_json(self): return json.dumps(self.hash_by_subject) diff --git a/plugins/hazelnut/DataTables.php b/plugins/hazelnut/DataTables.php new file mode 100644 index 00000000..67a8985c --- /dev/null +++ b/plugins/hazelnut/DataTables.php @@ -0,0 +1,116 @@ +uuid; + + $results = Array(); + $async = 1; + + + /* XXX required field in options : query_uuid */ + + + $query = Plugins::get_query_by_uuid($this->params['query_uuid']); + $is_unique = Plugins::get_key_filter($query); + $method_keys = Plugins::get_default_fields($query->method, $is_unique); + //$method_keys = Plugins::query_get_default_keys($options['query_uuid']); + //$fields = Plugins::query_get_fields($options['query_uuid']); + //$all_headers = $_SESSION['metadata']['nodes']['columns']; + //$fields = $all_headers; + + $fields = Plugins::metadata_get_fields($query->method); + + /* + * @author: lbaron + * date: 2012-05-29 + * debug columns QueryEditor to DataTables + * + */ + // Problem: field names are differents between + // $_SESSION['metadata']['nodes']['columns'] + // JSON values + //$fields[]="arch"; // architecture in Session metadata + //$fields[]="astype"; // as_type in Session metadata + $fields['platform'] = Array('column' => 'platform'); + $fields['platform_longname'] = Array('column' => 'platform_longname'); + + //---------------------------------- + + $out = Array(); + $out[] = ""; + $out[] = ""; + + foreach ($method_keys as $f) { + $out[] = ""; + } + + /* We put defaults fields (should be keys) at the beginning, and don't repeat them afterwards */ + foreach ($fields as $key=>$f) { + if((array_search($f['column'], $method_keys)) === false) + $out[] = ""; + } + + if (array_key_exists('checkboxes', $this->params) && ($this->params['checkboxes'])) { + $out[] = ""; + } + $out[] = ""; + $out[] = ""; + + /* This might be done asynchronously */ + if (!$async) { + $query = Plugins::get_query_by_uuid($this->params['query_uuid']); + foreach ($results as $r) { + $out[] = ""; + foreach ($fields as $f) { + $out[] = ""; + } + if (array_key_exists('checkboxes', $this->params) && ($this->params['checkboxes'])) { + $out[] = ""; + } + $out[] = ""; + } + } + + + if ($async) { + /* setup datatables */ + /* TODO: + * - fixed row for key columns + * - uniform row height + * - how to make some columns disappear + * - how to udpate some columns based on keys + */ + } + $out[] = ""; + $out[] = "
$f".$f['column']."+/-
"; + $out[] = Plugins::render_element($query, $r[$f], $f); // XXX was query->method + $out[] = "[X]
"; + return implode($out); + } +} + +Plugins::register_plugin( + 'DataTables', /* plugin name */ + 'DataTables', /* class name */ + Array( + 'method' => '*', + 'fields' => Array() + ) + /* XXX dependencies */ +); diff --git a/plugins/hazelnut/demo_page.css1 b/plugins/hazelnut/demo_page.css1 new file mode 100644 index 00000000..bee7b0d9 --- /dev/null +++ b/plugins/hazelnut/demo_page.css1 @@ -0,0 +1,93 @@ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * General page setup + */ +#dt_example { + font: 80%/1.45em "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + margin: 0; + padding: 0; + color: #333; + background-color: #fff; +} + + +#dt_example #container { + width: 800px; + margin: 30px auto; + padding: 0; +} + + +#dt_example #footer { + margin: 50px auto 0 auto; + padding: 0; +} + +#dt_example #demo { + margin: 30px auto 0 auto; +} + +#dt_example .demo_jui { + margin: 30px auto 0 auto; +} + +#dt_example .big { + font-size: 1.3em; + font-weight: bold; + line-height: 1.6em; + color: #4E6CA3; +} + +#dt_example .spacer { + height: 20px; + clear: both; +} + +#dt_example .clear { + clear: both; +} + +#dt_example pre { + padding: 15px; + background-color: #F5F5F5; + border: 1px solid #CCCCCC; +} + +#dt_example h1 { + margin-top: 2em; + font-size: 1.3em; + font-weight: normal; + line-height: 1.6em; + color: #4E6CA3; + border-bottom: 1px solid #B0BED9; + clear: both; +} + +#dt_example h2 { + font-size: 1.2em; + font-weight: normal; + line-height: 1.6em; + color: #4E6CA3; + clear: both; +} + +#dt_example a { + color: #0063DC; + text-decoration: none; +} + +#dt_example a:hover { + text-decoration: underline; +} + +#dt_example ul { + color: #4E6CA3; +} + +.css_right { + float: right; +} + +.css_left { + float: left; +} \ No newline at end of file -- 2.43.0