Exemplo n.º 1
0
 def setup(self):
     super(TestEnkiAPI, self).setup()
     self.collection = self._collection(protocol=EnkiAPI.ENKI)
     self.api = MockEnkiAPI(self._db)
Exemplo n.º 2
0
class TestEnkiAPI(DatabaseTest, BaseEnkiTest):
    def setup(self):
        super(TestEnkiAPI, self).setup()
        self.collection = self._collection(protocol=EnkiAPI.ENKI)
        self.api = MockEnkiAPI(self._db)

    def test_create_identifier_strings(self):
        identifier = self._identifier(identifier_type=Identifier.ENKI_ID)
        values = EnkiAPI.create_identifier_strings(["foo", identifier])
        eq_(["foo", identifier.identifier], values)

    def test_import_instantiation(self):
        """Test that EnkiImport can be instantiated"""
        imp = EnkiImport(self._db,
                         self.collection,
                         api_class=self.api.__class__)
        assert_not_equal(None, imp)

    def test_fulfillment_open_access(self):
        """Test that fulfillment info for non-ACS Enki books is parsed correctly."""
        data = self.get_data("checked_out_direct.json")
        self.api.queue_response(200, content=data)
        result = json.loads(data)
        fulfill_data = self.api.parse_fulfill_result(result['result'])
        eq_(
            fulfill_data[0],
            """http://cccl.enkilibrary.org/API/UserAPI?method=downloadEContentFile&username=21901000008080&password=deng&lib=1&recordId=2"""
        )
        eq_(fulfill_data[1], 'epub')

    def test_fulfillment_acs(self):
        """Test that fulfillment info for ACS Enki books is parsed correctly."""
        data = self.get_data("checked_out_acs.json")
        self.api.queue_response(200, content=data)
        result = json.loads(data)
        fulfill_data = self.api.parse_fulfill_result(result['result'])
        eq_(
            fulfill_data[0],
            """http://afs.enkilibrary.org/fulfillment/URLLink.acsm?action=enterloan&ordersource=Califa&orderid=ACS4-9243146841581187248119581&resid=urn%3Auuid%3Ad5f54da9-8177-43de-a53d-ef521bc113b4&gbauthdate=Wed%2C+23+Aug+2017+19%3A42%3A35+%2B0000&dateval=1503517355&rights=%24lat%231505331755%24&gblver=4&auth=8604f0fc3f014365ea8d3c4198c721ed7ed2c16d"""
        )
        eq_(fulfill_data[1], 'epub')

    def test_checkout_open_access(self):
        """Test that checkout info for non-ACS Enki books is parsed correctly."""
        data = self.get_data("checked_out_direct.json")
        self.api.queue_response(200, content=data)
        result = json.loads(data)
        loan = self.api.parse_patron_loans(
            result['result']['checkedOutItems'][0])
        eq_(loan.data_source_name, DataSource.ENKI)
        eq_(loan.identifier_type, Identifier.ENKI_ID)
        eq_(loan.identifier, "econtentRecord2")
        eq_(loan.start_date, datetime.datetime(2017, 8, 23, 19, 31, 58, 0))
        eq_(loan.end_date, datetime.datetime(2017, 9, 13, 19, 31, 58, 0))

    def test_checkout_acs(self):
        """Test that checkout info for ACS Enki books is parsed correctly."""
        data = self.get_data("checked_out_acs.json")
        self.api.queue_response(200, content=data)
        result = json.loads(data)
        loan = self.api.parse_patron_loans(
            result['result']['checkedOutItems'][0])
        eq_(loan.data_source_name, DataSource.ENKI)
        eq_(loan.identifier_type, Identifier.ENKI_ID)
        eq_(loan.identifier, "econtentRecord3334")
        eq_(loan.start_date, datetime.datetime(2017, 8, 23, 19, 42, 35, 0))
        eq_(loan.end_date, datetime.datetime(2017, 9, 13, 19, 42, 35, 0))

    @raises(AuthorizationFailedException)
    def test_login_fail(self):
        """Test that the correct exception is thrown upon an unsuccessful login."""
        data = self.get_data("login_unsuccessful.json")
        self.api.queue_response(200, content=data)
        result = json.loads(data)

        edition, pool = self._edition(identifier_type=Identifier.ENKI_ID,
                                      data_source_name=DataSource.ENKI,
                                      with_license_pool=True)
        pool.identifier.identifier = 'notanid'

        patron = self._patron(external_identifier='notabarcode')

        loan = self.api.checkout(patron, 'notapin', pool, None)

    @raises(NoAvailableCopies)
    def test_login_fail(self):
        """Test that the correct exception is thrown upon an unsuccessful login."""
        data = self.get_data("no_copies.json")
        self.api.queue_response(200, content=data)
        result = json.loads(data)

        edition, pool = self._edition(identifier_type=Identifier.ENKI_ID,
                                      data_source_name=DataSource.ENKI,
                                      with_license_pool=True)
        pool.identifier.identifier = 'econtentRecord1'
        patron = self._patron(external_identifier='12345678901234')

        loan = self.api.checkout(patron, '1234', pool, None)
