コード例 #1
0
    def test_all_then_first(self, mock_search):
        """Verify that calling first on a ResourceMatcher that has already been called with .all() doesn't fail."""
        # Set up mock response.
        mock_search.return_value = {
            "pagination": {
                "totalItems": 2
            },
            "list": [{
                "id": 1,
                "name": "test1"
            }, {
                "id": 2,
                "name": "test2"
            }],
        }

        # Run a search and try pulling records.
        results = matching.ResourceMatcher(models.Contributor,
                                           items_per_page=10)
        all_results = results.all()
        first_result = results.first()

        # Check expected results.
        for result in all_results:
            assert isinstance(result, models.Contributor)

        assert isinstance(first_result, models.Contributor)
コード例 #2
0
    def test_all(self, mock_search):
        # Set up mock response.
        mock_search.return_value = {
            "pagination": {
                "totalItems": 2
            },
            "list": [{
                "id": 1,
                "name": "test1"
            }, {
                "id": 2,
                "name": "test2"
            }],
        }

        # Run Search.
        results = matching.ResourceMatcher(models.Contributor,
                                           items_per_page=10).all()
        results = list(results)

        # Check results are expected type.
        for result in results:
            assert isinstance(result, models.Contributor)

        # Check result list is expected length.
        assert len(results) == 2
コード例 #3
0
    def match(cls, **kwargs) -> matching.ResourceMatcher:
        """Find a resource using passed keyword arguments.

        Note:
            If called without arguments, returns all records in the DA .
        """

        # Check that we no invalid search terms were passed.
        for key in kwargs:
            if key not in cls.__fields__.keys():
                raise exceptions.InvalidSearchFieldError

        # Prepare the "term" search field.
        # If we've got both a name and a value, join them.
        if kwargs.get("name") and kwargs.get("value"):
            kwargs["term"] = " ".join(
                [kwargs.pop("name"), kwargs.pop("value")])

        # Otherwise, treat the one that exists as the term.
        elif kwargs.get("name"):
            kwargs["term"] = kwargs.pop("name")
        elif kwargs.get("value"):
            kwargs["term"] = kwargs.pop("value")

        return matching.ResourceMatcher(cls, **kwargs)
コード例 #4
0
 def test_repr(self, mock_get_by_id):
     # Run match
     mock_get_by_id.return_value = {"list": [{"id": 1}]}
     test_match = matching.ResourceMatcher(models.Resource, id=1)
     assert (
         str(test_match) ==
         "ResourceMatcher(model=<class 'digitalarchive.models.Resource'>, query={'id': 1}, count=1)"
     )
コード例 #5
0
    def test_match_id_field(self, mock_get_by_id):
        """Test Search API called with proper params."""
        # Run match
        mock_get_by_id.return_value = {"list": [{"id": 1}]}
        test_match = matching.ResourceMatcher(models.Resource, id=1)

        # Check search called with proper params.
        mock_get_by_id.assert_called_once()

        # Inspect list for proper data
        match_results = list(test_match.list)
        assert len(match_results) == 1
        assert isinstance(match_results[0], models.Resource)
コード例 #6
0
    def test_match_record_by_id(self, mock_api_get):
        # pylint: disable=protected-access

        # instantiate matcher and reset mock, them run just the method.
        test_matcher = matching.ResourceMatcher(models.Publisher, id=1)
        mock_api_get.reset_mock()

        # Explicitly call record ID method
        test_response = test_matcher._record_by_id()

        # Check api called with correct params.
        mock_api_get.assert_called_with(endpoint=models.Publisher.endpoint,
                                        resource_id=1)

        # check response is correct format
        assert test_response["list"][0] is mock_api_get()
コード例 #7
0
 def test_match_by_keyword_single_page(self, mock_search):
     # Set up mock response.
     mock_search.return_value = {
         "pagination": {
             "totalItems": 1
         },
         "list": [unittest.mock.MagicMock()],
     }
     matching.ResourceMatcher(models.Publisher, name="test_name")
     mock_search.assert_called_with(
         model=models.Publisher.endpoint,
         params={
             "name": "test_name",
             "itemsPerPage": 200
         },
     )
コード例 #8
0
    def test_match_by_keyword_multi_page(self, mock_search, mock_get_all):
        # Set up mock response.
        mock_search.return_value = {
            "pagination": {
                "totalItems": 2
            },
            "list": [unittest.mock.MagicMock(),
                     unittest.mock.MagicMock()],
        }

        # Run Search.
        matching.ResourceMatcher(models.Publisher,
                                 items_per_page=1,
                                 name="test_name")

        # Verify generator function called.
        mock_get_all.assert_called_with(mock_search())
コード例 #9
0
    def test_get_all_search_results(self, mock_search):
        # Set up mock response.
        results_page_1 = {
            "pagination": {
                "page": 1,
                "totalPages": 2,
                "totalItems": 2
            },
            "list": [{
                "id": 1,
                "name": "test1",
                "slug": "testslug"
            }],
        }

        results_page_2 = {
            "pagination": {
                "page": 2,
                "totalPages": 2,
                "totalItems": 2
            },
            "list": [{
                "id": 2,
                "name": "test2",
                "slug": "testslug"
            }],
        }
        mock_search.side_effect = [
            results_page_1, results_page_1, results_page_2
        ]

        test_matcher = matching.ResourceMatcher(models.Collection,
                                                items_per_page=1,
                                                name="test_name")
        mock_search.reset_mock()
        mock_search.side_effect = [results_page_1, results_page_2]

        # Trigger function under test.
        results = list(test_matcher.list)

        # Check result is as intended
        assert len(results) == 2
        for result in results:
            assert isinstance(result, models.Collection)
