📚

AtoM(Access to Memory)のAPIを使って、オブジェクトを登録してみる

2025/03/12に公開

概要

AtoM(Access to Memory)のAPIを使って、オブジェクトを登録する方法の備忘録です。

APIの有効化

以下にアクセスします。

/sfPluginAdminPlugin/plugins

arRestApiPluginを有効にします。

APIキーの取得

以下に、APIキーを生成する方法が説明されています。

https://www.accesstomemory.org/en/docs/2.9/dev-manual/api/api-intro/#generating-an-api-key-for-a-user

ユーザ名とパスワードでもAPI接続できるようですが、今回はREST API Keyを発行しました。

エンドポイント

AtoMでは、「典拠レコード」や「機能」など、複数のメニューが提供されていますが、APIによって利用できるのは、以下のみのようです。

See the subsequent pages for more details on each endpoint, and available parameters. There are three endpoints available:

Browse taxonomy terms
Browse information objects
Read information object
Download digital objects
Add physical objects

この点は、ArchivesSpaceのほうが豊富なAPIが提供されており、軍配が上がるかもしれません。

https://archivesspace.github.io/archivesspace/api/

また、以下のソースコードを確認すると、CreateActionが可能なものは、informationobjectsphysicalobjectsdigitalobjectsに限定されているようでした。

https://github.com/artefactual/atom/tree/qa/2.x/plugins/arRestApiPlugin/modules/api/actions

ただ機械的に一括登録を行いたい場面は、主にinformationobjectsだと考えられるため、これらの機能のみで十分かもしれません。

physical objectsの登録

以下のようなクラスを用意します。

#| export
class ApiClient:
    def __init__(self):

        load_dotenv(override=True)

        self.url = os.getenv("atom_url")
        username = os.getenv("username")
        password = os.getenv("password")

        api_key = os.getenv("api_key")

        if api_key:
            self.headers = {
                "REST-API-Key": api_key,
                "Content-Type": "application/json"
            }
        else:
            # Basic 認証のヘッダーを作成
            auth_string = f"{username}:{password}"
            auth_bytes = auth_string.encode('ascii')
            auth_b64 = base64.b64encode(auth_bytes).decode('ascii')
            
            self.headers = {
                "Authorization": f"Basic {auth_b64}",
                "Content-Type": "application/json"
            }

    def add_physical_objects(self, physical_objects):


        url = f"{self.url}/api/physicalobjects"


        print(url, self.headers, physical_objects)
        response = requests.post(url, headers=self.headers, json=physical_objects)
        
        # レスポンスを確認
        if response.status_code in [200, 201]:
            print("物理オブジェクトが作成されました!")
            print(f"ステータスコード: {response.status_code}")
            print(f"レスポンス: {response.text}")
            
            # 作成されたオブジェクトの情報
            result = response.json()
            print(f"作成された物理オブジェクトID: {result.get('id')}")

            print(json.dumps(result, indent=4))
        else:
            print(f"エラー: {response.status_code}")
            print(f"レスポンス: {response.text}")

以下で実行します。

api_client = ApiClient()

# 物理オブジェクトのデータ
physical_data = {
    "name": "T-01",
    "location": "Example location",
    "type": "Shelf"
}

api_client.add_physical_objects(physical_data)

物理オブジェクトが作成されました!
ステータスコード: 201
レスポンス: {"slug":"t-01"}
作成された物理オブジェクトID: None
{
    "slug": "t-01"
}

結果、以下のように登録されます。

information objectsの登録

親レコードの登録

    def add_information_objects(self, information_objects):
        url = f"{self.url}/api/informationobjects"

        response = requests.post(url, headers=self.headers, json=information_objects)

        if response.status_code in [200, 201]:
            print("情報オブジェクトが作成されました!")
            print(f"ステータスコード: {response.status_code}")
            print(f"レスポンス: {response.text}")
        else:
            print(f"エラー: {response.status_code}")
            print(f"レスポンス: {response.text}")

以下を実行します。

parent_data = {
    "title": "親コレクション",
    "identifier": "PARENT-001",
    "level_of_description": "Collection",
}

api_client.add_information_objects(parent_data)

情報オブジェクトが作成されました!
ステータスコード: 201
レスポンス: {"id":453,"slug":"ftb7-3bzd-8759","parent_id":1}

以下のように登録されました。

identifierを用いた検索

この処理により、登録済みのレコードのslugを取得することができます。

    def find_information_objects(self, identifier):
        url = f"{self.url}/api/informationobjects"
        params = {
            "sq0": identifier,
            "sf0": "identifier",
        }
        response = requests.get(url, headers=self.headers, params=params)
        
        return response.json()

以下のように利用します。

identifier = "PARENT-001"
info_objects = api_client.find_information_objects(identifier)
print(json.dumps(info_objects, indent=4))

{
    "total": 1,
    "results": [
        {
            "reference_code": "PARENT-001",
            "slug": "ftb7-3bzd-8759",
            "title": "\u89aa\u30b3\u30ec\u30af\u30b7\u30e7\u30f3",
            "level_of_description": "Collection"
        }
    ]
}

クエリsq0sf0は以下を参考にします。

https://www.accesstomemory.org/en/docs/2.9/dev-manual/api/browse-io/#api-browse-io

子レコードの登録

親レコードのslugftb7-3bzd-8759とわかったので、以下により登録します。

# 子の情報オブジェクト
child_data = {
    "title": "子レコード",
    "identifier": "CHILD-008",
    "level_of_description": "File",
    "parent_slug": "ftb7-3bzd-8759"
}

api_client.add_information_objects(child_data)

情報オブジェクトが作成されました!
ステータスコード: 201
レスポンス: {"id":471,"slug":"nsaa-ywyw-2sx7","parent_id":453}

結果、以下のように、親レコードの下位レコードとして、子レコードが登録されました。

デジタルオブジェクトの登録

    def add_digital_objects(self, slug, file_path):

        file_name = os.path.basename(file_path)

        # マルチパートフォームデータでファイルをアップロード
        with open(file_path, 'rb') as f:
            files = {
                'file': (file_name, f, 'application/pdf'),
            }

            data = {
                'title': file_name,
                'informationObjectSlug': slug,
                'usage': 'Reference'  # Master または Reference
            }

            do_response = requests.post(f"{self.url}/api/digitalobjects", 
                                  headers=self.headers,  # Content-Type は自動設定
                                  files=files,
                                  data=data)
        
            if do_response.status_code in [200, 201]:
                do_result = do_response.json()
                print(f"デジタルオブジェクト作成成功: ID {do_result.get('id')}")
            else:
                print(f"デジタルオブジェクトの作成に失敗: {do_response.status_code}")
                print(f"エラーメッセージ: {do_response.text}")

ただし、以下のように実行してもエラーが発生してしまいました。

file_path = "_01.pdf"
slug = "nsaa-ywyw-2sx7"

api_client.add_digital_objects(slug, file_path)

この点については、引き続き調査したいと思います。

まとめ

AtoM(Access to Memory)のAPIの使用例について紹介しました。

簡単に調べた限りでは、information objectsのみ、API経由で削除することができるなど、APIによる機能提供が限定的な印象を受けました。

APIによる利用を前提にする場合には、AtoM以外の選択肢を考慮するのもよいかもしれません。

AtoMやArchivesSpaceの導入検討にあたり、参考になりましたら幸いです。

Discussion