Exemplo n.º 3
0
 def setup(self):
     super(BaseEnkiTest, self).setup()
     self.api = MockEnkiAPI(self._db)
     self.collection = self.api.collection
Exemplo n.º 4
0
    def test_update_circulation(self):

        # Here's information about a book we didn't know about before.
        circ_data = {
            "result": {
                "records":
                1,
                "recentactivity": [{
                    "historyid": "3738",
                    "id": "34278",
                    "recordId": "econtentRecord34278",
                    "time": "2018-06-26 10:08:23",
                    "action": "Checked Out",
                    "isbn": "9781618856050",
                    "availability": {
                        "accessType": "acs",
                        "totalCopies": "1",
                        "availableCopies": 0,
                        "onHold": 0
                    }
                }]
            }
        }

        # Because the book is unknown, update_circulation will do a follow-up
        # call to api.get_item to get bibliographic information.
        bib_data = {
            "result": {
                "id": "34278",
                "recordId": "econtentRecord34278",
                "isbn": "9781618856050",
                "title": "A book",
                "availability": {
                    "accessType": "acs",
                    "totalCopies": "1",
                    "availableCopies": 0,
                    "onHold": 0
                }
            }
        }

        api = MockEnkiAPI(self._db)
        api.queue_response(200, content=json.dumps(circ_data))
        api.queue_response(200, content=json.dumps(bib_data))

        from core.mock_analytics_provider import MockAnalyticsProvider
        analytics = MockAnalyticsProvider()
        monitor = EnkiImport(self._db,
                             self.collection,
                             api_class=api,
                             analytics=analytics)
        since = datetime.datetime.utcnow()
        monitor.update_circulation(since)

        # Two requests were made -- one to getRecentActivity
        # and one to getItem.
        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        eq_('get', method)
        eq_('https://enkilibrary.org/API/ItemAPI', url)
        eq_('getRecentActivity', params['method'])
        eq_(0, params['minutes'])

        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        eq_('get', method)
        eq_('https://enkilibrary.org/API/ItemAPI', url)
        eq_('getItem', params['method'])
        eq_('34278', params['recordid'])

        # We ended up with one Work, one LicensePool, and one Edition.
        work = self._db.query(Work).one()
        licensepool = self._db.query(LicensePool).one()
        edition = self._db.query(Edition).one()
        eq_([licensepool], work.license_pools)
        eq_(edition, licensepool.presentation_edition)

        identifier = licensepool.identifier
        eq_(Identifier.ENKI_ID, identifier.type)
        eq_(u"34278", identifier.identifier)

        # The LicensePool and Edition take their data from the mock API
        # requests.
        eq_("A book", work.title)
        eq_(1, licensepool.licenses_owned)
        eq_(0, licensepool.licenses_available)

        # An analytics event was sent out for the newly discovered book.
        eq_(1, analytics.count)

        # Now let's see what update_circulation does when the work
        # already exists.
        circ_data['result']['recentactivity'][0]['availability'][
            'totalCopies'] = 10
        api.queue_response(200, content=json.dumps(circ_data))
        # We're not queuing up more bib data, but that's no problem --
        # EnkiImport won't ask for it.

        # Pump the monitor again.
        monitor.update_circulation(since)

        # We made a single request, to getRecentActivity.
        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        eq_('getRecentActivity', params['method'])

        # The LicensePool was updated, but no new objects were created.
        eq_(10, licensepool.licenses_owned)
        for c in (LicensePool, Edition, Work):
            eq_(1, self._db.query(c).count())