コード例 #10
0
    def test_subject_pagination(self, mock_api_search):

        mock_api_search.return_value = {
            "pagination": {
                "totalItems": 2
            },
            "list": [unittest.mock.MagicMock(),
                     unittest.mock.MagicMock()],
        }

        test_match = matching.ResourceMatcher(models.Subject,
                                              name="test_name",
                                              items_per_page=1)

        # Confirm the list attribute is properly generated.
        try:
            test_list = test_match.list
        except AttributeError:
            self.fail()
コード例 #11
0
    def test_first(self, mock_get_by_id):
        # Run match
        mock_get_by_id.return_value = {"list": [{"id": 1}]}
        test_match = matching.ResourceMatcher(models.Resource, id=1).first()

        assert isinstance(test_match, models.Resource)
コード例 #12
0
    def match(cls, **kwargs) -> matching.ResourceMatcher:
        """
        Search for a Document by keyword, or fetch one by ID.

        Matching on the Document model runs a full-text search using keywords passed via the  title and description
        keywords. Results can also be limited by dates or by related records, as described below.

        Note:
            Title and description keywords are not searched for individually. All
            non-date or child record searches are concatenated to single querystring.

        Note:
            Collection and other related record searches use `INNER JOIN` logic when
            passed multiple related resources.

        **Allowed search fields:**

        Args:
            title (:obj:`str`, optional): Title search keywords.
            description (:obj:`str`, optional): Title search keywords.
            start_date (:class:`datetime.date`, optional): Return only Documents with a `doc_date` after the passed
                `start_date`.
            end_date (:class:`datetime.date`, optional): Return only Documents with a `doc_date` before the passed
                `end_date`.
            collections (:obj:`list` of :class:`digitalarchive.models.Collection`, optional): Restrict results to
                Documents contained in all of the passed Collections.
            publishers (:obj:`list` of :class:`digitalarchive.models.Publisher`, optional): Restrict results to
                Documents published by all of the passed Publishers.
            repositories (:obj:`list` of :class:`digitalarchive.models.Repository`, optional) Restrict results to
                Documents contained in all of the passed Repositories.
            coverages (:obj:`list` of :class:`digitalarchive.models.Coverage`, optional) Restrict results to Documents
                relating to all of the passed geographical Coverages.
            subjects (:obj:`list` of :class:`digitalarchive.models.Subject`) Restrict results to Documents tagged with
                all of the passed subjects
            contributors (:obj:`list of :class:`digitalarchive.models.Contributor`) Restrict results to Documents whose
                authors include all of the passed contributors.
            donors (list(:class:`digitalarchive.models.Donor`)) Restrict results to Documents who were obtained or
                translated with support from all of the passed donors.
            languages (:class:`digitalarchive.models.Language` or str) Restrict results to Documents by language of
                original document. If passing a string, you must pass an ISO 639-2/B language code.
            translation (:class:`digitalarchive.models.Translation`) Restrict results to Documents for which there
                is a translation available in the passed Language.
            theme (:class:`digitalarchive.models.Theme`) Restrict results to Documents belonging to the passed Theme.

        Returns:
            An instance of (:class:`digitalarchive.matching.ResourceMatcher`) containing any records responsive to the
                search.
        """
        # Limit search to only Documents (this excludes Collections from search result).
        kwargs["model"] = "Record"

        # Check that search keywords are valid.
        allowed_search_fields = [
            *cls.__fields__.keys(),
            "start_date",
            "end_date",
            "themes",
            "model",
        ]
        for key in kwargs:
            if key not in allowed_search_fields:
                logging.error(
                    f"[!] {key} is not a valid search term for {cls}. Valid terms: {allowed_search_fields}"
                )
                raise exceptions.InvalidSearchFieldError

        # Process date searches if they are present.
        if any(key in kwargs.keys() for key in ["start_date", "end_date"]):
            kwargs = Document._process_date_searches(kwargs)

        # Process language searches if they are present.
        if "languages" in kwargs.keys():
            kwargs = Document._process_language_search(kwargs)

        # Process any related model searches.
        if any(key in kwargs.keys() for key in [
                "collections",
                "publishers",
                "repositories",
                "original_coverages",
                "subjects",
                "contributors",
                "donors",
                "languages",
                "translations",
                "themes",
        ]):
            kwargs = Document._process_related_model_searches(kwargs)

        # Prepare the 'q' fulltext search field.
        keywords = []
        for field in ["name", "title", "description", "slug", "q"]:
            if kwargs.get(field) is not None:
                keywords.append(kwargs.pop(field))
        kwargs["q"] = " ".join(keywords)

        # Reformat fields that accept lists. This makes the queries inner joins rather than union all.
        for field in [
                "donor", "subject", "contributor", "coverage", "collection"
        ]:
            if field in kwargs.keys():
                kwargs[f"{field}[]"] = kwargs.pop(field)

        # Run the match.
        return matching.ResourceMatcher(cls, **kwargs)