diff --git a/sss/config.py b/sss/config.py index 643645c..df2319e 100644 --- a/sss/config.py +++ b/sss/config.py @@ -1,7 +1,7 @@ import os, uuid, sys, json from ingesters_disseminators import DefaultEntryIngester, DefaultDisseminator, FeedDisseminator, BinaryIngester, SimpleZipIngester, METSDSpaceIngester from negotiator import AcceptParameters, ContentType -from core import SwordServer, Authenticator, WebUI +from core import SwordServer, Authenticator, DAO, WebUI from sss_logging import logging ssslog = logging.getLogger(__name__) @@ -171,8 +171,9 @@ # In this default configuration we use the built-in SSS repository's # implementations for everything "sword_server" : "sss.repository.SSS", + "dao" : "sss.repository.DAO" "authenticator" : "sss.repository.SSSAuthenticator", - "webui" : "sss.repository.WebInterface" + "webui" : "sss.repository.WebInterface", } """ @@ -206,7 +207,13 @@ def get_authenticator_implementation(self): return self._get_class(self.authenticator) else: return Authenticator - + + def get_dao_implementation(self): + if self.dao is not None: + return self._get_class(self.dao) + else: + return DAO + def get_webui_implementation(self): if self.webui is not None: return self._get_class(self.webui) @@ -293,7 +300,7 @@ def _load_json(self): c = "" for line in f: if line.strip().startswith("#"): - c+= "\n" # this makes it easier to debug the config + c += "\n" # this makes it easier to debug the config else: c += line return json.loads(c) diff --git a/sss/core.py b/sss/core.py index f58ebc0..77fec90 100644 --- a/sss/core.py +++ b/sss/core.py @@ -143,6 +143,153 @@ def basic_authenticate(self, username, password, obo): def repoze_who_authenticate(self, identity, obo): raise NotImplementedError() +class DAO(object): + """ + Data Access Object for interacting with the store + """ + def __init__(self, config): + """ + Initialise the DAO and create the store directory in the Configuration() object if it does not already + exist along with the relevant collections. + """ + raise NotImplementedError() + + def get_collection_names(self): + """ list all the collections in the store """ + raise NotImplementedError() + + def get_container_names(self, collection): + """ list all the containers in the collection """ + raise NotImplementedError() + + def collection_exists(self, collection): + """ + Does the specified collection exist? + Args: + -collection: the Collection name + Returns true or false + """ + raise NotImplementedError() + + def container_exists(self, collection, id): + """ + Does the specified container exist? If the collection does not exist this will still return and will return + false + Args: + -collection: the Collection name + -id: the container id + Returns true or false + """ + raise NotImplementedError() + + def file_exists(self, collection, id, filename): + raise NotImplementedError() + + def create_container(self, collection, id=None): + """ + Create a container in the specified collection. The container will be assigned a random UUID as its + identifier. + Args: + -collection: the collection name in which to create the container + Returns the ID of the container + """ + # invent an identifier for the item, and create its directory + # we may have been passed an ID to use + raise NotImplementedError() + + def save(self, filepath, content, opts="w"): + """ + Shortcut to save the content to the filepath with the associated file handle opts (defaults to "w", so pass + in "wb" for binary files + """ + raise NotImplementedError() + + def get_filename(self, filename): + """ + Create a timestamped file name to avoid name clashes in the store + """ + raise NotImplementedError() + + def store_atom(self, collection, id, atom): + """ Store the supplied atom document content in the object identified by the id in the specified collection """ + raise NotImplementedError() + + def store_content(self, collection, id, content, filename): + """ + Store the supplied content in the object identified by the id in the specified collection under the supplied + filename. In reality, to avoid name collisions the filename will be preceded with a timestamp in the store. + Returns the localised filename the content was stored under + """ + raise NotImplementedError() + + def store_statement(self, collection, id, statement): + """ Store the supplied statement document content in the object identified by the id in the specified collection """ + # store the RDF version + raise NotImplementedError() + + def store_deposit_receipt(self, collection, id, receipt): + """ Store the supplied receipt document content in the object identified by the id in the specified collection """ + raise NotImplementedError() + + def store_metadata(self, collection, id, metadata): + """ Store the supplied metadata dictionary in the object identified by the id in the specified collection """ + raise NotImplementedError() + + def get_metadata(self, collection, id): + raise NotImplementedError() + + def remove_content(self, collection, id, keep_metadata=False, keep_atom=False): + """ + Remove all the content from the specified container. If keep_metadata is True then the sss_metadata.xml + file will not be removed + """ + raise NotImplementedError() + + def remove_container(self, collection, id): + """ Remove the specified container and all of its contents """ + raise NotImplementedError() + + def get_store_path(self, collection, id=None, filename=None): + """ + Get the path to the specified filename in the store. This is a utility method and should be used with care; + all content which goes into the store through the store_content method will have its filename localised to + avoid name clashes, so this method CANNOT be used to retrieve those files. Instead, this should be used + internally to locate sss specific files in the container, and for packagers to write their own files into + the store which are not part of the content itself. + """ + raise NotImplementedError() + + def get_deposit_receipt_content(self, collection, id): + """ Read the deposit receipt for the specified container """ + raise NotImplementedError() + + def get_statement_content(self, collection, id): + """ Read the statement for the specified container """ + raise NotImplementedError() + + def get_statement_feed(self, collection, id): + """ Read the statement for the specified container """ + raise NotImplementedError() + + def get_atom_content(self, collection, id): + """ Read the statement for the specified container """ + raise NotImplementedError() + + def load_statement(self, collection, id): + """ + Load the Statement object for the specified container + Returns a Statement object fully populated to represent this object + """ + raise NotImplementedError() + + def list_content(self, collection, id, exclude=[]): + """ + List the contents of the specified container, excluding any files whose name exactly matches those in the + exclude list. This method will also not list sss specific files, thus limiting it to the content files of + the object. + """ + raise NotImplementedError() + class EntryDocument(object): def __init__(self, atom_id=None, alternate_uri=None, content_uri=None, edit_uri=None, se_uri=None, em_uris=None, diff --git a/sss/repository.py b/sss/repository.py index 0cc0e4c..8f8e955 100644 --- a/sss/repository.py +++ b/sss/repository.py @@ -10,6 +10,7 @@ from sss_logging import logging ssslog = logging.getLogger(__name__) + class WebInterface(WebUI): def get(self, path=None): if path is not None: @@ -146,7 +147,8 @@ def __init__(self, config, auth): SwordServer.__init__(self, config, auth) # create a DAO for us to use - self.dao = DAO(self.configuration) + DAO = config.get_dao_implementation() + self.dao = DAO(config) # create a Namespace object for us to use self.ns = Namespaces() @@ -994,6 +996,9 @@ def get_collection_names(self): """ list all the collections in the store """ return os.listdir(self.configuration.store_dir) + def get_container_names(self, collection): + return os.listdir(self.get_store_path(collection)) + def collection_exists(self, collection): """ Does the specified collection exist? @@ -1207,7 +1212,8 @@ class HomePage(WebPage): """ def __init__(self, config): self.config = config - self.dao = DAO(self.config) + DAO = config.get_dao_implementation() + self.dao = DAO(config) self.um = URIManager(config) def get_home_page(self): @@ -1229,6 +1235,7 @@ def get_home_page(self): class CollectionPage(WebPage): def __init__(self, config): self.config = config + DAO = config.get_dao_implementation() self.dao = DAO(config) self.um = URIManager(config) @@ -1236,10 +1243,8 @@ def get_collection_page(self, id): frag = "