跳转到主要内容

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 ...>

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 (36.7 kB 查看散列

上传时间: 源代码

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面