[entropy.client.services.interfaces] add DocumentList, several API improvements

This commit is contained in:
Fabio Erculiani
2011-04-02 11:21:45 +02:00
parent b5593a44ff
commit 49d2b4dc7f
+195 -49
View File
@@ -10,7 +10,7 @@
"""
__all__ = ["ClientWebServiceFactory", "ClientWebService", "Document",
"DocumentFactory"]
"DocumentList", "DocumentFactory"]
import os
import time
@@ -79,6 +79,12 @@ class Document(dict):
# see Document.document_data()
DOCUMENT_DATA_ID = "ddata"
# Document hash table key for the document title
DOCUMENT_TITLE_ID = "title"
# Document hash table key for the document description
DOCUMENT_DESCRIPTION_ID = "description"
# Document hash table key pointing to document keywords (tags...)
# see Document.document_keywords()
DOCUMENT_KEYWORDS_ID = "keywords"
@@ -208,7 +214,7 @@ class Document(dict):
# backward compatibility
if not self.is_image():
return False
if self.get(DocumentFactory.DOCUMENT_TITLE_ID) == \
if self.get(Document.DOCUMENT_TITLE_ID) == \
Document.PACKAGE_ICON_TITLE_ID:
return True
return False
@@ -249,21 +255,75 @@ class Document(dict):
"""
return self.document_type() == Document.VIDEO_TYPE_ID
class DocumentList(list):
"""
DocumentList is a list object providing extra methods for obtaining extra
document list statuses, such as the number of elements found, the current
elements list offset, and if there are more elements on the remote service.
"""
def __init__(self, package_name, total, offset):
"""
DocumentList constructor.
@param package_name: package name string
@type package_name: string
@param total: number of total documents available
@type total: int
@param offset: list offset used by remote service
@type offset: int
"""
list.__init__(self)
self._package_name = package_name
self._total = total
self._offset = offset
def package_name(self):
"""
Return the package name bound to this object.
@return: the package name
@rtype: string
"""
return self._package_name
def total(self):
"""
Return the total amount of remotely available documents.
@return: the total amount of remotely available documents.
@rtype: int
"""
return self._total
def offset(self):
"""
Return the used offset for fetching this list.
@return: the used offset
@rtype: int
"""
return self._offset
def has_more(self):
"""
Return the amount of documents available after those listed here.
@return: the amount of documents still available on the service,
given the current offset
@rtype: int
"""
return (self._total - self._offset - len(self))
class DocumentFactory(object):
"""
Class to generate valid, new Document objects.
"""
# Document hash table key for the document title
DOCUMENT_TITLE_ID = "title"
# Document hash table key for the document username
DOCUMENT_USERNAME_ID = "username"
# Document hash table key for the document description
DOCUMENT_DESCRIPTION_ID = "description"
# Payload metadatum is only available on temporary, to-be-uploaded
# Document objects. Can contain a file object or any other pointer
# to a serializable resource.
@@ -333,7 +393,7 @@ class DocumentFactory(object):
doc = Document(self._repository_id, None, Document.COMMENT_TYPE_ID)
doc[DocumentFactory.DOCUMENT_USERNAME_ID] = username
doc[Document.DOCUMENT_DATA_ID] = comment
doc[DocumentFactory.DOCUMENT_TITLE_ID] = title
doc[Document.DOCUMENT_TITLE_ID] = title
doc[Document.DOCUMENT_KEYWORDS_ID] = keywords
return doc
@@ -363,8 +423,8 @@ class DocumentFactory(object):
doc[DocumentFactory.DOCUMENT_USERNAME_ID] = username
doc[DocumentFactory.DOCUMENT_PAYLOAD_ID] = \
(os.path.basename(file_object.name), file_object)
doc[DocumentFactory.DOCUMENT_TITLE_ID] = title
doc[DocumentFactory.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_TITLE_ID] = title
doc[Document.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_KEYWORDS_ID] = keywords
return doc
@@ -394,8 +454,8 @@ class DocumentFactory(object):
doc[DocumentFactory.DOCUMENT_USERNAME_ID] = username
doc[DocumentFactory.DOCUMENT_PAYLOAD_ID] = \
(os.path.basename(file_object.name), file_object)
doc[DocumentFactory.DOCUMENT_TITLE_ID] = title
doc[DocumentFactory.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_TITLE_ID] = title
doc[Document.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_KEYWORDS_ID] = keywords
return doc
@@ -425,8 +485,8 @@ class DocumentFactory(object):
doc[DocumentFactory.DOCUMENT_USERNAME_ID] = username
doc[DocumentFactory.DOCUMENT_PAYLOAD_ID] = \
(os.path.basename(file_object.name), file_object)
doc[DocumentFactory.DOCUMENT_TITLE_ID] = title
doc[DocumentFactory.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_TITLE_ID] = title
doc[Document.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_KEYWORDS_ID] = keywords
return doc
@@ -456,8 +516,8 @@ class DocumentFactory(object):
doc[DocumentFactory.DOCUMENT_USERNAME_ID] = username
doc[DocumentFactory.DOCUMENT_PAYLOAD_ID] = \
(os.path.basename(file_object.name), file_object)
doc[DocumentFactory.DOCUMENT_TITLE_ID] = title
doc[DocumentFactory.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_TITLE_ID] = title
doc[Document.DOCUMENT_DESCRIPTION_ID] = description
doc[Document.DOCUMENT_KEYWORDS_ID] = keywords
return doc
@@ -535,6 +595,35 @@ class ClientWebService(WebService):
return self._method_getter("get_votes", params, cache = cache,
require_credentials = False)
def get_available_votes(self, cache = True):
"""
Return all the available votes for all the available packages.
@keyword cache: True means use on-disk cache if available?
@type cache: bool
@return: mapping composed by package name as key and value as vote
(float)
@rtype: dict
@raise WebService.UnsupportedParameters: if input parameters are
invalid
@raise WebService.RequestError: if request cannot be satisfied
@raise WebService.MethodNotAvailable: if API method is not
available remotely and an error occured (error code passed as
exception argument)
@raise WebService.AuthenticationRequired: if require_credentials
is True and credentials are required.
@raise WebService.AuthenticationFailed: if credentials are
not valid
@raise WebService.MalformedResponse: if JSON response cannot be
converted back to dict.
@raise WebService.UnsupportedAPILevel: if client API and Web
Service API do not match
@raise WebService.MethodResponseError; if method execution failed
"""
return self._method_getter("get_available_votes", {}, cache = cache,
require_credentials = False)
def get_downloads(self, package_names, cache = True):
"""
For given package names, return the current download counter.
@@ -569,6 +658,36 @@ class ClientWebService(WebService):
return self._method_getter("get_downloads", params, cache = cache,
require_credentials = False)
def get_available_downloads(self, cache = True):
"""
Return all the available downloads for all the available packages.
@param package_names: list of package names, either atoms or keys
@type package_names: list
@keyword cache: True means use on-disk cache if available?
@type cache: bool
@return: mapping composed by package name as key and downloads as value
@rtype: dict
@raise WebService.UnsupportedParameters: if input parameters are
invalid
@raise WebService.RequestError: if request cannot be satisfied
@raise WebService.MethodNotAvailable: if API method is not
available remotely and an error occured (error code passed as
exception argument)
@raise WebService.AuthenticationRequired: if require_credentials
is True and credentials are required.
@raise WebService.AuthenticationFailed: if credentials are
not valid
@raise WebService.MalformedResponse: if JSON response cannot be
converted back to dict.
@raise WebService.UnsupportedAPILevel: if client API and Web
Service API do not match
@raise WebService.MethodResponseError; if method execution failed
"""
return self._method_getter("get_available_downloads", {}, cache = cache,
require_credentials = False)
def add_vote(self, package_name, vote):
"""
For given package name, add a vote.
@@ -654,17 +773,25 @@ class ClientWebService(WebService):
self._drop_cached("get_downloads")
return valid
def get_icons(self, package_names, cache = True):
def get_icons(self, package_names, offset = 0, cache = True):
"""
For given package names, return the current Document icon object list.
Packages having no icon will get empty list as value.
For given package names, return the current Document icon object
DocumentList.
Packages having no icon will get empty DocumentList as value.
Results are paged, if offset is 0, the first page is returned.
For gathering information regarding pages (total documents,
offset provided, etc), see DocumentList API.
@param package_names: list of names of the packages to query,
either atom or key
@type package_names: list
@keyword offset: specify the offset document from where to start
for each package_name
@type offset: int
@keyword cache: True means use on-disk cache if available?
@type cache: bool
@return: mapping composed by package name as key and Document list
@return: mapping composed by package name as key and DocumentList
as value
@rtype: dict
@@ -684,26 +811,30 @@ class ClientWebService(WebService):
Service API do not match
@raise WebService.MethodResponseError; if method execution failed
"""
document_type_filter = [Document.IMAGE_TYPE_ID]
data = self.get_documents(package_names,
document_type_filter = document_type_filter, cache = cache)
icons_data = {}
for key in list(data.keys()):
icons_data[key] = [x for x in data.get(key, []) if x.is_icon()]
return icons_data
document_type_filter = [Document.ICON_TYPE_ID]
return self.get_documents(package_names,
document_type_filter = document_type_filter,
offset = offset, cache = cache)
def get_comments(self, package_names, cache = True):
def get_comments(self, package_names, offset = 0, cache = True):
"""
For given package names, return the current Document Comment object
list.
Packages having no comments will get empty list as value.
DocumentList.
Packages having no comments will get empty DocumentList as value.
Results are paged, if offset is 0, the first page is returned.
For gathering information regarding pages (total documents,
offset provided, etc), see DocumentList API.
@param package_names: list of names of the packages to query,
either atom or key
@type package_names: list
@keyword offset: specify the offset document from where to start
for each package_name
@type offset: int
@keyword cache: True means use on-disk cache if available?
@type cache: bool
@return: mapping composed by package name as key and Document list
@return: mapping composed by package name as key and DocumentList
as value
@rtype: dict
@@ -724,18 +855,20 @@ class ClientWebService(WebService):
@raise WebService.MethodResponseError; if method execution failed
"""
document_type_filter = [Document.COMMENT_TYPE_ID]
data = self.get_documents(package_names,
document_type_filter = document_type_filter, cache = cache)
icons_data = {}
for key in list(data.keys()):
icons_data[key] = [x for x in data.get(key, []) if x.is_icon()]
return icons_data
return self.get_documents(package_names,
document_type_filter = document_type_filter,
offset = offset, cache = cache)
def get_documents(self, package_names, document_type_filter = None,
cache = True):
offset = 0, cache = True):
"""
For given package names, return the current Document object list.
Packages having no documents will get empty list as value.
For given package names, return the current Document object
DocumentList.
Packages having no documents will get empty DocumentList as value.
Results are paged, if offset is 0, the first page is returned.
For gathering information regarding pages (total documents,
offset provided, etc), see DocumentList API.
@param package_names: list of names of the packages to query,
either atom or key
@@ -743,9 +876,12 @@ class ClientWebService(WebService):
@keyword document_type_filter: list of document type identifiers (
see Document class) that are required.
@type document_type_filter: list
@keyword offset: specify the offset document from where to start
for each package_name
@type offset: int
@keyword cache: True means use on-disk cache if available?
@type cache: bool
@return: mapping composed by package name as key and Document list as
@return: mapping composed by package name as key and DocumentList as
value
@rtype: dict
@@ -770,16 +906,25 @@ class ClientWebService(WebService):
params = {
"package_names": " ".join(package_names),
"filter": " ".join([str(x) for x in document_type_filter]),
"offset": offset,
}
objs = self._method_getter("get_documents", params, cache = cache,
require_credentials = False)
data = {}
for package_name in package_names:
objs = objs.get(package_name, [])
m_objs = data.setdefault(package_name, [])
objs_map = objs.get(package_name)
if not objs_map:
data[package_name] = DocumentList(package_name, 0, offset)
continue
total, objs = objs_map['total'], objs_map['docs']
m_objs = data.setdefault(package_name,
DocumentList(package_name, total, offset))
for obj in objs:
d_obj = Document(self._repository_id, obj['document_id'],
obj['document_type_id'])
d_obj = Document(self._repository_id,
obj[Document.DOCUMENT_DOCUMENT_ID],
obj[Document.DOCUMENT_DOCUMENT_TYPE_ID])
d_obj.update(obj)
m_objs.append(d_obj)
return data
@@ -824,8 +969,9 @@ class ClientWebService(WebService):
for document_id in document_ids:
obj = objs.get(document_id)
if obj is not None:
d_obj = Document(self._repository_id, obj['document_id'],
obj['document_type_id'])
d_obj = Document(self._repository_id,
obj[Document.DOCUMENT_DOCUMENT_ID],
obj[Document.DOCUMENT_DOCUMENT_TYPE_ID])
d_obj.update(obj)
obj = d_obj
data[package_name] = obj
@@ -874,7 +1020,7 @@ class ClientWebService(WebService):
"""
if not isinstance(document, Document):
raise AttributeError("only accepting Document objects")
if document['document_id'] is not None:
if document[Document.DOCUMENT_DOCUMENT_ID] is not None:
raise WebService.UnsupportedParameters("document is not new")
# This returns None if document is not accepted
remote_document = self._method_getter("add_document", document,