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_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_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_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_invalid_json(self): annotation = AnnotationParser.parse(self._db, "not json", self.patron) eq_(INVALID_ANNOTATION_FORMAT, annotation)