Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius/store.py
diff options
context:
space:
mode:
Diffstat (limited to 'tutorius/store.py')
-rw-r--r--tutorius/store.py353
1 files changed, 312 insertions, 41 deletions
diff --git a/tutorius/store.py b/tutorius/store.py
index 480c81b..9c57ce9 100644
--- a/tutorius/store.py
+++ b/tutorius/store.py
@@ -15,6 +15,9 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import urllib
+import urllib2
+from xml.dom import minidom
+from apilib.restful_lib import Connection
class StoreProxy(object):
"""
@@ -23,78 +26,215 @@ class StoreProxy(object):
shop to implement all the requests that could be made to the Store.
"""
+ def __init__(self):
+
+ # Base Urls for the api
+ self.base_url = "http://tutorius.dev/en-US/tutorius"
+ self.remora_api = "api/1.4"
+ self.tutorius_api = "TutoriusApi"
+ self.bandwagon_api = "api/1.4/sharing"
+
+ self.api_auth_key = ""
+
+ # Prepares the connection with the api
+ self.conn = Connection(self.base_url)
+
+ # Setup the helper
+ self.helper = StoreProxyHelper()
+
def get_categories(self):
"""
Returns all the categories registered in the store. Categories are used to
classify tutorials according to a theme. (e.g. Mathematics, History, etc...)
-
+
@return The list of category names stored on the server.
"""
- raise NotImplementedError("get_categories() not implemented")
+ categories = {}
+
+ request_url = "/%s/categories" % (self.tutorius_api)
+
+ response = self.conn.request_get(request_url)
+
+ if self.helper.iserror(response):
+ return categories
+
+ xml_response = minidom.parseString(response['body'])
+
+ xml_categories = xml_response.getElementsByTagName('category')
+
+ categories = list()
+
+ # Loop through the categories and create the list to be returned
+ for xml_category in xml_categories:
+ category = {}
+
+ category['id'] = xml_category.getElementsByTagName('id')[0].firstChild.nodeValue
+ category['name'] = xml_category.getElementsByTagName('name')[0].firstChild.nodeValue
- def get_tutorials(self, keywords=None, category=None, startIndex=0, numResults=10, sortBy='name'):
+ categories.append(category)
+
+ return categories
+
+
+ def get_tutorials(self, category='all', page=1, numResults=10, sortBy='name'):
"""
Returns the list of tutorials that correspond to the given search criteria.
-
+
@param keywords The list of keywords that should be matched inside the tutorial title
or description. If None, the search will not filter the results
according to the keywords.
@param category The category in which to restrict the search.
- @param startIndex The index in the result set from which to return results. This is
+ @param page The page in the result set from which to return results. This is
used to allow applications to fetch results one set at a time.
- @param numResults The max number of results that can be returned
+ @param numResults The max number of results that can be returned in a page
@param sortBy The field on which to sort the results
@return A list of tutorial meta-data that corresponds to the query
"""
- raise NotImplementedError("get_tutorials() not implemented")
+
+ request_url = "/%s/tutorials/%s/%d/%d/%s" % (self.tutorius_api, category, page, numResults, sortBy)
+
+ response = self.conn.request_get(request_url)
+
+ if (self.helper.iserror(response)):
+ return False
+
+ xml_response = minidom.parseString(response['body'])
+
+ xml_tutorials = xml_response.getElementsByTagName('tutorial')
+
+ tutorials = list()
+
+ for xml_tutorial in xml_tutorials:
+ tutorial = self.helper.parse_tutorial(xml_tutorial)
+ tutorials.append(tutorial)
+
+ return tutorials
+
+ def list(self, type='recommended', numResults=3):
+ """
+ Returns a list of tutorials corresponding to the type specified.
+ Type examples: 'Most downloaded', 'recommended', etc.
+
+ @param type The type of list (Most downloaded, recommended, etc.)
+ @return A list of tutorials
+ """
+ request_url = "/%s/list/%s/tutorial/%s" % (self.remora_api, type, numResults)
+
+ response = self.conn.request_get(request_url)
+
+ if (self.helper.iserror(response)):
+ return False
+
+ xml_response = minidom.parseString(response['body'])
+ xml_tutorials = xml_response.getElementsByTagName('addon')
+
+ tutorials = list()
+
+ for xml_tutorial in xml_tutorials:
+ tutorial = self.helper.parse_tutorial(xml_tutorial)
+ tutorials.append(tutorial)
+
+ return tutorials
+
+
def get_tutorial_collection(self, collection_name):
"""
Returns a list of tutorials corresponding to the given collection name.
Collections can be groups like '5 most downloaded' or 'Top 10 ratings'.
-
+
@param collection_name The name of the collection from which we want the
meta-data
@return A list of tutorial meta-data corresponding to the given group
"""
raise NotImplementedError("get_tutorial_collection() not implemented... yet!")
-
+
def get_latest_version(self, tutorial_id_list):
"""
Returns the latest version number on the server, for each tutorial ID
in the list.
-
+
@param tutorial_id_list The list of tutorial IDs from which we want to
known the latest version number.
- @return A dictionary having the tutorial ID as the key and the version
+ @return A dictionary having the tutorial ID as the key and the version
as the value.
"""
- raise NotImplementedError("get_latest_version() not implemented")
-
+
+ versions = {}
+
+ for tutorial_id in tutorial_id_list:
+
+ request_url = "/%s/addon/%s/" % (self.remora_api, tutorial_id)
+
+ response = self.conn.request_get(request_url)
+
+ xml = minidom.parseString(response['body'])
+
+ versionnode = xml.getElementsByTagName("version")[0]
+
+ version = versionnode.firstChild.nodeValue
+
+ versions[tutorial_id] = version
+
+ return versions
+
def download_tutorial(self, tutorial_id, version=None):
"""
- Fetches the tutorial file from the server and returns the
-
+ Fetches the tutorial file from the server and returns the
+
@param tutorial_id The tutorial that we want to get
@param version The version number that we want to download. If None,
the latest version will be downloaded.
@return The downloaded file itself (an in-memory representation of the file,
not a path to it on the disk)
-
+
TODO : We should decide if we're saving to disk or in mem.
"""
- raise NotImplementedError("downloadTutorial() not implemented")
+ request_url = "/%s/addon/%s/" % (self.remora_api, tutorial_id)
+
+ response = self.conn.request_get(request_url)
+ if (self.helper.iserror(response)):
+ return False
+
+ xml = minidom.parseString(response['body'])
+
+ installnode = xml.getElementsByTagName("install")[0]
+ installurl = installnode.firstChild.nodeValue
+
+ fp = urllib.urlopen(installurl)
+
+ return fp
+
def login(self, username, password):
"""
Logs in the user on the store and saves the login status in the proxy
state. After a successful logon, the operation requiring a login will
be successful.
-
+
+ @param username
+ @param password
@return True if the login was successful, False otherwise
"""
- raise NotImplementedError("login() not implemented yet")
+ request_url = "/%s/auth/" % (self.tutorius_api)
+
+ params = {'username': username, 'password': password}
+
+ response = self.conn.request_post(request_url, params)
+ if (self.helper.iserror(response)):
+ return False
+
+ xml_response = minidom.parseString(response['body'])
+
+ keynode = xml_response.getElementsByTagName("token")[0]
+
+ key = keynode.getAttribute('value')
+
+ self.api_auth_key = key
+
+ return True
+
def close_session(self):
"""
Ends the user's session on the server and changes the state of the proxy
@@ -102,72 +242,203 @@ class StoreProxy(object):
@return True if the user was disconnected, False otherwise
"""
- raise NotImplementedError("close_session() not implemented yet")
+ request_url = "/%s/auth/%s" % (self.tutorius_api, self.api_auth_key)
+
+ headers = { 'X-API-Auth' : self.api_auth_key }
+
+ response = self.conn.request_delete(request_url, None, headers)
+ if (self.helper.iserror(response)):
+ return False
+
+ self.api_auth_key = ""
+
+ return True
+
def get_session_id(self):
"""
Gives the current session ID cached in the Store Proxy, or returns
None is the user is not logged yet.
-
+
@return The current session's ID, or None if the user is not logged
"""
- raise NotImplementedError("get_session_id() not implemented yet")
-
+ return self.api_auth_key
+
def rate(self, value, tutorial_store_id):
"""
Sends a rating for the given tutorial.
-
+
This function requires the user to be logged in.
-
- @param value The value of the rating. It must be an integer with a value
+
+ @param value The value of the rating. It must be an integer with a value
from 1 to 5.
@param tutorial_store_id The ID of the tutorial that was rated
@return True if the rating was sent to the Store, False otherwise.
"""
- raise NotImplementedError("rate() not implemented")
+ request_url = "/%s/review/%s" % (self.tutorius_api, tutorial_store_id)
+
+ params = {'title': 'from api', 'body': 'from api', 'rating': value}
+ headers = { 'X-API-Auth' : self.api_auth_key }
+
+ response = self.conn.request_post(request_url, params, None, None, headers)
- def publish(self, tutorial):
+ if self.helper.iserror(response):
+ return False
+
+ return True
+
+ def publish(self, tutorial, tutorial_info=None, tutorial_store_id = None):
"""
Sends a tutorial to the store.
-
+
This function requires the user to be logged in.
- @param tutorial The tutorial file to be sent. Note that this is the
+ @param tutorial The tutorial file to be sent. Note that this is the
content itself and not the path to the file.
+ @param tutorial_info An array containing the tutorial information
@return True if the tutorial was sent correctly, False otherwise.
"""
- raise NotImplemetedError("publish() not implemented")
+
+ # This is in the case we have to re-publish a tutorial
+ if tutorial_store_id != None:
+ request_url = "/%s/publish/%s" % (self.tutorius_api, tutorial_store_id)
+
+ response = self.conn.request_post(request_url)
+
+ if self.helper.iserror(response):
+ return False
+
+ return True
+
+ # Otherwise, we want to publish a new tutorial
+ if tutorial_info == None:
+ return False
+
+ request_url = "/%s/publish/" % (self.tutorius_api)
+
+ headers = { 'X-API-Auth' : self.api_auth_key }
+
+ response = self.conn.request_post(request_url, tutorial_info, tutorial, tutorial_info['filename'], headers)
+
+ if self.helper.iserror(response):
+ return False
+ return True
+
+
def unpublish(self, tutorial_store_id):
"""
Removes a tutorial from the server. The user in the current session
- needs to be the creator for it to be unpublished. This will remove
+ needs to be the creator for it to be unpublished. This will remove
the file from the server and from all its collections and categories.
-
+
This function requires the user to be logged in.
-
+
@param tutorial_store_id The ID of the tutorial to be removed
@return True if the tutorial was properly removed from the server
"""
- raise NotImplementedError("unpublish() not implemeted")
+ request_url = "/%s/publish/%s" % (self.tutorius_api, tutorial_store_id)
- def update_published_tutorial(self, tutorial_id, tutorial):
+ headers = { 'X-API-Auth' : self.api_auth_key }
+ response = self.conn.request_delete(request_url, None, headers)
+
+ if self.helper.iserror(response):
+ return False
+
+ return True
+
+ def update_published_tutorial(self, tutorial_id, tutorial, tutorial_info):
"""
Sends the new content for the tutorial with the given ID.
-
+
This function requires the user to be logged in.
-
+
@param tutorial_id The ID of the tutorial to be updated
@param tutorial The bundled tutorial file content (not a path!)
@return True if the tutorial was sent and updated, False otherwise
"""
- raise NotImplementedError("update_published_tutorial() not implemented yet")
+ request_url = "/%s/update/%s" % (self.tutorius_api, tutorial_id)
+
+ headers = { 'X-API-Auth' : self.api_auth_key }
+ response = self.conn.request_post(request_url, tutorial_info, tutorial, tutorial_info['filename'], headers)
+
+ if self.helper.iserror(response):
+ return False
+
+ return True
+
+
def register_new_user(self, user_info):
"""
- Creates a new user from the given user information.
+ Creates a new user from the given user information.
@param user_info A structure containing all the data required to do a login.
@return True if the new account was created, false otherwise
"""
- raise NotImplementedError("register_new_user() not implemented")
+ request_url = "/%s/registerNewUser" % (self.tutorius_api)
+
+ params = {'nickname': user_info['nickname'], 'password': user_info['password'], 'email': user_info['email']}
+
+ response = self.conn.request_post(request_url, params)
+
+ if self.helper.iserror(response):
+ return False
+
+ return True
+
+
+class StoreProxyHelper(object):
+ """
+ Implements helper methods for the Store, more specifically
+ methods to handle xml responses and errors
+ """
+ def iserror(self, response):
+ """
+ Check if the response received from the server is an error
+
+ @param response The XML response from the server
+ @return True if the response is an error
+ """
+
+ # first look for HTTP errors
+ http_status = response['headers']['status']
+
+ if http_status in ['400', '401', '403', '500' ]:
+ return True
+
+ # Now check if the response is valid XML
+ try:
+ minidom.parseString(response['body'])
+ except Exception, e:
+ return True
+
+ # The response is valid XML, parse it and look for
+ # an error in xml format
+ xml_response = minidom.parseString(response['body'])
+
+ errors = xml_response.getElementsByTagName('error')
+
+ if (len(errors) > 0):
+ return True
+
+ return False
+
+ def parse_tutorial(self, xml_tutorial):
+ """
+ Parse a tutorial's XML metadata and returns a dictionnary
+ containing the metadata
+
+ @param xml_tutorial The tutorial metadata in XML format
+ @return A dictionnary containing the metadata
+ """
+ tutorial = {}
+
+ tutorial['name'] = xml_tutorial.getElementsByTagName('name')[0].firstChild.nodeValue
+ tutorial['summary'] = xml_tutorial.getElementsByTagName('summary')[0].firstChild.nodeValue
+ tutorial['version'] = xml_tutorial.getElementsByTagName('version')[0].firstChild.nodeValue
+ tutorial['description'] =""
+ tutorial['author'] = ""
+ tutorial['rating'] = ""
+
+ return tutorial \ No newline at end of file