コード例 #1
0
ファイル: test_lane.py プロジェクト: datalogics/server_core
    def test_visible_sublanes(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        urban_fantasy, ig = Genre.lookup(self._db, classifier.Urban_Fantasy)
        humorous, ig = Genre.lookup(self._db, classifier.Humorous_Fiction)

        visible_sublane = Lane(self._db, "Humorous Fiction", genres=humorous)

        visible_grandchild = Lane(self._db,
                                  "Urban Fantasy",
                                  genres=urban_fantasy)

        invisible_sublane = Lane(self._db,
                                 "Fantasy",
                                 invisible=True,
                                 genres=fantasy,
                                 sublanes=[visible_grandchild],
                                 subgenre_behavior=Lane.IN_SAME_LANE)

        lane = Lane(self._db,
                    "English",
                    sublanes=[visible_sublane, invisible_sublane],
                    subgenre_behavior=Lane.IN_SAME_LANE)

        eq_(2, len(lane.visible_sublanes))
        assert visible_sublane in lane.visible_sublanes
        assert visible_grandchild in lane.visible_sublanes
コード例 #2
0
ファイル: test_lane.py プロジェクト: datalogics/server_core
    def test_custom_sublanes(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        urban_fantasy, ig = Genre.lookup(self._db, classifier.Urban_Fantasy)

        urban_fantasy_lane = Lane(self._db,
                                  "Urban Fantasy",
                                  genres=urban_fantasy)

        fantasy_lane = Lane(self._db,
                            "Fantasy",
                            fantasy,
                            genres=fantasy,
                            subgenre_behavior=Lane.IN_SAME_LANE,
                            sublanes=[urban_fantasy_lane])
        eq_([urban_fantasy_lane], fantasy_lane.sublanes.lanes)

        # You can just give the name of a genre as a sublane and it
        # will work.
        fantasy_lane = Lane(self._db,
                            "Fantasy",
                            fantasy,
                            genres=fantasy,
                            subgenre_behavior=Lane.IN_SAME_LANE,
                            sublanes="Urban Fantasy")
        eq_([["Urban Fantasy"]],
            [x.genre_names for x in fantasy_lane.sublanes.lanes])
コード例 #3
0
    def test_gather_matching_genres(self):
        self.fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        self.urban_fantasy, ig = Genre.lookup(self._db,
                                              classifier.Urban_Fantasy)

        self.cooking, ig = Genre.lookup(self._db, classifier.Cooking)
        self.history, ig = Genre.lookup(self._db, classifier.History)

        # Fantasy contains three subgenres and is restricted to fiction.
        fantasy, default = Lane.gather_matching_genres(
            [self.fantasy], Lane.FICTION_DEFAULT_FOR_GENRE)
        eq_(4, len(fantasy))
        eq_(True, default)

        fantasy, default = Lane.gather_matching_genres([self.fantasy], True)
        eq_(4, len(fantasy))
        eq_(True, default)

        fantasy, default = Lane.gather_matching_genres([self.fantasy], True,
                                                       [self.urban_fantasy])
        eq_(3, len(fantasy))
        eq_(True, default)

        # Attempting to create a contradiction (like nonfiction fantasy)
        # will create a lane broad enough to actually contain books
        fantasy, default = Lane.gather_matching_genres([self.fantasy], False)
        eq_(4, len(fantasy))
        eq_(Lane.BOTH_FICTION_AND_NONFICTION, default)

        # Fantasy and history have conflicting fiction defaults, so
        # although we can make a lane that contains both, we can't
        # have it use the default value.
        assert_raises(UndefinedLane, Lane.gather_matching_genres,
                      [self.fantasy, self.history],
                      Lane.FICTION_DEFAULT_FOR_GENRE)
コード例 #4
0
    def test_search_info(self):
        # Searching this lane will use the language
        # and audience restrictions from the lane.
        lane = self._lane()
        lane.display_name = "Fiction"
        lane.languages = ["eng", "ger"]
        lane.audiences = [Classifier.AUDIENCE_YOUNG_ADULT]
        lane.fiction = True

        info = OpenSearchDocument.search_info(lane)
        eq_("Search", info['name'])
        eq_("Search English/Deutsch Young Adult", info['description'])
        eq_("english/deutsch-young-adult", info['tags'])

        # This lane is the root for a patron type, so searching
        # it will use all the lane's restrictions.
        root_lane = self._lane()
        root_lane.root_for_patron_type = ['A']
        root_lane.display_name = "Science Fiction & Fantasy"
        sf, ignore = Genre.lookup(self._db, "Science Fiction")
        fantasy, ignore = Genre.lookup(self._db, "Fantasy")
        root_lane.add_genre(sf)
        root_lane.add_genre(fantasy)

        info = OpenSearchDocument.search_info(root_lane)
        eq_("Search", info['name'])
        eq_("Search Science Fiction & Fantasy", info['description'])
        eq_("science-fiction-&-fantasy", info['tags'])
コード例 #5
0
ファイル: test_lane.py プロジェクト: datalogics/server_core
 def test_all_matching_genres(self):
     fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
     cooking, ig = Genre.lookup(self._db, classifier.Cooking)
     matches = Lane.all_matching_genres(self._db, [fantasy, cooking])
     names = sorted([x.name for x in matches])
     eq_([
         u'Cooking', u'Epic Fantasy', u'Fantasy', u'Historical Fantasy',
         u'Urban Fantasy'
     ], names)
コード例 #6
0
    def test_from_description(self):
        """Create a LaneList from a simple description."""
        lanes = LaneList.from_description(
            self._db,
            None,
            [dict(
                full_name="Fiction",
                fiction=True,
                audiences=Classifier.AUDIENCE_ADULT,
            ),
             classifier.Fantasy,
             dict(
                 full_name="Young Adult",
                 fiction=Lane.BOTH_FICTION_AND_NONFICTION,
                 audiences=Classifier.AUDIENCE_YOUNG_ADULT,
             ),
         ]
        )

        fantasy_genre, ignore = Genre.lookup(self._db, classifier.Fantasy.name)
        urban_fantasy_genre, ignore = Genre.lookup(self._db, classifier.Urban_Fantasy.name)

        fiction = lanes.by_languages['']['Fiction']
        young_adult = lanes.by_languages['']['Young Adult']
        fantasy = lanes.by_languages['']['Fantasy'] 
        urban_fantasy = lanes.by_languages['']['Urban Fantasy'] 

        eq_(set([fantasy, fiction, young_adult]), set(lanes.lanes))

        eq_("Fiction", fiction.name)
        eq_(set([Classifier.AUDIENCE_ADULT]), fiction.audiences)
        eq_([], fiction.genre_ids)
        eq_(True, fiction.fiction)

        eq_("Fantasy", fantasy.name)
        eq_(set(), fantasy.audiences)
        expect = set(x.name for x in fantasy_genre.self_and_subgenres)
        eq_(expect, set(fantasy.genre_names))
        eq_(True, fantasy.fiction)

        eq_("Urban Fantasy", urban_fantasy.name)
        eq_(set(), urban_fantasy.audiences)
        eq_([urban_fantasy_genre.id], urban_fantasy.genre_ids)
        eq_(True, urban_fantasy.fiction)

        eq_("Young Adult", young_adult.name)
        eq_(set([Classifier.AUDIENCE_YOUNG_ADULT]), young_adult.audiences)
        eq_([], young_adult.genre_ids)
        eq_(Lane.BOTH_FICTION_AND_NONFICTION, young_adult.fiction)
コード例 #7
0
    def test_custom_lanes_conflict_with_subgenre_sublanes(self):

        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        urban_fantasy, ig = Genre.lookup(self._db, classifier.Urban_Fantasy)

        urban_fantasy_lane = Lane(
            self._db, "Urban Fantasy", genres=urban_fantasy)

        assert_raises(UndefinedLane, Lane,
            self._db, "Fantasy", fantasy, 
            genres=fantasy,
            audiences=Lane.AUDIENCE_YOUNG_ADULT,
            subgenre_behavior=Lane.IN_SUBLANES,
            sublanes=[urban_fantasy_lane]
        )
コード例 #8
0
    def test_get_search_target(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        lane = Lane(
            self._db, "YA Fantasy", genres=fantasy, 
            languages='eng',
            audiences=Lane.AUDIENCE_YOUNG_ADULT,
            age_range=[15,16],
            subgenre_behavior=Lane.IN_SUBLANES
        )
        sublanes = lane.sublanes.lanes
        names = sorted([x.name for x in sublanes])
        eq_(["Epic Fantasy", "Historical Fantasy", "Urban Fantasy"],
            names)

        # To start with, none of the lanes are searchable.
        eq_(None, lane.search_target)
        eq_(None, sublanes[0].search_target)

        # If we make a lane searchable, suddenly there's a search target.
        lane.searchable = True
        eq_(lane, lane.search_target)

        # The searchable lane also becomes the search target for its
        # children.
        eq_(lane, sublanes[0].search_target)
コード例 #9
0
 def _lane(self, display_name=None, library=None,
           parent=None, genres=None, languages=None,
           fiction=None
 ):
     display_name = display_name or self._str
     library = library or self._default_library
     lane, is_new = create(
         self._db, Lane,
         library=library,
         parent=parent, display_name=display_name,
         fiction=fiction
     )
     if is_new and parent:
         lane.priority = len(parent.sublanes)-1
     if genres:
         if not isinstance(genres, list):
             genres = [genres]
         for genre in genres:
             if isinstance(genre, basestring):
                 genre, ignore = Genre.lookup(self._db, genre)
             lane.genres.append(genre)
     if languages:
         if not isinstance(languages, list):
             languages = [languages]
         lane.languages = languages
     return lane
コード例 #10
0
 def _lane(self,
           display_name=None,
           library=None,
           parent=None,
           genres=None,
           languages=None,
           fiction=None):
     display_name = display_name or self._str
     library = library or self._default_library
     lane, is_new = get_one_or_create(
         self._db,
         Lane,
         library=library,
         parent=parent,
         display_name=display_name,
         create_method_kwargs=dict(fiction=fiction))
     if is_new and parent:
         lane.priority = len(parent.sublanes) - 1
     if genres:
         if not isinstance(genres, list):
             genres = [genres]
         for genre in genres:
             if isinstance(genre, basestring):
                 genre, ignore = Genre.lookup(self._db, genre)
             lane.genres.append(genre)
     if languages:
         if not isinstance(languages, list):
             languages = [languages]
         lane.languages = languages
     return lane
コード例 #11
0
    def load_genre(cls, _db, descriptor):
        """Turn some kind of genre descriptor into a (Genre, GenreData) 
        2-tuple.

        The descriptor might be a 2-tuple, a 3-tuple, a Genre object
        or a GenreData object.
        """
        if isinstance(descriptor, tuple):
            if len(descriptor) == 2:
                genre, subgenres = descriptor
            else:
                genre, subgenres, audience_restriction = descriptor
        else:
            genre = descriptor

        if isinstance(genre, GenreData):
            genredata = genre
        else:
            if isinstance(genre, Genre):
                genre_name = genre.name
            else:
                genre_name = genre
            # It's in the database--just make sure it's not an old entry
            # that shouldn't be in the database anymore.
            genredata = classifier.genres.get(genre_name)

        if not isinstance(genre, Genre):
            genre, ignore = Genre.lookup(_db, genre)
        return genre, genredata
コード例 #12
0
ファイル: test_lane.py プロジェクト: datalogics/server_core
    def test_gather_matching_genres(self):
        self.fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        self.urban_fantasy, ig = Genre.lookup(self._db,
                                              classifier.Urban_Fantasy)

        self.cooking, ig = Genre.lookup(self._db, classifier.Cooking)
        self.history, ig = Genre.lookup(self._db, classifier.History)

        # Fantasy contains three subgenres and is restricted to fiction.
        fantasy, default = Lane.gather_matching_genres(
            self._db, [self.fantasy], Lane.FICTION_DEFAULT_FOR_GENRE)
        eq_(4, len(fantasy))
        eq_(True, default)

        fantasy, default = Lane.gather_matching_genres(self._db,
                                                       [self.fantasy], True)
        eq_(4, len(fantasy))
        eq_(True, default)

        fantasy, default = Lane.gather_matching_genres(self._db,
                                                       [self.fantasy], True,
                                                       [self.urban_fantasy])
        eq_(3, len(fantasy))
        eq_(True, default)

        # If there are only exclude_genres available, then it and its
        # subgenres are ignored while every OTHER genre is set.
        genres, default = Lane.gather_matching_genres(self._db, [], True,
                                                      [self.fantasy])
        eq_(False,
            any([g for g in self.fantasy.self_and_subgenres if g in genres]))
        # According to known fiction status, that is.
        eq_(True, all([g.default_fiction == True for g in genres]))

        # Attempting to create a contradiction (like nonfiction fantasy)
        # will create a lane broad enough to actually contain books
        fantasy, default = Lane.gather_matching_genres(self._db,
                                                       [self.fantasy], False)
        eq_(4, len(fantasy))
        eq_(Lane.BOTH_FICTION_AND_NONFICTION, default)

        # Fantasy and history have conflicting fiction defaults, so
        # although we can make a lane that contains both, we can't
        # have it use the default value.
        assert_raises(UndefinedLane, Lane.gather_matching_genres, self._db,
                      [self.fantasy, self.history],
                      Lane.FICTION_DEFAULT_FOR_GENRE)
コード例 #13
0
    def test_visible_parent(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        urban_fantasy, ig = Genre.lookup(self._db, classifier.Urban_Fantasy)

        sublane = Lane(
            self._db, "Urban Fantasy", genres=urban_fantasy)

        invisible_parent = Lane(
            self._db, "Fantasy", invisible=True, genres=fantasy, 
            sublanes=[sublane], subgenre_behavior=Lane.IN_SAME_LANE)

        visible_grandparent = Lane(
            self._db, "English", sublanes=[invisible_parent],
            subgenre_behavior=Lane.IN_SAME_LANE)

        eq_(sublane.visible_parent(), visible_grandparent)
        eq_(invisible_parent.visible_parent(), visible_grandparent)
        eq_(visible_grandparent.visible_parent(), None)
コード例 #14
0
    def test_visible_ancestors(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        urban_fantasy, ig = Genre.lookup(self._db, classifier.Urban_Fantasy)

        lane = Lane(
            self._db, "Urban Fantasy", genres=urban_fantasy)

        visible_parent = Lane(
            self._db, "Fantasy", genres=fantasy,
            sublanes=[lane], subgenre_behavior=Lane.IN_SAME_LANE)

        invisible_grandparent = Lane(
            self._db, "English", invisible=True, sublanes=[visible_parent],
            subgenre_behavior=Lane.IN_SAME_LANE)

        visible_ancestor = Lane(
            self._db, "Books With Words", sublanes=[invisible_grandparent],
            subgenre_behavior=Lane.IN_SAME_LANE)

        eq_(lane.visible_ancestors(), [visible_parent, visible_ancestor])
コード例 #15
0
ファイル: test_lane.py プロジェクト: datalogics/server_core
    def test_subgenres_become_sublanes(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        lane = Lane(self._db,
                    "YA Fantasy",
                    genres=fantasy,
                    languages='eng',
                    audiences=Lane.AUDIENCE_YOUNG_ADULT,
                    age_range=[15, 16],
                    subgenre_behavior=Lane.IN_SUBLANES)
        sublanes = lane.sublanes.lanes
        names = sorted([x.name for x in sublanes])
        eq_(["Epic Fantasy", "Historical Fantasy", "Urban Fantasy"], names)

        # Sublanes inherit settings from their parent.
        assert all([x.languages == ['eng'] for x in sublanes])
        assert all([x.age_range == [15, 16] for x in sublanes])
        assert all([x.audiences == set(['Young Adult']) for x in sublanes])
コード例 #16
0
    def _work(self,
              title=None,
              authors=None,
              genre=None,
              language=None,
              audience=None,
              fiction=True,
              with_license_pool=False,
              with_open_access_download=False,
              quality=0.5,
              series=None,
              presentation_edition=None,
              collection=None,
              data_source_name=None):
        """Create a Work.

        For performance reasons, this method does not generate OPDS
        entries or calculate a presentation edition for the new
        Work. Tests that rely on this information being present
        should call _slow_work() instead, which takes more care to present
        the sort of Work that would be created in a real environment.
        """
        pools = []
        if with_open_access_download:
            with_license_pool = True
        language = language or "eng"
        title = unicode(title or self._str)
        audience = audience or Classifier.AUDIENCE_ADULT
        if audience == Classifier.AUDIENCE_CHILDREN and not data_source_name:
            # TODO: This is necessary because Gutenberg's childrens books
            # get filtered out at the moment.
            data_source_name = DataSource.OVERDRIVE
        elif not data_source_name:
            data_source_name = DataSource.GUTENBERG
        if fiction is None:
            fiction = True
        new_edition = False
        if not presentation_edition:
            new_edition = True
            presentation_edition = self._edition(
                title=title,
                language=language,
                authors=authors,
                with_license_pool=with_license_pool,
                with_open_access_download=with_open_access_download,
                data_source_name=data_source_name,
                series=series,
                collection=collection,
            )
            if with_license_pool:
                presentation_edition, pool = presentation_edition
                if with_open_access_download:
                    pool.open_access = True
                pools = [pool]
        else:
            pools = presentation_edition.license_pools
        work, ignore = get_one_or_create(self._db,
                                         Work,
                                         create_method_kwargs=dict(
                                             audience=audience,
                                             fiction=fiction,
                                             quality=quality),
                                         id=self._id)
        if genre:
            if not isinstance(genre, Genre):
                genre, ignore = Genre.lookup(self._db, genre, autocreate=True)
            work.genres = [genre]
        work.random = 0.5
        work.set_presentation_edition(presentation_edition)

        if pools:
            # make sure the pool's presentation_edition is set,
            # bc loan tests assume that.
            if not work.license_pools:
                for pool in pools:
                    work.license_pools.append(pool)

            for pool in pools:
                pool.set_presentation_edition()

            # This is probably going to be used in an OPDS feed, so
            # fake that the work is presentation ready.
            work.presentation_ready = True
            work.calculate_opds_entries(verbose=False)

        return work
コード例 #17
0
    def _work(self,
              title=None,
              authors=None,
              genre=None,
              language=None,
              audience=None,
              fiction=True,
              with_license_pool=False,
              with_open_access_download=False,
              quality=0.5,
              series=None,
              presentation_edition=None,
              collection=None):
        pool = None
        if with_open_access_download:
            with_license_pool = True
        language = language or "eng"
        title = unicode(title or self._str)
        audience = audience or Classifier.AUDIENCE_ADULT
        if audience == Classifier.AUDIENCE_CHILDREN:
            # TODO: This is necessary because Gutenberg's childrens books
            # get filtered out at the moment.
            data_source_name = DataSource.OVERDRIVE
        else:
            data_source_name = DataSource.GUTENBERG
        if fiction is None:
            fiction = True
        new_edition = False
        if not presentation_edition:
            new_edition = True
            presentation_edition = self._edition(
                title=title,
                language=language,
                authors=authors,
                with_license_pool=with_license_pool,
                with_open_access_download=with_open_access_download,
                data_source_name=data_source_name,
                series=series,
                collection=collection,
            )
            if with_license_pool:
                presentation_edition, pool = presentation_edition
        else:
            pool = presentation_edition.license_pool
        if new_edition:
            presentation_edition.calculate_presentation()
        work, ignore = get_one_or_create(self._db,
                                         Work,
                                         create_method_kwargs=dict(
                                             audience=audience,
                                             fiction=fiction,
                                             quality=quality),
                                         id=self._id)
        if genre:
            if not isinstance(genre, Genre):
                genre, ignore = Genre.lookup(self._db, genre, autocreate=True)
            work.genres = [genre]
        work.random = 0.5

        work.set_presentation_edition(presentation_edition)
        work.calculate_presentation_edition()

        if pool != None:
            # make sure the pool's presentation_edition is set,
            # bc loan tests assume that.
            if not work.license_pools:
                work.license_pools.append(pool)

            pool.set_presentation_edition()

            # This is probably going to be used in an OPDS feed, so
            # fake that the work is presentation ready.
            work.presentation_ready = True
            work.calculate_opds_entries(verbose=False)

        if with_open_access_download:
            pool.open_access = True

        return work
コード例 #18
0
ファイル: test_lane.py プロジェクト: datalogics/server_core
    def setup(self):
        super(TestLanesQuery, self).setup()

        # Look up the Fantasy genre and some of its subgenres.
        self.fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        self.epic_fantasy, ig = Genre.lookup(self._db, classifier.Epic_Fantasy)
        self.urban_fantasy, ig = Genre.lookup(self._db,
                                              classifier.Urban_Fantasy)

        # Look up the History genre and some of its subgenres.
        self.history, ig = Genre.lookup(self._db, classifier.History)
        self.african_history, ig = Genre.lookup(self._db,
                                                classifier.African_History)

        self.adult_works = {}
        self.ya_works = {}
        self.childrens_works = {}

        for genre in (self.fantasy, self.epic_fantasy, self.urban_fantasy,
                      self.history, self.african_history):
            fiction = True
            if genre in (self.history, self.african_history):
                fiction = False

            # Create a number of books for each genre.
            adult_work = self._work(
                title="%s Adult" % genre.name,
                audience=Lane.AUDIENCE_ADULT,
                fiction=fiction,
                with_license_pool=True,
                genre=genre,
            )
            self.adult_works[genre] = adult_work
            adult_work.simple_opds_entry = '<entry>'

            # Childrens and YA books need to be attached to a data
            # source other than Gutenberg, or they'll get filtered
            # out.
            ya_edition, lp = self._edition(
                title="%s YA" % genre.name,
                data_source_name=DataSource.OVERDRIVE,
                with_license_pool=True)
            ya_work = self._work(
                audience=Lane.AUDIENCE_YOUNG_ADULT,
                fiction=fiction,
                with_license_pool=True,
                presentation_edition=ya_edition,
                genre=genre,
            )
            self.ya_works[genre] = ya_work
            ya_work.simple_opds_entry = '<entry>'

            childrens_edition, lp = self._edition(
                title="%s Childrens" % genre.name,
                data_source_name=DataSource.OVERDRIVE,
                with_license_pool=True)
            childrens_work = self._work(
                audience=Lane.AUDIENCE_CHILDREN,
                fiction=fiction,
                with_license_pool=True,
                presentation_edition=childrens_edition,
                genre=genre,
            )
            if genre == self.epic_fantasy:
                childrens_work.target_age = NumericRange(7, 9, '[]')
            else:
                childrens_work.target_age = NumericRange(8, 10, '[]')
            self.childrens_works[genre] = childrens_work
            childrens_work.simple_opds_entry = '<entry>'

        # Create generic 'Adults Only' fiction and nonfiction books
        # that are not in any genre.
        self.nonfiction = self._work(title="Generic Nonfiction",
                                     fiction=False,
                                     audience=Lane.AUDIENCE_ADULTS_ONLY,
                                     with_license_pool=True)
        self.nonfiction.simple_opds_entry = '<entry>'
        self.fiction = self._work(title="Generic Fiction",
                                  fiction=True,
                                  audience=Lane.AUDIENCE_ADULTS_ONLY,
                                  with_license_pool=True)
        self.fiction.simple_opds_entry = '<entry>'

        # Create a work of music.
        self.music = self._work(
            title="Music",
            fiction=False,
            audience=Lane.AUDIENCE_ADULT,
            with_license_pool=True,
        )
        self.music.presentation_edition.medium = Edition.MUSIC_MEDIUM
        self.music.simple_opds_entry = '<entry>'

        # Create a Spanish book.
        self.spanish = self._work(title="Spanish book",
                                  fiction=True,
                                  audience=Lane.AUDIENCE_ADULT,
                                  with_license_pool=True,
                                  language='spa')
        self.spanish.simple_opds_entry = '<entry>'

        # Refresh the materialized views so that all these books are present
        # in them.
        SessionManager.refresh_materialized_views(self._db)
コード例 #19
0
    def _work(self, title=None, authors=None, genre=None, language=None,
              audience=None, fiction=True, with_license_pool=False,
              with_open_access_download=False, quality=0.5, series=None,
              presentation_edition=None, collection=None, data_source_name=None):
        """Create a Work.

        For performance reasons, this method does not generate OPDS
        entries or calculate a presentation edition for the new
        Work. Tests that rely on this information being present
        should call _slow_work() instead, which takes more care to present
        the sort of Work that would be created in a real environment.
        """
        pools = []
        if with_open_access_download:
            with_license_pool = True
        language = language or "eng"
        title = unicode(title or self._str)
        audience = audience or Classifier.AUDIENCE_ADULT
        if audience == Classifier.AUDIENCE_CHILDREN and not data_source_name:
            # TODO: This is necessary because Gutenberg's childrens books
            # get filtered out at the moment.
            data_source_name = DataSource.OVERDRIVE
        elif not data_source_name:
            data_source_name = DataSource.GUTENBERG
        if fiction is None:
            fiction = True
        new_edition = False
        if not presentation_edition:
            new_edition = True
            presentation_edition = self._edition(
                title=title, language=language,
                authors=authors,
                with_license_pool=with_license_pool,
                with_open_access_download=with_open_access_download,
                data_source_name=data_source_name,
                series=series,
                collection=collection,
            )
            if with_license_pool:
                presentation_edition, pool = presentation_edition
                if with_open_access_download:
                    pool.open_access = True
                pools = [pool]
        else:
            pools = presentation_edition.license_pools
        work, ignore = get_one_or_create(
            self._db, Work, create_method_kwargs=dict(
                audience=audience,
                fiction=fiction,
                quality=quality), id=self._id)
        if genre:
            if not isinstance(genre, Genre):
                genre, ignore = Genre.lookup(self._db, genre, autocreate=True)
            work.genres = [genre]
        work.random = 0.5
        work.set_presentation_edition(presentation_edition)

        if pools:
            # make sure the pool's presentation_edition is set,
            # bc loan tests assume that.
            if not work.license_pools:
                for pool in pools:
                    work.license_pools.append(pool)

            for pool in pools:
                pool.set_presentation_edition()

            # This is probably going to be used in an OPDS feed, so
            # fake that the work is presentation ready.
            work.presentation_ready = True
            work.calculate_opds_entries(verbose=False)

        return work