def test_update_from_request(self): # create test note to update note = Annotation(text="Here's the thing", quote="really", extra_data=json.dumps({'sample data': 'foobar'})) note.save() # permissions check requires a real user user = get_user_model().objects.get(username='******') self.mockrequest.user = user with patch.object(note, 'db_permissions') as mock_db_perms: note.update_from_request(self.mockrequest) self.assertEqual(self.annotation_data['text'], note.text) self.assertEqual(self.annotation_data['quote'], note.quote) self.assertEqual(self.annotation_data['uri'], note.uri) self.assert_('ranges' in note.extra_data) self.assertEqual(self.annotation_data['ranges'][0]['start'], note.extra_data['ranges'][0]['start']) self.assert_('permissions' not in note.extra_data) # existing extra data should no longer present self.assert_('sample data' not in note.extra_data) # testuser does not have admin on this annotation; # permissions should not be updated mock_db_perms.assert_not_called() # give user admin permission and update again note.assign_permission('admin_annotation', user) note.update_from_request(self.mockrequest) mock_db_perms.assert_called_with( self.annotation_data['permissions'])
def test_update_from_request(self): # create test note to update note = Annotation(text="Here's the thing", quote="really", extra_data=json.dumps({'sample data': 'foobar'})) note.save() # permissions check requires a real user user = get_user_model().objects.get(username='******') self.mockrequest.user = user with patch.object(note, 'db_permissions') as mock_db_perms: note.update_from_request(self.mockrequest) self.assertEqual(self.annotation_data['text'], note.text) self.assertEqual(self.annotation_data['quote'], note.quote) self.assertEqual(self.annotation_data['uri'], note.uri) self.assert_('ranges' in note.extra_data) self.assertEqual(self.annotation_data['ranges'][0]['start'], note.extra_data['ranges'][0]['start']) self.assert_('permissions' not in note.extra_data) # existing extra data should no longer present self.assert_('sample data' not in note.extra_data) # testuser does not have admin on this annotation; # permissions should not be updated mock_db_perms.assert_not_called() # give user admin permission and update again note.assign_permission('admin_annotation', user) note.update_from_request(self.mockrequest) mock_db_perms.assert_called_with(self.annotation_data['permissions'])
def test_related_pages(self): note = Annotation.create_from_request(self.mockrequest) self.assertEqual(len(self.annotation_data['related_pages']), len(note.related_pages)) for idx in range(len(self.annotation_data['related_pages'])): self.assertEqual(self.annotation_data['related_pages'][idx], note.related_pages[idx]) self.assertEqual(self.annotation_data['related_pages'][idx], note.extra_data['related_pages'][idx]) note = Annotation() self.assertEqual(None, note.related_pages)
def test_create_from_request(self): note = Annotation.create_from_request(self.mockrequest) self.assertEqual(self.annotation_data['text'], note.text) self.assertEqual(self.annotation_data['quote'], note.quote) self.assertEqual(self.annotation_data['uri'], note.uri) self.assert_('ranges' in note.extra_data) self.assertEqual(self.annotation_data['ranges'][0]['start'], note.extra_data['ranges'][0]['start']) self.assert_('permissions' in note.extra_data) # create from request with user specified user = get_user_model().objects.get(username='******') self.mockrequest.user = user note = Annotation.create_from_request(self.mockrequest) self.assertEqual(user, note.user)
def test_user_permissions(self): # annotation user/owner automatically gets permissions user = get_user_model().objects.get(username='******') note = Annotation.create_from_request(self.mockrequest) note.user = user note.save() user_perms = note.user_permissions() self.assertEqual(4, user_perms.count()) self.assert_(user_perms.filter(user=user, permission__codename='view_annotation') .exists()) self.assert_(user_perms.filter(user=user, permission__codename='change_annotation') .exists()) self.assert_(user_perms.filter(user=user, permission__codename='delete_annotation') .exists()) self.assert_(user_perms.filter(user=user, permission__codename='admin_annotation') .exists()) note.save() # saving again shouldn't duplicate the permissions self.assertEqual(4, note.user_permissions().count())
def test_permissions_dict(self): note = Annotation.create_from_request(self.mockrequest) note.save() # get some users and groups to work with user = get_user_model().objects.get(username='******') group1 = AnnotationGroup.objects.create(name='foo') group2 = AnnotationGroup.objects.create(name='foobar') perms = { 'read': [user.username, group1.annotation_id, group2.annotation_id], 'update': [user.username, group1.annotation_id], 'delete': [user.username], 'admin': [] } # test round-trip: convert to db permissions and then back note.db_permissions(perms) self.assertEqual(perms, note.permissions_dict()) perms = { 'read': [user.username, group1.annotation_id], 'update': [user.username], 'delete': [], 'admin': [] } note.db_permissions(perms) self.assertEqual(perms, note.permissions_dict()) perms = {'read': [], 'update': [], 'delete': [], 'admin': []} note.db_permissions(perms) self.assertEqual(perms, note.permissions_dict())
def test_user_permissions(self): # annotation user/owner automatically gets permissions user = get_user_model().objects.get(username='******') note = Annotation.create_from_request(self.mockrequest) note.user = user note.save() user_perms = note.user_permissions() self.assertEqual(4, user_perms.count()) self.assert_( user_perms.filter(user=user, permission__codename='view_annotation').exists()) self.assert_( user_perms.filter( user=user, permission__codename='change_annotation').exists()) self.assert_( user_perms.filter( user=user, permission__codename='delete_annotation').exists()) self.assert_( user_perms.filter( user=user, permission__codename='admin_annotation').exists()) note.save() # saving again shouldn't duplicate the permissions self.assertEqual(4, note.user_permissions().count())
def last_updated_time(self): Annotation.objects.all().delete() # delete fixture annotations self.assertEqual(None, Annotation.objects.all().last_updated_time()) note = Annotation.create_from_request(self.mockrequest) note.save() # save so created/updated will get set self.assertEqual(note.updated, Annotation.objects.all().last_updated_time())
def test_last_created_time(self): # test custom queryset methods Annotation.objects.all().delete() # delete fixture annotations self.assertEqual(None, Annotation.objects.all().last_created_time()) note = Annotation.create_from_request(self.mockrequest) note.save() # save so created/updated will get set self.assertEqual(note.created, Annotation.objects.all().last_created_time())
def post(self, request): 'Create a new annotation via AJAX.' # for now, only support creation via ajax if request.is_ajax(): note = Annotation.create_from_request(request) note.save() # annotator store documentation says to return 303 # not sure why this isn't a 201 Created... return HttpResponseSeeOtherRedirect(note.get_absolute_url()) else: return HttpResponseBadRequest(non_ajax_error_msg)
def test_annotation_to_tei(self): teidoc = load_xmlobject_from_file(os.path.join(FIXTURE_DIR, 'teifacsimile.xml'), tei.AnnotatedFacsimile) note = Annotation(text="Here's the thing", quote="really", extra_data=json.dumps({'sample data': 'foobar', 'tags': ['test', 'one', 'two']})) teinote = annotation_to_tei(note, teidoc) self.assert_(isinstance(teinote, tei.Note)) self.assertEqual('annotation-%s' % note.id, teinote.id) self.assert_(teinote.href.endswith(note.get_absolute_url())) self.assertEqual(note.text, teinote.paragraphs[0]) # todo: add a schema validation once we get the output to be valid # teidoc.schema_valid() # access errors with teidoc.schema_validation_errors() # annotation user should be set as note response user = get_user_model()(username='******') user.save() note.user = user teinote = annotation_to_tei(note, teidoc) self.assertEqual(user.username, teinote.resp) # tags should be set as interp ids ana attribute for tag in note.info()['tags']: self.assert_('#%s' % tag in teinote.ana) # test that markdown formatting is coming through footnote = '''Footnotes[^1] have a label and content. [^1]: This is some footnote content.''' note.text = footnote teinote = annotation_to_tei(note, teidoc) self.assert_('<ref target="#fn1" type="noteAnchor">1</ref>' in teinote.serialize()) # markdown should be included in a code element self.assertEqual(note.text, teinote.markdown) # related page references rel_pages = [ 'http://testpid.co/ark:/1234/11', 'http://testpid.co/ark:/1234/22', 'http://testpid.co/ark:/1234/qq' ] note.extra_data = json.dumps({'related_pages': rel_pages}) teinote = annotation_to_tei(note, teidoc) self.assertEqual(len(rel_pages), len(teinote.related_pages)) # first ark has a corresponding id in the fixture, should be converted self.assertEqual('#%s' % teidoc.page_id_by_xlink(rel_pages[0]), teinote.related_pages[0].target) for idx in range(len(rel_pages)): self.assertEqual(rel_pages[idx], teinote.related_pages[idx].text)
def import_annotation(self, data): '''Create and save a new annotation, setting fields based on a dictionary of data passed in. Raises an error if an annotation author is not found as a user in the database.''' note = Annotation() # NOTE: because we are using uuid for annotation id field, # importing an annotation twice does not error, but simply # replaces the old copy. Might want to add checks for this... # required fields that should always be present # (not normally set by user) for field in ['updated', 'created', 'id']: setattr(note, field, data[field]) del data[field] # user is special: annotation data only includes username, # but we need a user object # NOTE: this could result in making one person's annotations # available to someone else, if someone is using a different # username in another instance if 'user' in data: try: note.user = get_user_model().objects.get(username=data['user']) del data['user'] except get_user_model().DoesNotExist: raise CommandError( 'Cannot import annotations for user %s (does not exist)' % data['user']) for field in Annotation.common_fields: if field in data: setattr(note, field, data[field]) del data[field] # put any other data that is left in extra data json field if data: note.extra_data.update(data) note.save()
def import_annotation(self, data): '''Create and save a new annotation, setting fields based on a dictionary of data passed in. Raises an error if an annotation author is not found as a user in the database.''' note = Annotation() # NOTE: because we are using uuid for annotation id field, # importing an annotation twice does not error, but simply # replaces the old copy. Might want to add checks for this... # required fields that should always be present # (not normally set by user) for field in ['updated', 'created', 'id']: setattr(note, field, data[field]) del data[field] # user is special: annotation data only includes username, # but we need a user object # NOTE: this could result in making one person's annotations # available to someone else, if someone is using a different # username in another instance if 'user' in data: try: note.user = get_user_model().objects.get(username=data['user']) del data['user'] except get_user_model().DoesNotExist: raise CommandError('Cannot import annotations for user %s (does not exist)' % data['user']) for field in Annotation.common_fields: if field in data: setattr(note, field, data[field]) del data[field] # put any other data that is left in extra data json field if data: note.extra_data.update(data) note.save()
def test_info(self): note = Annotation.create_from_request(self.mockrequest) note.save() # save so created/updated will get set info = note.info() fields = ['id', 'annotator_schema_version', 'created', 'updated', 'text', 'quote', 'uri', 'user', 'ranges', 'permissions'] # test that expected fields are present for f in fields: self.assert_(f in info) # test that dates are in isoformat self.assertEqual(info['created'], note.created.isoformat()) self.assertEqual(info['updated'], note.updated.isoformat()) # associate note with a user user = get_user_model().objects.get(username='******') note.user = user info = note.info() self.assertEqual(user.username, info['user'])
def test_info(self): note = Annotation.create_from_request(self.mockrequest) note.save() # save so created/updated will get set info = note.info() fields = [ 'id', 'annotator_schema_version', 'created', 'updated', 'text', 'quote', 'uri', 'user', 'ranges', 'permissions' ] # test that expected fields are present for f in fields: self.assert_(f in info) # test that dates are in isoformat self.assertEqual(info['created'], note.created.isoformat()) self.assertEqual(info['updated'], note.updated.isoformat()) # associate note with a user user = get_user_model().objects.get(username='******') note.user = user info = note.info() self.assertEqual(user.username, info['user'])
def test_permissions_dict(self): note = Annotation.create_from_request(self.mockrequest) note.save() # get some users and groups to work with user = get_user_model().objects.get(username='******') group1 = AnnotationGroup.objects.create(name='foo') group2 = AnnotationGroup.objects.create(name='foobar') perms = { 'read': [user.username, group1.annotation_id, group2.annotation_id], 'update': [user.username, group1.annotation_id], 'delete': [user.username], 'admin': [] } # test round-trip: convert to db permissions and then back note.db_permissions(perms) self.assertEqual(perms, note.permissions_dict()) perms = { 'read': [user.username, group1.annotation_id], 'update': [user.username], 'delete': [], 'admin': [] } note.db_permissions(perms) self.assertEqual(perms, note.permissions_dict()) perms = { 'read': [], 'update': [], 'delete': [], 'admin': [] } note.db_permissions(perms) self.assertEqual(perms, note.permissions_dict())
def test_db_permissions(self): note = Annotation.create_from_request(self.mockrequest) note.save() # get some users and groups to work with user = get_user_model().objects.get(username='******') group1 = AnnotationGroup.objects.create(name='foo') group2 = AnnotationGroup.objects.create(name='foobar') note.db_permissions({ 'read': [user.username, group1.annotation_id, group2.annotation_id], 'update': [user.username, group1.annotation_id], 'delete': [user.username] }) # inspect the db permissions created # should be two total user permissions, one to view and one to change user_perms = note.user_permissions() self.assertEqual(3, user_perms.count()) self.assert_( user_perms.filter(user=user, permission__codename='view_annotation').exists()) self.assert_( user_perms.filter( user=user, permission__codename='change_annotation').exists()) self.assert_( user_perms.filter( user=user, permission__codename='delete_annotation').exists()) # should be three total group permissions group_perms = note.group_permissions() self.assertEqual(3, group_perms.count()) self.assert_( group_perms.filter( group=group1, permission__codename='view_annotation').exists()) self.assert_( group_perms.filter( group=group1, permission__codename='change_annotation').exists()) self.assert_( group_perms.filter( group=group2, permission__codename='view_annotation').exists()) # updating the permissions for the same note should # remove permissions that no longer apply note.db_permissions({ 'read': [user.username, group1.annotation_id], 'update': [user.username], 'delete': [] }) # counts should reflect the changes user_perms = note.user_permissions() self.assertEqual(2, user_perms.count()) group_perms = note.group_permissions() self.assertEqual(1, group_perms.count()) # permissions created before should be gone self.assertFalse( user_perms.filter( user=user, permission__codename='delete_annotation').exists()) self.assertFalse( group_perms.filter( group=group1, permission__codename='change_annotation').exists()) self.assertFalse( group_perms.filter( group=group2, permission__codename='view_annotation').exists()) # invalid group/user should not error note.db_permissions({ 'read': ['bogus', 'group:666', 'group:foo'], 'update': ['group:__world__'], 'delete': [] }) self.assertEqual(0, note.user_permissions().count()) self.assertEqual(0, note.group_permissions().count())
def test_db_permissions(self): note = Annotation.create_from_request(self.mockrequest) note.save() # get some users and groups to work with user = get_user_model().objects.get(username='******') group1 = AnnotationGroup.objects.create(name='foo') group2 = AnnotationGroup.objects.create(name='foobar') note.db_permissions({ 'read': [user.username, group1.annotation_id, group2.annotation_id], 'update': [user.username, group1.annotation_id], 'delete': [user.username] }) # inspect the db permissions created # should be two total user permissions, one to view and one to change user_perms = note.user_permissions() self.assertEqual(3, user_perms.count()) self.assert_(user_perms.filter(user=user, permission__codename='view_annotation') .exists()) self.assert_(user_perms.filter(user=user, permission__codename='change_annotation') .exists()) self.assert_(user_perms.filter(user=user, permission__codename='delete_annotation') .exists()) # should be three total group permissions group_perms = note.group_permissions() self.assertEqual(3, group_perms.count()) self.assert_(group_perms.filter(group=group1, permission__codename='view_annotation') .exists()) self.assert_(group_perms.filter(group=group1, permission__codename='change_annotation') .exists()) self.assert_(group_perms.filter(group=group2, permission__codename='view_annotation') .exists()) # updating the permissions for the same note should # remove permissions that no longer apply note.db_permissions({ 'read': [user.username, group1.annotation_id], 'update': [user.username], 'delete': [] }) # counts should reflect the changes user_perms = note.user_permissions() self.assertEqual(2, user_perms.count()) group_perms = note.group_permissions() self.assertEqual(1, group_perms.count()) # permissions created before should be gone self.assertFalse(user_perms.filter(user=user, permission__codename='delete_annotation') .exists()) self.assertFalse(group_perms.filter(group=group1, permission__codename='change_annotation') .exists()) self.assertFalse(group_perms.filter(group=group2, permission__codename='view_annotation') .exists()) # invalid group/user should not error note.db_permissions({ 'read': ['bogus', 'group:666', 'group:foo'], 'update': ['group:__world__'], 'delete': [] }) self.assertEqual(0, note.user_permissions().count()) self.assertEqual(0, note.group_permissions().count())
def test_annotated_tei(self): # create annotation with a user user = get_user_model()(username='******', first_name="Anne", last_name="O'Tater") user.save() page_uri = "http://readux.co/books/pages/some:1/" note = Annotation(text="Here's the thing", quote="really", uri=page_uri, extra_data=json.dumps({ 'sample data': 'foobar', 'tags': ['test', 'one', 'two'], 'ranges': [ {'start': '//div[@id="fnstr.idm320760248608"]/span[1]', 'end': '//div[@id="fnstr.idm320760242176"]/span[1]', 'startOffset': 0, 'endOffset': 6 } ], 'ark': page_uri }), user=user) note.save() imagenote = Annotation(text='interesting image detail', uri=page_uri, user=user, extra_data=json.dumps({ 'image_selection': { # NOTE: image src currently not checked when generating annotated tei 'h': '20.73%', 'w': '14.70%', 'y': '62.42%', 'x': '61.60%' }, 'ark': page_uri }) ) imagenote.save() # use page tei fixture as starting point title = 'Lecoq' teidoc = load_xmlobject_from_file(os.path.join(FIXTURE_DIR, 'teifacsimile.xml'), tei.AnnotatedFacsimile) teidoc.title = title teidoc.page.href = page_uri del teidoc.responsible_names[0] # remove empty resp name from fixture annotei = annotated_tei(teidoc, Annotation.objects.all()) self.assert_(isinstance(annotei, tei.AnnotatedFacsimile), 'annotated_tei should return an insance of tei.AnnotatedFacsimile') # title should be updated, split into main and subtitle parts self.assertEqual(title, annotei.main_title) self.assertEqual(', an annotated digital edition', annotei.subtitle) self.assertEqual('annotated by', annotei.responsibility) # annotation authors should be added in resp statement self.assertEqual(user.username, annotei.responsible_names[0].id) self.assertEqual(user.get_full_name(), annotei.responsible_names[0].value) # publication statement should include readux version, current date self.assert_('Annotated TEI generated by Readux' in annotei.pubstmt.desc) self.assert_(__version__ in annotei.pubstmt.desc) today = datetime.date.today() self.assertEqual(today, annotei.pubstmt.date) self.assertEqual(today, annotei.pubstmt.date_normal) # tei annotation should be added to body for text note self.assertEqual('annotation-%s' % note.id, annotei.annotations[0].id) # start/end highlight anchors should be added to facsimile self.assert_(annotei.node.xpath('//tei:anchor[@xml:id="highlight-start-%s"]' % note.id, namespaces=tei.Zone.ROOT_NAMESPACES)) self.assert_(annotei.node.xpath('//tei:anchor[@xml:id="highlight-end-%s"]' % note.id, namespaces=tei.Zone.ROOT_NAMESPACES)) # tei annotation should be added to body for image note self.assertEqual('annotation-%s' % imagenote.id, annotei.annotations[1].id) # zone added for image highlight self.assert_(annotei.node.xpath('//tei:zone[@xml:id="highlight-%s"][@type="image-annotation-highlight"]' % imagenote.id, namespaces=tei.Zone.ROOT_NAMESPACES)) # tags added to back as interp group self.assertEqual('test', annotei.tags.interp[0].id) self.assertEqual('test', annotei.tags.interp[0].value) # encoding desc should be present self.assert_(annotei.encoding_desc)
class AnnotatedTei(TestCase): def test_annotation_to_tei(self): teidoc = load_xmlobject_from_file(os.path.join(FIXTURE_DIR, 'teifacsimile.xml'), tei.AnnotatedFacsimile) note = Annotation(text="Here's the thing", quote="really", extra_data=json.dumps({'sample data': 'foobar', 'tags': ['test', 'one', 'two']})) teinote = annotation_to_tei(note, teidoc) self.assert_(isinstance(teinote, tei.Note)) self.assertEqual('annotation-%s' % note.id, teinote.id) self.assert_(teinote.href.endswith(note.get_absolute_url())) self.assertEqual(note.text, teinote.paragraphs[0]) # todo: add a schema validation once we get the output to be valid # teidoc.schema_valid() # access errors with teidoc.schema_validation_errors() # annotation user should be set as note response user = get_user_model()(username='******') user.save() note.user = user teinote = annotation_to_tei(note, teidoc) self.assertEqual(user.username, teinote.resp) # tags should be set as interp ids ana attribute for tag in note.info()['tags']: self.assert_('#%s' % tag in teinote.ana) # test that markdown formatting is coming through footnote = '''Footnotes[^1] have a label and content. [^1]: This is some footnote content.''' note.text = footnote teinote = annotation_to_tei(note, teidoc) self.assert_('<ref target="#fn1" type="noteAnchor">1</ref>' in teinote.serialize()) # markdown should be included in a code element self.assertEqual(note.text, teinote.markdown) # related page references rel_pages = [ 'http://testpid.co/ark:/1234/11', 'http://testpid.co/ark:/1234/22', 'http://testpid.co/ark:/1234/qq' ] note.extra_data = json.dumps({'related_pages': rel_pages}) teinote = annotation_to_tei(note, teidoc) self.assertEqual(len(rel_pages), len(teinote.related_pages)) # first ark has a corresponding id in the fixture, should be converted self.assertEqual('#%s' % teidoc.page_id_by_xlink(rel_pages[0]), teinote.related_pages[0].target) for idx in range(len(rel_pages)): self.assertEqual(rel_pages[idx], teinote.related_pages[idx].text) zotero_note = Annotation(text=u'''update test ing la lala ([Jockers and Mimno 2013](#zotero-MUXAEE89)) more content ([Underwood and Sellers 2012](#zotero-7CBCH6E8)) la la la foo bar lala --- ### Works Cited * <a name="zotero-MUXAEE89"></a>Jockers, Matthew L., and David Mimno. “Significant Themes in 19th-Century Literature.” <i>Poetics</i> 41, no. 6 (December 2013): 750–69. doi:10.1016/j.poetic.2013.08.005. * <a name="zotero-7CBCH6E8"></a>Underwood, Ted, and Jordan Sellers. “The Emergence of Literary Diction.” <i>Journal of Digital Humanities</i>, June 26, 2012. http://journalofdigitalhumanities.org/1-2/the-emergence-of-literary-diction-by-ted-underwood-and-jordan-sellers/.''', quote="really", extra_data=json.dumps({'citations': [ '<biblStruct xmlns="http://www.tei-c.org/ns/1.0" type="webpage" xml:id="zoteroItem-http://zotero.org/users/758030/items/7CBCH6E8" corresp="http://zotero.org/users/758030/items/7CBCH6E8"><monogr><title level="m">The Emergence of Literary Diction</title><author><forename>Ted</forename><surname>Underwood</surname></author><author><forename>Jordan</forename><surname>Sellers</surname></author><imprint><date>2012</date><note type="accessed">2015-05-09T22:02:51Z</note><note type="url">http://journalofdigitalhumanities.org/1-2/the-emergence-of-literary-diction-by-ted-underwood-and-jordan-sellers/</note></imprint></monogr></biblStruct>', '<biblStruct xmlns="http://www.tei-c.org/ns/1.0" type="journalArticle" xml:id="zoteroItem-http://zotero.org/users/758030/items/MUXAEE89" corresp="http://zotero.org/users/758030/items/MUXAEE89"><analytic><title level="a">Significant themes in 19th-century literature</title><author><forename>Matthew L.</forename><surname>Jockers</surname></author><author><forename>David</forename><surname>Mimno</surname></author></analytic><monogr><title level="j">Poetics</title><imprint><biblScope unit="volume">41</biblScope><biblScope unit="issue">6</biblScope><biblScope unit="page">750-769</biblScope><date>2013</date><note type="accessed">2016-01-24T02:44:56Z</note><note type="url">http://linkinghub.elsevier.com/retrieve/pii/S0304422X13000673</note></imprint></monogr><idno type="ISSN">0304422X</idno><idno type="DOI">10.1016/j.poetic.2013.08.005</idno></biblStruct>' ]})) def test_annotation_citation_to_tei(self): teidoc = load_xmlobject_from_file(os.path.join(FIXTURE_DIR, 'teifacsimile.xml'), tei.AnnotatedFacsimile) teinote = annotation_to_tei(self.zotero_note, teidoc) # print teinote.serialize(pretty=True) # number of citations should match self.assertEqual(len(self.zotero_note.extra_data['citations']), len(teinote.citations)) # minimal inspection to check that values carried through as expected self.assertEqual('webpage', teinote.citations[0].type) self.assertEqual('journalArticle', teinote.citations[1].type) self.assertEqual('zotero-7CBCH6E8', teinote.citations[0].id) self.assertEqual('zotero-MUXAEE89', teinote.citations[1].id) def test_insert_anchor(self): def get_test_elements(): div = etree.fromstring('''<div> <p>Sample text to insert anchors into</p> </div>''') element = div.xpath('//p')[0] return div, element div, element = get_test_elements() anchor = etree.fromstring('<anchor/>') # insert anchor at 0 index relative to paragraph insert_anchor(element, anchor, 0) # 0 index, anchor should be *before* tag self.assertEqual('anchor', element.getprevious().tag) div, element = get_test_elements() # insert anchor in the middle of the paragraph insert_anchor(element, anchor, 11) # anchor element should be a child of p tag self.assert_(anchor in element.getchildren()) # text should be split before and after anchor self.assertEqual('Sample text', element.text) self.assertEqual(' to insert anchors into', anchor.tail) div, element = get_test_elements() anchor = etree.fromstring('<anchor/>') # insert anchor at the end of the paragraph insert_anchor(element, anchor, 35) # 0 index, anchor should be after tag self.assertEqual('anchor', element.getnext().tag) def test_annotated_tei(self): # create annotation with a user user = get_user_model()(username='******', first_name="Anne", last_name="O'Tater") user.save() page_uri = "http://readux.co/books/pages/some:1/" note = Annotation(text="Here's the thing", quote="really", uri=page_uri, extra_data=json.dumps({ 'sample data': 'foobar', 'tags': ['test', 'one', 'two'], 'ranges': [ {'start': '//div[@id="fnstr.idm320760248608"]/span[1]', 'end': '//div[@id="fnstr.idm320760242176"]/span[1]', 'startOffset': 0, 'endOffset': 6 } ], 'ark': page_uri }), user=user) note.save() imagenote = Annotation(text='interesting image detail', uri=page_uri, user=user, extra_data=json.dumps({ 'image_selection': { # NOTE: image src currently not checked when generating annotated tei 'h': '20.73%', 'w': '14.70%', 'y': '62.42%', 'x': '61.60%' }, 'ark': page_uri }) ) imagenote.save() # use page tei fixture as starting point title = 'Lecoq' teidoc = load_xmlobject_from_file(os.path.join(FIXTURE_DIR, 'teifacsimile.xml'), tei.AnnotatedFacsimile) teidoc.title = title teidoc.page.href = page_uri del teidoc.responsible_names[0] # remove empty resp name from fixture annotei = annotated_tei(teidoc, Annotation.objects.all()) self.assert_(isinstance(annotei, tei.AnnotatedFacsimile), 'annotated_tei should return an insance of tei.AnnotatedFacsimile') # title should be updated, split into main and subtitle parts self.assertEqual(title, annotei.main_title) self.assertEqual(', an annotated digital edition', annotei.subtitle) self.assertEqual('annotated by', annotei.responsibility) # annotation authors should be added in resp statement self.assertEqual(user.username, annotei.responsible_names[0].id) self.assertEqual(user.get_full_name(), annotei.responsible_names[0].value) # publication statement should include readux version, current date self.assert_('Annotated TEI generated by Readux' in annotei.pubstmt.desc) self.assert_(__version__ in annotei.pubstmt.desc) today = datetime.date.today() self.assertEqual(today, annotei.pubstmt.date) self.assertEqual(today, annotei.pubstmt.date_normal) # tei annotation should be added to body for text note self.assertEqual('annotation-%s' % note.id, annotei.annotations[0].id) # start/end highlight anchors should be added to facsimile self.assert_(annotei.node.xpath('//tei:anchor[@xml:id="highlight-start-%s"]' % note.id, namespaces=tei.Zone.ROOT_NAMESPACES)) self.assert_(annotei.node.xpath('//tei:anchor[@xml:id="highlight-end-%s"]' % note.id, namespaces=tei.Zone.ROOT_NAMESPACES)) # tei annotation should be added to body for image note self.assertEqual('annotation-%s' % imagenote.id, annotei.annotations[1].id) # zone added for image highlight self.assert_(annotei.node.xpath('//tei:zone[@xml:id="highlight-%s"][@type="image-annotation-highlight"]' % imagenote.id, namespaces=tei.Zone.ROOT_NAMESPACES)) # tags added to back as interp group self.assertEqual('test', annotei.tags.interp[0].id) self.assertEqual('test', annotei.tags.interp[0].value) # encoding desc should be present self.assert_(annotei.encoding_desc) def test_consolidate_bibl(self): teidoc = load_xmlobject_from_file(os.path.join(FIXTURE_DIR, 'teifacsimile.xml'), tei.AnnotatedFacsimile) teinote = annotation_to_tei(self.zotero_note, teidoc) teidoc.annotations.append(teinote) consolidate_bibliography(teidoc) self.assertEqual(2, len(teidoc.citations), 'annotation citations should be present in main document bibl') teinote = teidoc.annotations[0] self.assertEqual(0, len(teinote.citations), 'citations should not be present on individual annotation') self.assertEqual(None, teinote.works_cited) self.assertEqual(None, teinote.zotero_items) self.assertEqual(None, teinote.works_cited_milestone) teinote_xml = teinote.serialize() self.assertFalse('<item><anchor xml:id="zotero-' in teinote_xml) self.assertFalse('<listBibl/>' in teinote_xml) # repeated zotero ids should only appear once in document bibl # load the same note and add it again teinote = annotation_to_tei(self.zotero_note, teidoc) teidoc.annotations.append(teinote) consolidate_bibliography(teidoc) self.assertEqual(2, len(teidoc.citations), 'citations repeated in annotations should only appear once')