def test_parse_treats_duplicates_as_interchangeable(self):
        self.pool.loan_to(self.patron)

        # Due to an earlier race condition, two duplicate annotations
        # were put in the database.
        a1, ignore = create(
            self._db, Annotation,
            patron_id=self.patron.id,
            identifier_id=self.identifier.id,
            motivation=Annotation.IDLING,
        )

        a2, ignore = create(
            self._db, Annotation,
            patron_id=self.patron.id,
            identifier_id=self.identifier.id,
            motivation=Annotation.IDLING,
        )

        assert a1 != a2

        # Parsing the annotation again retrieves one or the other
        # of the annotations rather than crashing or creating a third
        # annotation.
        data = self._sample_jsonld()
        data = json.dumps(data)
        annotation = AnnotationParser.parse(self._db, data, self.patron)
        assert annotation in (a1, a2)
    def test_parse_treats_duplicates_as_interchangeable(self):
        self.pool.loan_to(self.patron)

        # Due to an earlier race condition, two duplicate annotations
        # were put in the database.
        a1, ignore = create(
            self._db, Annotation,
            patron_id=self.patron.id,
            identifier_id=self.identifier.id,
            motivation=Annotation.IDLING,
        )

        a2, ignore = create(
            self._db, Annotation,
            patron_id=self.patron.id,
            identifier_id=self.identifier.id,
            motivation=Annotation.IDLING,
        )

        assert a1 != a2

        # Parsing the annotation again retrieves one or the other
        # of the annotations rather than crashing or creating a third
        # annotation.
        data = self._sample_jsonld()
        data = json.dumps(data)
        annotation = AnnotationParser.parse(self._db, data, self.patron)
        assert annotation in (a1, a2)
    def test_parse_compacted_jsonld(self):
        self.pool.loan_to(self.patron)

        data = dict()
        data["@type"] = "http://www.w3.org/ns/oa#Annotation"
        data["http://www.w3.org/ns/oa#motivatedBy"] = {
            "@id": Annotation.IDLING
        }
        data["http://www.w3.org/ns/oa#hasBody"] = {
            "@type": "http://www.w3.org/ns/oa#TextualBody",
            "http://www.w3.org/ns/oa#bodyValue": "A good description of the topic that bears further investigation",
            "http://www.w3.org/ns/oa#hasPurpose": {
                "@id": "http://www.w3.org/ns/oa#describing"
            }
        }
        data["http://www.w3.org/ns/oa#hasTarget"] = {
            "http://www.w3.org/ns/oa#hasSource": {
                "@id": self.identifier.urn
            },
            "http://www.w3.org/ns/oa#hasSelector": {
                "@type": "http://www.w3.org/ns/oa#FragmentSelector",
                "http://www.w3.org/1999/02/22-rdf-syntax-ns#value": "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/3:10)"
            }
        }

        data_json = json.dumps(data)
        expanded = jsonld.expand(data)[0]

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)
        eq_(self.patron.id, annotation.patron_id)
        eq_(self.identifier.id, annotation.identifier_id)
        eq_(Annotation.IDLING, annotation.motivation)
        eq_(True, annotation.active)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasTarget"][0]), annotation.target)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasBody"][0]), annotation.content)
    def test_parse_compacted_jsonld(self):
        self.pool.loan_to(self.patron)

        data = dict()
        data["@type"] = "http://www.w3.org/ns/oa#Annotation"
        data["http://www.w3.org/ns/oa#motivatedBy"] = {
            "@id": Annotation.IDLING
        }
        data["http://www.w3.org/ns/oa#hasBody"] = {
            "@type": "http://www.w3.org/ns/oa#TextualBody",
            "http://www.w3.org/ns/oa#bodyValue": "A good description of the topic that bears further investigation",
            "http://www.w3.org/ns/oa#hasPurpose": {
                "@id": "http://www.w3.org/ns/oa#describing"
            }
        }
        data["http://www.w3.org/ns/oa#hasTarget"] = {
            "http://www.w3.org/ns/oa#hasSource": {
                "@id": self.identifier.urn
            },
            "http://www.w3.org/ns/oa#hasSelector": {
                "@type": "http://www.w3.org/ns/oa#FragmentSelector",
                "http://www.w3.org/1999/02/22-rdf-syntax-ns#value": "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/3:10)"
            }
        }

        data_json = json.dumps(data)
        expanded = jsonld.expand(data)[0]

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)
        eq_(self.patron.id, annotation.patron_id)
        eq_(self.identifier.id, annotation.identifier_id)
        eq_(Annotation.IDLING, annotation.motivation)
        eq_(True, annotation.active)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasTarget"][0]), annotation.target)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasBody"][0]), annotation.content)
    def test_parse_jsonld_with_no_loan(self):
        data = self._sample_jsonld()
        data_json = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)

        eq_(INVALID_ANNOTATION_TARGET, annotation)
    def test_parse_jsonld_with_no_loan(self):
        data = self._sample_jsonld()
        data_json = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)

        eq_(INVALID_ANNOTATION_TARGET, annotation)
 def test_invalid_identifier(self):
     # If the target source can't be parsed as a URN we send
     # INVALID_ANNOTATION_TARGET
     data = self._sample_jsonld()
     data["target"]["source"] = "not a URN"
     annotation = AnnotationParser.parse(self._db, json.dumps(data),
                                         self.patron)
     assert INVALID_ANNOTATION_TARGET == annotation
    def test_parse_jsonld_with_no_target(self):
        data = self._sample_jsonld()
        del data["target"]
        data_json = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)

        assert INVALID_ANNOTATION_TARGET == annotation
 def test_invalid_identifier(self):
     # If the target source can't be parsed as a URN we send
     # INVALID_ANNOTATION_TARGET
     data = self._sample_jsonld()
     data['target']['source'] = 'not a URN'
     annotation = AnnotationParser.parse(self._db, json.dumps(data),
                                         self.patron)
     eq_(INVALID_ANNOTATION_TARGET, annotation)
    def test_parse_jsonld_with_patron_opt_out(self):
        self.pool.loan_to(self.patron)
        data = self._sample_jsonld()
        data_json = json.dumps(data)

        self.patron.synchronize_annotations = False
        annotation = AnnotationParser.parse(self._db, data_json, self.patron)
        eq_(PATRON_NOT_OPTED_IN_TO_ANNOTATION_SYNC, annotation)
 def test_invalid_identifier(self):
     # If the target source can't be parsed as a URN we send
     # INVALID_ANNOTATION_TARGET
     data = self._sample_jsonld()
     data['target']['source'] = 'not a URN'
     annotation = AnnotationParser.parse(
         self._db, json.dumps(data), self.patron
     )
     eq_(INVALID_ANNOTATION_TARGET, annotation)
    def test_parse_jsonld_with_patron_opt_out(self):
        self.pool.loan_to(self.patron)
        data = self._sample_jsonld()
        data_json = json.dumps(data)

        self.patron.synchronize_annotations=False
        annotation = AnnotationParser.parse(
            self._db, data_json, self.patron
        )
        eq_(PATRON_NOT_OPTED_IN_TO_ANNOTATION_SYNC, annotation)
    def test_parse_jsonld_with_invalid_motivation(self):
        self.pool.loan_to(self.patron)

        data = self._sample_jsonld()
        data["motivation"] = "not-a-valid-motivation"
        data_json = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)

        eq_(INVALID_ANNOTATION_MOTIVATION, annotation)
    def test_parse_jsonld_with_invalid_motivation(self):
        self.pool.loan_to(self.patron)

        data = self._sample_jsonld()
        data["motivation"] = "not-a-valid-motivation"
        data_json = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)

        eq_(INVALID_ANNOTATION_MOTIVATION, annotation)
 def test_null_id(self):
     # A JSON-LD document can have its @id set to null -- it's the
     # same as if the @id wasn't present -- but the jsonld library
     # can't handle this, so we need to test it specially.
     self.pool.loan_to(self.patron)
     data = self._sample_jsonld()
     data['id'] = None
     annotation = AnnotationParser.parse(self._db, json.dumps(data),
                                         self.patron)
     assert isinstance(annotation, Annotation)
 def test_null_id(self):
     # A JSON-LD document can have its @id set to null -- it's the
     # same as if the @id wasn't present -- but the jsonld library
     # can't handle this, so we need to test it specially.
     self.pool.loan_to(self.patron)
     data = self._sample_jsonld()
     data['id'] = None
     annotation = AnnotationParser.parse(
         self._db, json.dumps(data), self.patron
     )
     assert isinstance(annotation, Annotation)
    def test_parse_jsonld_with_bookmarking_motivation(self):
        """You can create multiple bookmarks in a single book."""
        self.pool.loan_to(self.patron)

        data = self._sample_jsonld(motivation=Annotation.BOOKMARKING)
        data_json = json.dumps(data)
        annotation = AnnotationParser.parse(self._db, data_json, self.patron)
        eq_(Annotation.BOOKMARKING, annotation.motivation)

        # You can't create another bookmark at the exact same location --
        # you just get the same annotation again.
        annotation2 = AnnotationParser.parse(self._db, data_json, self.patron)
        eq_(annotation, annotation2)

        # But unlike with IDLING, you _can_ create multiple bookmarks
        # for the same identifier, so long as the selector value
        # (ie. the location within the book) is different.
        data['target']['selector']['value'] = 'epubcfi(/3/4[chap01ref]!/4[body01]/15[para05]/3:10)'
        data_json = json.dumps(data)
        annotation3 = AnnotationParser.parse(self._db, data_json, self.patron)
        assert annotation3 != annotation
        eq_(2, len(self.patron.annotations))
    def test_parse_jsonld_with_bookmarking_motivation(self):
        """You can create multiple bookmarks in a single book."""
        self.pool.loan_to(self.patron)

        data = self._sample_jsonld(motivation=Annotation.BOOKMARKING)
        data_json = json.dumps(data)
        annotation = AnnotationParser.parse(self._db, data_json, self.patron)
        eq_(Annotation.BOOKMARKING, annotation.motivation)

        # You can't create another bookmark at the exact same location --
        # you just get the same annotation again.
        annotation2 = AnnotationParser.parse(self._db, data_json, self.patron)
        eq_(annotation, annotation2)

        # But unlike with IDLING, you _can_ create multiple bookmarks
        # for the same identifier, so long as the selector value
        # (ie. the location within the book) is different.
        data['target']['selector']['value'] = 'epubcfi(/3/4[chap01ref]!/4[body01]/15[para05]/3:10)'
        data_json = json.dumps(data)
        annotation3 = AnnotationParser.parse(self._db, data_json, self.patron)
        assert annotation3 != annotation
        eq_(2, len(self.patron.annotations))
    def test_parse_jsonld_with_context(self):
        self.pool.loan_to(self.patron)

        data = self._sample_jsonld()
        data_json = json.dumps(data)
        expanded = jsonld.expand(data)[0]

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)

        eq_(self.patron.id, annotation.patron_id)
        eq_(self.identifier.id, annotation.identifier_id)
        eq_(Annotation.IDLING, annotation.motivation)
        eq_(True, annotation.active)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasTarget"][0]), annotation.target)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasBody"][0]), annotation.content)
    def test_parse_jsonld_with_context(self):
        self.pool.loan_to(self.patron)

        data = self._sample_jsonld()
        data_json = json.dumps(data)
        expanded = jsonld.expand(data)[0]

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)

        eq_(self.patron.id, annotation.patron_id)
        eq_(self.identifier.id, annotation.identifier_id)
        eq_(Annotation.IDLING, annotation.motivation)
        eq_(True, annotation.active)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasTarget"][0]), annotation.target)
        eq_(json.dumps(expanded["http://www.w3.org/ns/oa#hasBody"][0]), annotation.content)
    def test_parse_expanded_jsonld(self):
        self.pool.loan_to(self.patron)

        data = dict()
        data["@type"] = ["http://www.w3.org/ns/oa#Annotation"]
        data["http://www.w3.org/ns/oa#motivatedBy"] = [{
            "@id": Annotation.IDLING
        }]
        data["http://www.w3.org/ns/oa#hasBody"] = [{
            "@type": ["http://www.w3.org/ns/oa#TextualBody"],
            "http://www.w3.org/ns/oa#bodyValue": [{
                "@value":
                "A good description of the topic that bears further investigation"
            }],
            "http://www.w3.org/ns/oa#hasPurpose": [{
                "@id":
                "http://www.w3.org/ns/oa#describing"
            }],
        }]
        data["http://www.w3.org/ns/oa#hasTarget"] = [{
            "http://www.w3.org/ns/oa#hasSelector": [{
                "@type": ["http://www.w3.org/ns/oa#FragmentSelector"],
                "http://www.w3.org/1999/02/22-rdf-syntax-ns#value": [{
                    "@value":
                    "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/3:10)"
                }],
            }],
            "http://www.w3.org/ns/oa#hasSource": [{
                "@id": self.identifier.urn
            }],
        }]

        data_json = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data_json, self.patron)
        assert self.patron.id == annotation.patron_id
        assert self.identifier.id == annotation.identifier_id
        assert Annotation.IDLING == annotation.motivation
        assert True == annotation.active
        assert (json.dumps(
            data["http://www.w3.org/ns/oa#hasTarget"][0]) == annotation.target)
        assert (json.dumps(
            data["http://www.w3.org/ns/oa#hasBody"][0]) == annotation.content)
    def test_parse_updates_existing_annotation(self):
        self.pool.loan_to(self.patron)

        original_annotation, ignore = create(
            self._db, Annotation,
            patron_id=self.patron.id,
            identifier_id=self.identifier.id,
            motivation=Annotation.IDLING,
        )
        original_annotation.active = False
        yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
        original_annotation.timestamp = yesterday

        data = self._sample_jsonld()
        data = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data, self.patron)

        eq_(original_annotation, annotation)
        eq_(True, annotation.active)
        assert annotation.timestamp > yesterday
    def test_parse_updates_existing_annotation(self):
        self.pool.loan_to(self.patron)

        original_annotation, ignore = create(
            self._db, Annotation,
            patron_id=self.patron.id,
            identifier_id=self.identifier.id,
            motivation=Annotation.IDLING,
        )
        original_annotation.active = False
        yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
        original_annotation.timestamp = yesterday

        data = self._sample_jsonld()
        data = json.dumps(data)

        annotation = AnnotationParser.parse(self._db, data, self.patron)

        eq_(original_annotation, annotation)
        eq_(True, annotation.active)
        assert annotation.timestamp > yesterday
 def test_parse_invalid_json(self):
     annotation = AnnotationParser.parse(self._db, "not json", self.patron)
     eq_(INVALID_ANNOTATION_FORMAT, annotation)
 def test_parse_invalid_json(self):
     annotation = AnnotationParser.parse(self._db, "not json", self.patron)
     eq_(INVALID_ANNOTATION_FORMAT, annotation)