Fedora Commons Repository平台的API实现
项目描述
FCRepo,Fedora Commons Repository的客户端
信息
此软件包提供了对Fedora Commons Repository的访问。
来自Fedora Commons网站的介绍
Fedora(灵活可扩展的数字对象存储库架构)最初由康奈尔大学的学者开发,作为一个存储、管理和访问数字内容(以数字对象的形式)的架构,这些数字内容受到Kahn和Wilensky框架的启发。Fedora定义了一系列用于表达数字对象、断言数字对象之间的关系并将“行为”(即服务)链接到数字对象的抽象。Fedora Repository项目(即Fedora)在健壮的开源软件系统中实现了Fedora抽象。
本包使用WADL(Web应用程序描述语言),Web应用程序描述语言,来解析Fedora附带的WADL文件,从而支持完整的REST API。在此基础上,还编写了一个更高层次的抽象,将在本doctest中演示。本包是为FedoraCommons 3.3和3.4编写的,尚未在旧版本上进行测试。REST API文档可以在Fedora维基中找到。
可以使用buildout安装此包,它还会检索Fedora安装程序,并在本地进行测试安装。按照以下步骤安装和运行此doctest:
python2.6 bootstrap.py ./bin/buildout ./bin/install_fedora ./bin/start_fedora ./bin/test
使用fcrepo包
连接到存储库
要连接到运行的Fedora,我们首先需要一个连接。连接代码主要复制自Etienne Posthumus(“Epoz”)duraspace模块。
>>> from fcrepo.connection import Connection >>> connection = Connection('http://localhost:8080/fedora', ... username='fedoraAdmin', ... password='fedoraAdmin')
现在我们有了连接,我们可以创建一个FedoraClient
>>> from fcrepo.client import FedoraClient >>> client = FedoraClient(connection)
PID
Fedora对象需要一个唯一的PID才能运行。PID由一个命名空间字符串、一个分号和一个标识符字符串组成。您可以使用随机UUID创建自己的PID,也可以使用Fedora的nextPID功能,该功能返回一个递增数字。
>>> pid = client.getNextPID(u'foo') >>> ns, num = pid.split(':') >>> ns == 'foo' and num.isdigit() True
我们也可以一次性获取多个PID
>>> pids = client.getNextPID(u'foo', numPIDs=10) >>> len(pids) 10
此方法返回Unicode字符串,如果请求多个PID,则返回Unicode字符串列表。
客户端抽象提供了对由WADL文件生成的“低级”API代码的包装。以下是相同的调用通过WADL API:
>>> print client.api.getNextPID().submit(namespace=u'foo', format=u'text/xml').read() <?xml ...?> <pidList ...> <pid>...</pid> </pidList>
因此,客户端方法调用WADL API中的方法,解析结果XML,并使用合理的默认参数。
这是大多数客户端方法调用的工作方式。通常,您根本不需要直接访问WADL API,所以我们继续前进。
创建对象
现在我们可以获取PID,我们可以使用它们来创建一个新对象
>>> pid = client.getNextPID(u'foo') >>> obj = client.createObject(pid, label=u'My First Test Object')
不能使用相同的PID创建两次对象。
>>> obj = client.createObject(pid, label=u'Second try?') Traceback (most recent call last): ... FedoraConnectionException: ... The PID 'foo:...' already exists in the registry; the object can't be re-created.
获取对象
当然,也可以使用客户端检索现有的对象
>>> obj = client.getObject(pid) >>> print obj.label My First Test Object
如果对象不存在,您将得到一个错误
>>> obj = client.getObject(u'foo:bar') Traceback (most recent call last): ... FedoraConnectionException: ...HTTP code=404, Reason=Not Found...
删除对象
可以通过在对象上调用delete方法或通过将pid传递给客户端上的deleteObject方法来删除对象。
>>> pid = client.getNextPID(u'foo') >>> o = client.createObject(pid, label=u'About to be deleted') >>> o.delete(logMessage=u'Bye Bye') >>> o = client.getObject(pid) Traceback (most recent call last): ... FedoraConnectionException: ...HTTP code=404, Reason=Not Found...
请注意,在大多数情况下,您不想删除对象。最好将对象的状态设置为删除。下一节将详细介绍。
对象属性
在前面的示例中,我们检索了一个Fedora对象。这些对象有多个属性可以获取和设置
>>> obj.label u'My First Test Object' >>> date = obj.lastModifiedDate >>> obj.label = u'Changed it!'
最后一行修改了Fedora服务器上的标签属性,最后修改日期应该已经更新
>>> obj.lastModifiedDate > date True >>> obj.label u'Changed it!'
设置属性也可以用来更改FedoraObject的状态为非活动或已删除。以下字符串可以使用
A表示活动
I表示非活动
D表示已删除
>>> obj.state = u'I'
让我们尝试一个不支持的状态
>>> obj.state = u'Z' Traceback (most recent call last): ... FedoraConnectionException: ... The object state of "Z" is invalid. The allowed values for state are: A (active), D (deleted), and I (inactive).
直接设置修改或创建日期将导致错误,它们不能被设置。
>>> obj.lastModifiedDate = date Traceback (most recent call last): ... AttributeError: can't set attribute
也可以使用属性配置所有者ID
>>> obj.ownerId = u'me' >>> print obj.ownerId me
对象数据流
Fedora对象基本上是一个数据流的容器。您可以通过迭代对象来找到数据流ID,或调用datastreams方法
>>> print obj.datastreams() ['DC'] >>> for id in obj: print id DC >>> 'DC' in obj True
要实际获取数据流,我们可以将其作为字典来访问
>>> ds = obj['DC'] >>> ds <fcrepo.datastream.DCDatastream object at ...> >>> obj['FOO'] Traceback (most recent call last): ... FedoraConnectionException: ...No datastream could be found. Either there is no datastream for the digital object "..." with datastream ID of "FOO" OR there are no datastreams that match the specified date/time value of "null".
数据流属性
数据流有许多属性,包括标签、状态和创建日期,就像Fedora对象一样
>>> print ds.label Dublin Core Record for this object>>> print ds.state A
数据流有不同类型,这个是类型X,这意味着内容存储在FOXML文件中。FOXML是Fedora的内部存储格式。
>>> print ds.controlGroup X
数据流可以是可版本化的,这可以打开或关闭。
>>> ds.versionable True
数据流还具有一个位置,由对象PID、数据流ID和版本号组成。
>>> ds.location u'foo:...+DC+DC1.0'
让我们更改标签,看看会发生什么。
>>> ds.label = u'Datastream Metadata' >>> ds.location u'foo:...+DC+DC.1'>>> ds.label = u'Datastream DC Metadata' >>> ds.location u'foo:...+DC+DC.2'
位置ID会随着每个版本而改变,旧版本的数据流仍然可用。fcrepo客户端代码不包含检索数据流旧版本或查看对象审计日志的方法。不过,这些方法在WADL API中是可用的。
Fedora可以创建存储在数据流中的内容的校验和,默认情况下校验和是禁用的。如果我们设置checksumType属性为MD5,Fedora将为我们生成校验和。
>>> ds.checksumType u'DISABLED' >>> ds.checksumType = u'MD5' >>> ds.checksum # the checksum always changes between tests u'...'
有一些额外的属性,并不是所有的都可以设置。请查看REST API文档以获取完整列表。
>>> ds.mimeType u'text/xml' >>> ds.size > 0 True >>> ds.formatURI u'http://www.openarchives.org/OAI/2.0/oai_dc/'
获取和设置内容 - 1
我们还可以获取和设置数据流的内容。
>>> xml = ds.getContent().read() >>> print xml <oai_dc:dc ...> <dc:title>My First Test Object</dc:title> <dc:identifier>foo:...</dc:identifier> </oai_dc:dc>>>> xml = xml.replace('My First Test Object', 'My First Modified Datastream') >>> ds.setContent(xml)
获取和设置内容 - 2
我们还可以直接获取和设置内容,就像它是一个字典的字典。
>>> print obj['DC']['title'] [u'My First Modified Datastream'] >>> obj['DC']['title'] = [u'My Second Modified Datastream'] >>> print obj['DC']['title'] [u'My Second Modified Datastream']
特殊数据流:DC
这个始终可用的DC数据流实际上是一种特殊的数据流。来自此XML流的Dublin Core属性存储在可以搜索的关系型数据库中。这些值也用于OAIPMH馈送。Fedora使用遗留的/elements/1.1/命名空间,其中包含以下术语
contributor
coverage
creator
date
description
format
identifier
language
publisher
relation
rights
source
subject
title
type
请访问Dublin Core网站了解这些属性的描述。
由于手动编辑Dublin Core XML数据有点繁琐,DC数据流允许以字典的方式访问DC属性。
>>> ds['title'] [u'My Second Modified Datastream']
这也可以用来设置值。
>>> ds['subject'] = [u'fcrepo', u'unittest'] >>> ds['description'].append(u'A test object from the fcrepo unittest')>>> for prop in sorted(ds): print prop description identifier subject title >>> 'subject' in ds True
要保存这些,我们再次调用setContent方法,但这次不带任何参数。这将使代码使用字典中的值为您生成XML字符串。
>>> ds.setContent() >>> print ds.getContent().read() <oai_dc:dc ...> ... <dc:description>A test object from the fcrepo unittest</dc:description> ... </oai_dc:dc>
内联XML数据流
让我们尝试添加一些数据流,例如,我们想要存储一些XML数据。
>>> obj.addDataStream('FOOXML', '<foo/>', ... label=u'Foo XML', ... logMessage=u'Added an XML Datastream') >>> obj.datastreams() ['DC', 'FOOXML'] >>> print obj['FOOXML'].getContent().read() <foo></foo>
管理内容数据流
我们还可以添加管理内容,这将由Fedora存储和管理,但不是内联XML。数据存储在硬盘上的单独文件中。我们通过设置controlGroup参数为M来完成此操作。
>>> obj.addDataStream('TEXT', 'Hello!', label=u'Some Text', ... mimeType=u'text/plain', controlGroup=u'M', ... logMessage=u'Added some managed text') >>> obj.datastreams() ['DC', 'FOOXML', 'TEXT'] >>> ds = obj['TEXT'] >>> ds.size == 0 or ds.size == 6 # this does not work in Fedora 3.3 True >>> ds.getContent().read() 'Hello!'
这对于小文件来说完全没问题。然而,当您不想在内存中保存整个文件时,也可以提供文件流。让我们创建一个3MB的文件。
>>> import tempfile, os >>> fp = tempfile.NamedTemporaryFile(mode='w+b', delete=False) >>> filename = fp.name >>> fp.write('foo' * (1024**2)) >>> fp.close() >>> os.path.getsize(filename) 3145728...
现在我们将打开文件并将其流式传输到Fedora。然后我们将其全部读入内存,看看它的大小是否相同。
>>> fp = open(filename, 'r') >>> ds.setContent(fp) >>> fp.close() >>> content = ds.getContent().read() >>> len(content) 3145728... >>> os.remove(filename)
外部引用数据流
对于大文件,将它们存储在Fedora内部可能不方便。在这种情况下,文件可以托管在外部,我们存储一个控制组类型为E(外部引用)的数据流。
>>> obj.addDataStream('URL', controlGroup=u'E', ... location=u'http://pypi.python.org/fcrepo') >>> obj.datastreams() ['DC', 'FOOXML', 'TEXT', 'URL']
此数据流没有任何内容,因此尝试读取内容将导致错误。
>>> ds = obj['URL'] >>> ds.getContent() Traceback (most recent call last): ... FedoraConnectionException:..."Error getting http://pypi.python.org/fcrepo" .
但是我们可以获取位置。
>>> ds.location u'http://pypi.python.org/fcrepo'
数据流类型的最后一种是重定向的外部引用流。此数据流的控制组为R(重定向引用)。
>>> obj.addDataStream('HOMEPAGE', controlGroup=u'R', ... location=u'http://pypi.python.org/fcrepo') >>> obj.datastreams() ['DC', 'FOOXML', 'TEXT', 'URL', 'HOMEPAGE']
此数据流与外部引用流的工作方式相同。
删除数据流
可以通过在对象上使用Python del关键字或通过在数据流上调用delete方法来删除数据流。
>>> len(obj.datastreams()) 5 >>> ds = obj['HOMEPAGE'] >>> ds.delete(logMessage=u'Removed Homepage DS') >>> len(obj.datastreams()) 4 >>> del obj['URL'] >>> len(obj.datastreams()) 3
另一个特殊数据流:RELS-EXT
除了特殊的DC数据流之外,还有一种称为RELS-EXT的特殊数据流。这个数据流应包含扁平的RDFXML数据,这些数据将在三元组存储中索引。RELS-EXT数据流有一些额外的方法来帮助处理RDF数据。
创建RELS-EXT流时,我们不需要提供RDFXML文件,如果没有数据发送,它将创建一个空文件。
>>> obj.addDataStream('RELS-EXT') >>> ds = obj['RELS-EXT']
现在我们可以添加一些RDF数据。每个谓词包含一个值列表,每个值都是一个包含值和类型键的字典,以及可选的lang和datatype键。这与RDF+JSON格式相同。
>>> from fcrepo.utils import NS >>> ds[NS.rdfs.comment].append( ... {'value': u'A Comment set in RDF', 'type': u'literal'}) >>> ds[NS.rdfs.comment] [{'type': u'literal', 'value': u'A Comment set in RDF'}] >>> NS.rdfs.comment in ds True >>> for predicate in ds: print predicate http://www.w3.org/2000/01/rdf-schema#comment
要保存这些数据,我们调用不带任何数据的setContent方法。这将序列化RDF语句到RDFXML,并执行保存操作
>>> ds.setContent() >>> print ds.getContent().read() <rdf:RDF ...> <rdf:Description rdf:about="info:fedora/foo:..."> <rdfs:comment>A Comment set in RDF</rdfs:comment> </rdf:Description> </rdf:RDF>
我们不允许使用DC命名空间添加语句。这将导致错误。我想这可能是因为它应该通过DC数据流设置。
>>> ds[NS.dc.title].append({'value': u'A title', 'type': 'literal'}) >>> ds.setContent() Traceback (most recent call last): ... FedoraConnectionException: ... The RELS-EXT datastream has improper relationship assertion: dc:title.
我们还可以使用RDF创建对象之间的关系。例如,我们可以添加一个关系,使用Fedora的isMemberOfCollection,这可以用来将对象分组到在OAIPMH馈送中使用的集合中。
>>> colpid = client.getNextPID(u'foo') >>> collection = client.createObject(colpid, label=u'A test Collection') >>> ds[NS.fedora.isMemberOfCollection].append( ... {'value': u'info:fedora/%s' % colpid, 'type':u'uri'}) >>> ds.setContent() >>> print ds.getContent().read() <rdf:RDF ...> <rdf:Description rdf:about="info:fedora/foo:..."> <fedora:isMemberOfCollection rdf:resource="info:fedora/foo:..."></fedora:isMemberOfCollection> <rdfs:comment>A Comment set in RDF</rdfs:comment> </rdf:Description> </rdf:RDF>>>> print ds.predicates() ['http://www.w3.org/2000/01/rdf-schema#comment', 'info:fedora/fedora-system:def/relations-external#isMemberOfCollection']
请注意,Fedora PID需要在RDF中引用之前转换为URI,这可以通过在PID前添加info:fedora/来完成。
服务定义和对象方法
除了数据流之外,Fedora对象可以通过服务定义注册方法。我们不提供对服务定义的直接访问,但假设所有方法都有唯一的名称。
>>> obj.methods() ['viewObjectProfile', 'viewMethodIndex', 'viewItemIndex', 'viewDublinCore']>>> print obj.call('viewDublinCore').read() <html ...> ... <td ...>My Second Modified Datastream</td> ... </html>
搜索对象
Fedora提供了两种搜索功能:字段查询搜索和简单查询搜索。它们都搜索来自DC数据流和Fedora对象属性的数据库。
字段搜索查询可以搜索以下字段:
cDate
contributor
coverage
creator
date
dcmDate
description
format
identifier
label
language
mDate
ownerId
pid
publisher
source
state
subject
title
type
rights
Fedora有一个查询语法,您可以输入一个或多个条件,条件之间用空格分隔。将返回匹配所有条件的对象。
条件是一个字段(从上面的字段名称中选择),后面跟着一个操作符,后面跟着一个值。
=操作符将匹配字段值的整个值与给定的值匹配。~操作符将在字段内的短语上进行匹配,并接受?和*通配符。<、>、<=和>=操作符可以与数字值(如日期)一起使用。
示例
- pid~demo:* description~fedora
匹配所有包含描述中包含单词fedora的demo对象。
- cDate>=1976-03-04 creator~*n*
匹配1976年3月4日或之后创建的对象,其中至少有一个创建者姓名中有n。
- mDate>2002-10-2 mDate<2002-10-2T12:00:00
匹配在2002年10月2日中午(UTC)之前修改的对象
因此,让我们创建5个对象,我们可以使用它们来搜索。
>>> pids = client.getNextPID(u'searchtest', numPIDs=5) >>> for pid in pids: client.createObject(pid, label=u'Search Test Object') <fcrepo.object.FedoraObject object at ...> <fcrepo.object.FedoraObject object at ...> <fcrepo.object.FedoraObject object at ...> <fcrepo.object.FedoraObject object at ...> <fcrepo.object.FedoraObject object at ...>
现在我们将使用pid搜索这些对象,我们还希望从搜索中返回标签。
>>> client.searchObjects(u'pid~searchtest:*', ['pid', 'label']) <generator object searchObjects at ...>
搜索返回一个生成器,默认情况下它查询服务器的前10个对象,但如果您遍历结果集并到达末尾,下一批将自动添加。
为了说明这一点,我们将以2个批次的大小进行查询
>>> results = client.searchObjects(u'pid~searchtest:*', ['pid', 'label'], ... maxResults=2) >>> result_list = [r for r in results] >>> len(result_list) >= 5 True >>> result_list[0]['pid'] [u'searchtest:...'] >>> result_list[0]['label'] [u'Search Test Object']
如上图所示,我们实际上得到了超过2的最大值的结果,但客户端在迭代结果生成器时向Fedora请求2个批次的结果。
当我们想要在所有字段中进行搜索时,我们只需要去掉条件‘pid:’,并指定‘terms=True’。搜索不区分大小写,并使用*或?作为通配符。
>>> client.searchObjects(u'searchtest*', ['pid', 'label'], terms=True) <generator object searchObjects at ...>
RDF索引搜索
除了在关系数据库中搜索DC数据流之外,还可以通过SPARQL语言在三元组存储中查询RELS-EXT数据流。
让我们在RELS-EXT数据流示例中找到我们创建的集合中所有对象。
>>> sparql = '''prefix fedora: <%s> ... select ?s where {?s fedora:isMemberOfCollection <info:fedora/%s>.} ... ''' % (NS.fedora, colpid) >>> result = client.searchTriples(sparql) >>> result <generator object searchTriples at ...> >>> result = list(result) >>> len(result) 1 >>> result[0]['s']['value'] u'info:fedora/foo:...'
其他输出格式和查询语言可以作为参数指定,默认情况下仅支持SPARQL。
searchTriples方法还有一个flush参数。如果您更改Fedora中的RELS-EXT数据流,三元组存储实际上不会更新!您必须将此flush参数设置为true以确保三元组存储已更新。默认情况下,Fedora将flush参数设置为false,这是出于性能考虑,但可能会造成混淆。此库默认将参数设置为true,这并不总是非常高效,但您可以确保三元组存储是最新的。
FCRepo更改
1.1 (2010-11-04)
添加了简单搜索(通过searchObject),由Steen Manniche提供。
从buildout.cfg中移除了buildout版本。
修复了解码空文本时的错误。
更新了readme。
1.0 (2010-09-30)
添加了对Fedora3.4的支持。
更改了联系信息,从Subversion切换到Mercurial。
更改
修复了在检索不包含文本的DC数据流值时触发的错误。
fcrepo 1.0b2(2010-05-17)
更改
通过Owen Nelson的补丁实现全Windows兼容性。
数据流处理中的错误修复。
fcrepo 1.0b1(2010-05-03)
更改
初始代码发布,包含工作的API-A、API-M搜索和索引搜索。
项目详细信息
fcrepo-1.1.tar.gz的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 87bb2d30d8effc281f64054a1269431c96f57477be0c0eff774a31cc27beb82d |
|
MD5 | 61bafa0ca88316a7e87de5be82c7688a |
|
BLAKE2b-256 | 8158a9933d6d93b5f78f6715371b94caca2d63a055600cc6cc1d0b831b62a2fd |