Example #1
0
class FedoraTestCase(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        unittest.TestCase.__init__(self, *args, **kwargs)
        self.fedora_fixtures_ingested = []
        self.pidspace = FEDORA_PIDSPACE

        self.repo = Repository(FEDORA_ROOT, FEDORA_USER, FEDORA_PASSWORD)

        # fixture cleanup happens in tearDown, which doesn't always run
        # if a test fails - clean up stale test objects from a previous run here
        stale_objects = list(self.repo.find_objects(pid__contains="%s:*" % self.pidspace))
        if stale_objects:
            print "Removing %d stale test object(s) in pidspace %s" % (len(stale_objects), self.pidspace)
            for obj in stale_objects:
                try:
                    self.repo.purge_object(obj.pid)
                except RequestFailed as rf:
                    logger.warn("Error purging stale test object %s (TestCase init): %s" % (obj.pid, rf))

    def setUp(self):
        # NOTE: queries require RI flush=True or test objects will not show up in RI
        self.repo.risearch.RISEARCH_FLUSH_ON_QUERY = True
        self.opener = self.repo.opener
        self.api = ApiFacade(self.opener)
        fixtures = getattr(self, "fixtures", [])
        for fixture in fixtures:
            self.ingestFixture(fixture)

    def tearDown(self):
        for pid in self.fedora_fixtures_ingested:
            try:
                self.repo.purge_object(pid)
            except RequestFailed as rf:
                logger.warn("Error purging test object %s in tear down: %s" % (pid, rf))

    def getNextPid(self):
        pidspace = getattr(self, "pidspace", None)
        return self.repo.get_next_pid(namespace=pidspace)

    def loadFixtureData(self, fname):
        data = load_fixture_data(fname)
        # if pidspace is specified, get a new pid from fedora and set it as the pid in the xml
        if hasattr(self, "pidspace"):
            xml = xmlmap.load_xmlobject_from_string(data, _MinimalFoxml)
            xml.pid = self.getNextPid()
            return xml.serialize()
        else:
            return data

    def ingestFixture(self, fname):
        object = self.loadFixtureData(fname)
        pid = self.repo.ingest(object)
        if pid:
            # we'd like this always to be true. if ingest fails we should
            # throw an exception. that probably hasn't been thoroughly
            # tested yet, though, so we'll check it until it has been.
            self.append_test_pid(pid)

    def append_test_pid(self, pid):
        self.fedora_fixtures_ingested.append(pid)
Example #2
0
class Command(BaseCommand):
    def get_password_option(option, opt, value, parser):
        setattr(parser.values, option.dest, getpass())

    help = """Generate missing Fedora content model objects and load initial objects."""

    # NOTE: will need to be converted to add_argument / argparse
    # format for django 11 (optparse will be removed)
    option_list = BaseCommand.option_list + (
        make_option('--username', '-u',
                    dest='username',
                    action='store',
                    help='''Username to connect to fedora'''),
        make_option('--password',
                    dest='password',
                    action='callback', callback=get_password_option,
                    help='''Prompt for password required when username used'''
                ))

    def __init__(self, *args, **kwargs):
        super(Command, self).__init__(*args, **kwargs)

    def handle(self, *args, **options):

        repo_args = {}
        if options.get('username') is not None:
            repo_args['username'] = options.get('username')
        if options.get('password') is not None:
            repo_args['password'] = options.get('password')

        self.repo = Repository(**repo_args)


        self.verbosity = int(options.get('verbosity', 1))

        # FIXME/TODO: add count/summary info for content models objects created ?
        if self.verbosity > 1:
            sys.stdout.write("Generating content models for %d classes"
                             % len(DigitalObject.defined_types))

        for cls in DigitalObject.defined_types.itervalues():
            self.process_class(cls)

        self.load_initial_objects()

    def process_class(self, cls):
        try:
            ContentModel.for_class(cls, self.repo)
        except ValueError as v:
            # for_class raises a ValueError when a class has >1
            # CONTENT_MODELS.
            if self.verbosity > 1:
                sys.stderr.write(v)
        except RequestFailed as rf:
            if hasattr(rf, 'detail'):
                if 'ObjectExistsException' in rf.detail:
                    # This shouldn't happen, since ContentModel.for_class
                    # shouldn't attempt to ingest unless the object doesn't exist.
                    # In some cases, Fedora seems to report that an object doesn't exist,
                    # then complain on attempted ingest.

                    full_name = '%s.%s' % (cls.__module__, cls.__name__)
                    logger.warn('Fedora error (ObjectExistsException) on Content Model ingest for %s' % \
                                full_name)
                else:
                    # if there is a detail message, display that
                    sys.stderr.write("Error ingesting ContentModel for %s: %s"
                                     % (cls, rf.detail))

    def load_initial_objects(self):
        # look for any .xml files in apps under fixtures/initial_objects
        # and attempt to load them as Fedora objects
        # NOTE! any fixtures should have pids specified, or new versions of the
        # fixture will be created every time syncrepo runs

        app_module_paths = []

        if hasattr(django_apps, 'get_app_configs'):
            apps = django_apps.get_app_configs()
        else:
            apps = get_apps()

        # monkey see django code, monkey do
        for app in apps:
            # newer django AppConfig
            if hasattr(app, 'path'):
                app_module_paths.append(app.path)
            elif hasattr(app, '__path__'):
                # It's a 'models/' subpackage
                for path in app.__path__:
                    app_module_paths.append(path)
            else:
                # It's a models.py module
                app_module_paths.append(app.__file__)

        app_fixture_paths = [os.path.join(os.path.dirname(path),
                                          'fixtures', 'initial_objects', '*.xml')
                             for path in app_module_paths]
        fixture_count = 0
        load_count = 0

        for path in app_fixture_paths:
            fixtures = glob.iglob(path)
            for f in fixtures:
                # FIXME: is there a sane, sensible way to shorten file path for error/success messages?
                fixture_count += 1
                with open(f) as fixture_data:
                    # rather than pulling PID from fixture and checking if it already exists,
                    # just ingest and catch appropriate excetions
                    try:
                        pid = self.repo.ingest(fixture_data.read(), "loaded from fixture")
                        if self.verbosity > 1:
                            self.stdout.write("Loaded fixture %s as %s" % (f, pid))
                        load_count += 1
                    except RequestFailed as rf:
                        if hasattr(rf, 'detail'):
                            if 'ObjectExistsException' in rf.detail or \
                              'already exists in the registry; the object can\'t be re-created' in rf.detail:
                                if self.verbosity > 1:
                                    self.stdout.write("Fixture %s has already been loaded" % f)
                            elif 'ObjectValidityException' in rf.detail:
                                # could also look for: fedora.server.errors.ValidationException
                                # (e.g., RELS-EXT about does not match pid)
                                self.stdout.write("Error: fixture %s is not a valid Repository object" % f)
                            else:
                                # if there is at least a detail message, display that
                                self.stdout.write("Error ingesting %s: %s" %
                                                  (f, rf.detail))
                        else:
                            raise rf

        # summarize what was actually done
        if self.verbosity > 0:
            if fixture_count == 0:
                self.stdout.write("No fixtures found")
            else:
                self.stdout.write("Loaded %d object(s) from %d fixture(s)"
                                  % (load_count, fixture_count))
Example #3
0
class FedoraTestCase(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        unittest.TestCase.__init__(self, *args, **kwargs)
        self.fedora_fixtures_ingested = []
        self.pidspace = FEDORA_PIDSPACE

        self.repo = Repository(FEDORA_ROOT, FEDORA_USER, FEDORA_PASSWORD)

        # fixture cleanup happens in tearDown, which doesn't always run
        # if a test fails - clean up stale test objects from a previous run here
        stale_objects = list(self.repo.find_objects(pid__contains='%s:*' % self.pidspace))
        if stale_objects:
            logger.info('Removing %d stale test object(s) in pidspace %s' \
                % (len(stale_objects), self.pidspace))

            for obj in stale_objects:
                try:
                    self.repo.purge_object(obj.pid)
                except RequestFailed as rf:
                    logger.warn('Error purging stale test object %s (TestCase init): %s' % \
                                (obj.pid, rf))

    def setUp(self):
        # NOTE: queries require RI flush=True or test objects will not show up in RI
        self.repo.risearch.RISEARCH_FLUSH_ON_QUERY = True
        self.opener = self.repo.opener
        self.api = ApiFacade(self.opener)
        fixtures = getattr(self, 'fixtures', [])
        for fixture in fixtures:
            self.ingestFixture(fixture)

    def tearDown(self):
        for pid in self.fedora_fixtures_ingested:
            try:
                self.repo.purge_object(pid)
            except RequestFailed as rf:
                logger.warn('Error purging test object %s in tear down: %s' % \
                            (pid, rf))

    def getNextPid(self):
        pidspace = getattr(self, 'pidspace', None)
        return self.repo.get_next_pid(namespace=pidspace)

    def loadFixtureData(self, fname):
        data = load_fixture_data(fname)
        # if pidspace is specified, get a new pid from fedora and set it as the pid in the xml
        if hasattr(self, 'pidspace'):
            xml = xmlmap.load_xmlobject_from_string(data, _MinimalFoxml)
            xml.pid = self.getNextPid()
            return xml.serialize()
        else:
            return data

    def ingestFixture(self, fname):
        object = self.loadFixtureData(fname)
        pid = self.repo.ingest(object)
        if pid:
            # we'd like this always to be true. if ingest fails we should
            # throw an exception. that probably hasn't been thoroughly
            # tested yet, though, so we'll check it until it has been.
            self.append_pid(pid)

    # note: renamed from append_test_pid so that nosetests doesn't
    # autodetect and attempt to run as a unit test.
    def append_pid(self, pid):
            self.fedora_fixtures_ingested.append(pid)
Example #4
0
class Command(BaseCommand):
    def get_password_option(option, opt, value, parser):
        setattr(parser.values, option.dest, getpass())

    help = """Generate missing Fedora content model objects and load initial objects."""

    # NOTE: will need to be converted to add_argument / argparse
    # format for django 11 (optparse will be removed)
    option_list = BaseCommand.option_list + (
        make_option('--username',
                    '-u',
                    dest='username',
                    action='store',
                    help='''Username to connect to fedora'''),
        make_option(
            '--password',
            dest='password',
            action='callback',
            callback=get_password_option,
            help='''Prompt for password required when username used'''))

    def __init__(self, *args, **kwargs):
        super(Command, self).__init__(*args, **kwargs)

    def handle(self, *args, **options):

        repo_args = {}
        if options.get('username') is not None:
            repo_args['username'] = options.get('username')
        if options.get('password') is not None:
            repo_args['password'] = options.get('password')

        self.repo = Repository(**repo_args)

        self.verbosity = int(options.get('verbosity', 1))

        # FIXME/TODO: add count/summary info for content models objects created ?
        if self.verbosity > 1:
            sys.stdout.write("Generating content models for %d classes" %
                             len(DigitalObject.defined_types))

        for cls in DigitalObject.defined_types.itervalues():
            self.process_class(cls)

        self.load_initial_objects()

    def process_class(self, cls):
        try:
            ContentModel.for_class(cls, self.repo)
        except ValueError as v:
            # for_class raises a ValueError when a class has >1
            # CONTENT_MODELS.
            if self.verbosity > 1:
                sys.stderr.write(v)
        except RequestFailed as rf:
            if hasattr(rf, 'detail'):
                if 'ObjectExistsException' in rf.detail:
                    # This shouldn't happen, since ContentModel.for_class
                    # shouldn't attempt to ingest unless the object doesn't exist.
                    # In some cases, Fedora seems to report that an object doesn't exist,
                    # then complain on attempted ingest.

                    full_name = '%s.%s' % (cls.__module__, cls.__name__)
                    logger.warn('Fedora error (ObjectExistsException) on Content Model ingest for %s' % \
                                full_name)
                else:
                    # if there is a detail message, display that
                    sys.stderr.write(
                        "Error ingesting ContentModel for %s: %s" %
                        (cls, rf.detail))

    def load_initial_objects(self):
        # look for any .xml files in apps under fixtures/initial_objects
        # and attempt to load them as Fedora objects
        # NOTE! any fixtures should have pids specified, or new versions of the
        # fixture will be created every time syncrepo runs

        app_module_paths = []

        if hasattr(django_apps, 'get_app_configs'):
            apps = django_apps.get_app_configs()
        else:
            apps = get_apps()

        # monkey see django code, monkey do
        for app in apps:
            # newer django AppConfig
            if hasattr(app, 'path'):
                app_module_paths.append(app.path)
            elif hasattr(app, '__path__'):
                # It's a 'models/' subpackage
                for path in app.__path__:
                    app_module_paths.append(path)
            else:
                # It's a models.py module
                app_module_paths.append(app.__file__)

        app_fixture_paths = [
            os.path.join(os.path.dirname(path), 'fixtures', 'initial_objects',
                         '*.xml') for path in app_module_paths
        ]
        fixture_count = 0
        load_count = 0

        for path in app_fixture_paths:
            fixtures = glob.iglob(path)
            for f in fixtures:
                # FIXME: is there a sane, sensible way to shorten file path for error/success messages?
                fixture_count += 1
                with open(f) as fixture_data:
                    # rather than pulling PID from fixture and checking if it already exists,
                    # just ingest and catch appropriate excetions
                    try:
                        pid = self.repo.ingest(fixture_data.read(),
                                               "loaded from fixture")
                        if self.verbosity > 1:
                            self.stdout.write("Loaded fixture %s as %s" %
                                              (f, pid))
                        load_count += 1
                    except RequestFailed as rf:
                        if hasattr(rf, 'detail'):
                            if 'ObjectExistsException' in rf.detail or \
                              'already exists in the registry; the object can\'t be re-created' in rf.detail:
                                if self.verbosity > 1:
                                    self.stdout.write(
                                        "Fixture %s has already been loaded" %
                                        f)
                            elif 'ObjectValidityException' in rf.detail:
                                # could also look for: fedora.server.errors.ValidationException
                                # (e.g., RELS-EXT about does not match pid)
                                self.stdout.write(
                                    "Error: fixture %s is not a valid Repository object"
                                    % f)
                            else:
                                # if there is at least a detail message, display that
                                self.stdout.write("Error ingesting %s: %s" %
                                                  (f, rf.detail))
                        else:
                            raise rf

        # summarize what was actually done
        if self.verbosity > 0:
            if fixture_count == 0:
                self.stdout.write("No fixtures found")
            else:
                self.stdout.write("Loaded %d object(s) from %d fixture(s)" %
                                  (load_count, fixture_count))