Exemplo n.º 5
0
    def test__update_circulation(self):

        # Here's information about a book we didn't know about before.
        circ_data = {
            "result": {
                "records":
                1,
                "recentactivity": [{
                    "historyid": "3738",
                    "id": "34278",
                    "recordId": "econtentRecord34278",
                    "time": "2018-06-26 10:08:23",
                    "action": "Checked Out",
                    "isbn": "9781618856050",
                    "availability": {
                        "accessType": "acs",
                        "totalCopies": "1",
                        "availableCopies": 0,
                        "onHold": 0,
                    },
                }],
            }
        }

        # Because the book is unknown, update_circulation will do a follow-up
        # call to api.get_item to get bibliographic information.
        bib_data = {
            "result": {
                "id": "34278",
                "recordId": "econtentRecord34278",
                "isbn": "9781618856050",
                "title": "A book",
                "availability": {
                    "accessType": "acs",
                    "totalCopies": "1",
                    "availableCopies": 0,
                    "onHold": 0,
                },
            }
        }

        api = MockEnkiAPI(self._db)
        api.queue_response(200, content=json.dumps(circ_data))
        api.queue_response(200, content=json.dumps(bib_data))

        from core.mock_analytics_provider import MockAnalyticsProvider

        analytics = MockAnalyticsProvider()
        monitor = EnkiImport(self._db,
                             self.collection,
                             api_class=api,
                             analytics=analytics)
        end = utc_now()

        # Ask for circulation events from one hour in 1970.
        start = datetime_utc(1970, 1, 1, 0, 0, 0)
        end = datetime_utc(1970, 1, 1, 1, 0, 0)
        monitor._update_circulation(start, end)

        # Two requests were made -- one to getRecentActivityTime
        # and one to getItem.
        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        assert "get" == method
        assert "https://enkilibrary.org/API/ItemAPI" == url
        assert "getRecentActivityTime" == params["method"]

        # The parameters passed to getRecentActivityTime show the
        # start and end points of the request as seconds since the
        # epoch.
        assert "0" == params["stime"]
        assert "3600" == params["etime"]

        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        assert "get" == method
        assert "https://enkilibrary.org/API/ItemAPI" == url
        assert "getItem" == params["method"]
        assert "34278" == params["recordid"]

        # We ended up with one Work, one LicensePool, and one Edition.
        work = self._db.query(Work).one()
        licensepool = self._db.query(LicensePool).one()
        edition = self._db.query(Edition).one()
        assert [licensepool] == work.license_pools
        assert edition == licensepool.presentation_edition

        identifier = licensepool.identifier
        assert Identifier.ENKI_ID == identifier.type
        assert "34278" == identifier.identifier

        # The LicensePool and Edition take their data from the mock API
        # requests.
        assert "A book" == work.title
        assert 1 == licensepool.licenses_owned
        assert 0 == licensepool.licenses_available

        # An analytics event was sent out for the newly discovered book.
        assert 1 == analytics.count

        # Now let's see what update_circulation does when the work
        # already exists.
        circ_data["result"]["recentactivity"][0]["availability"][
            "totalCopies"] = 10
        api.queue_response(200, content=json.dumps(circ_data))
        # We're not queuing up more bib data, but that's no problem --
        # EnkiImport won't ask for it.

        # Pump the monitor again.
        monitor._update_circulation(start, end)

        # We made a single request, to getRecentActivityTime.
        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        assert "getRecentActivityTime" == params["method"]

        # The LicensePool was updated, but no new objects were created.
        assert 10 == licensepool.licenses_owned
        for c in (LicensePool, Edition, Work):
            assert 1 == self._db.query(c).count()
Exemplo n.º 6
0
    def test_update_circulation(self):

        # Here's information about a book we didn't know about before.
        circ_data = {"result":{"records":1,"recentactivity":[{"historyid":"3738","id":"34278","recordId":"econtentRecord34278","time":"2018-06-26 10:08:23","action":"Checked Out","isbn":"9781618856050","availability":{"accessType":"acs","totalCopies":"1","availableCopies":0,"onHold":0}}]}}

        # Because the book is unknown, update_circulation will do a follow-up
        # call to api.get_item to get bibliographic information.
        bib_data = {"result":{"id":"34278","recordId":"econtentRecord34278","isbn":"9781618856050","title":"A book","availability":{"accessType":"acs","totalCopies":"1","availableCopies":0,"onHold":0}}}

        api = MockEnkiAPI(self._db)
        api.queue_response(200, content=json.dumps(circ_data))
        api.queue_response(200, content=json.dumps(bib_data))

        from core.mock_analytics_provider import MockAnalyticsProvider
        analytics = MockAnalyticsProvider()
        monitor = EnkiImport(self._db, self.collection, api_class=api,
                             analytics=analytics)
        since = datetime.datetime.utcnow()
        monitor.update_circulation(since)

        # Two requests were made -- one to getRecentActivity
        # and one to getItem.
        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        eq_('get', method)
        eq_('https://enkilibrary.org/API/ItemAPI', url)
        eq_('getRecentActivity', params['method'])
        eq_(0, params['minutes'])

        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        eq_('get', method)
        eq_('https://enkilibrary.org/API/ItemAPI', url)
        eq_('getItem', params['method'])
        eq_('34278', params['recordid'])

        # We ended up with one Work, one LicensePool, and one Edition.
        work = self._db.query(Work).one()
        licensepool = self._db.query(LicensePool).one()
        edition = self._db.query(Edition).one()
        eq_([licensepool], work.license_pools)
        eq_(edition, licensepool.presentation_edition)

        identifier = licensepool.identifier
        eq_(Identifier.ENKI_ID, identifier.type)
        eq_(u"34278", identifier.identifier)

        # The LicensePool and Edition take their data from the mock API
        # requests.
        eq_("A book", work.title)
        eq_(1, licensepool.licenses_owned)
        eq_(0, licensepool.licenses_available)

        # An analytics event was sent out for the newly discovered book.
        eq_(1, analytics.count)

        # Now let's see what update_circulation does when the work
        # already exists.
        circ_data['result']['recentactivity'][0]['availability']['totalCopies'] = 10
        api.queue_response(200, content=json.dumps(circ_data))
        # We're not queuing up more bib data, but that's no problem --
        # EnkiImport won't ask for it.

        # Pump the monitor again.
        monitor.update_circulation(since)

        # We made a single request, to getRecentActivity.
        [method, url, headers, data, params, kwargs] = api.requests.pop(0)
        eq_('getRecentActivity', params['method'])

        # The LicensePool was updated, but no new objects were created.
        eq_(10, licensepool.licenses_owned)
        for c in (LicensePool, Edition, Work):
            eq_(1, self._db.query(c).count())