ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English

最新の記事

ShExCとShExJの違い

ShExCとShExJの違い

概要 ShExC(ShEx Compact Syntax)とShExJ(ShEx JSON Syntax)の違いについて、ChatGPTによる回答です。誤っている点もあるかもしれませんが、参考になりましたら幸いです。 回答 ShExC(ShEx Compact Syntax)とShExJ(ShEx JSON Syntax)は、どちらもShEx(Shape Expressions)スキーマの表現形式ですが、表記形式や用途が異なります。以下にその違いを説明します。 1. 表記形式 ShExC(ShEx Compact Syntax) : テキストベース のシンタックスです。 人間が読み書きしやすいように設計されており、シンプルかつ簡潔にRDFデータの構造や制約を記述することができます。 主に手動で記述 したり、コード内に直接埋め込む ことを想定して設計されています。 例 : <PersonShape> { foaf:name xsd:string ; foaf:age xsd:integer ? } * これは「Person」という形状が、`foaf:name` という必須の文字列プロパティと、`foaf:age` というオプションの整数プロパティを持つことを示しています。 ShExJ(ShEx JSON Syntax) : JSONベース のシンタックスです。 機械可読性を重視した形式で、ShExスキーマをプログラムやAPIで扱うために設計されています。 ツールやライブラリで処理しやすい ため、特にプログラム間でのデータ交換やAPIレスポンスで使用されることが多いです。 例 : { "type": "Schema", "shapes": [ { "id": "PersonShape", "type": "Shape", "expression": { "type": "EachOf", "expressions": [ { "type": "TripleConstraint", "predicate": "foaf:name", "valueExpr": { "type": "NodeConstraint", "datatype": "xsd:string" } }, { "type": "TripleConstraint", "predicate": "foaf:age", "valueExpr": { "type": "NodeConstraint", "datatype": "xsd:integer" }, "min": 0, "max": 1 } ] } } ] } 2. 用途 ShExC : ...

ShExとSHACLの違い

ShExとSHACLの違い

概要 ShEx(Shape Expressions)Schema と SHACL(Shapes Constraint Language)の違いに関するChatGPTによる回答です。間違っている点もあるかもしれませんが、参考になりましたら幸いです。 回答 ShEx(Shape Expressions)Schema と SHACL(Shapes Constraint Language)は、どちらもRDFデータに対する検証 や制約 を定義するための言語です。両者は同じ目的を持ちながらも、構文 やアプローチ に違いがあります。以下にその違いを説明します。 1. 目的と用途 ShEx(Shape Expressions) : ShExは、RDFデータが特定の形状(Shape)に従っているかどうかを 簡潔に定義し、検証するための言語です。 主に、柔軟で簡単なスキーマ定義 を目的として設計されており、特定の形状にデータが適合するかどうかの判定がしやすいです。 スキーマ検証 が主な用途で、特定のシェイプにデータが従っているかどうかをすばやく確認できます。 SHACL(Shapes Constraint Language) : SHACLは、RDFデータに対して複雑な制約 やルール を定義するために設計されたW3C標準の言語です。 より柔軟で強力な検証機能を持ち、スキーマ定義だけでなく、制約の論理的な定義 や高次の推論 を行うことができます。 例えば、条件付き制約や、プロパティの型、データの依存関係など、より複雑なルールを記述できます。 2. 構文と表現力 ShEx : シンプルでコンパクトな構文 を持ち、RDFデータの形状を定義する際に、簡単に読み書きできるよう設計されています。 直感的で短い記述 でシェイプを定義できるため、学習コストが低く、データ検証を行う際に適しています。 例(ShExC形式): <PersonShape> { foaf:name xsd:string ; foaf:age xsd:integer ? } * これは、「`PersonShape`は、`foaf:name`として文字列型、`foaf:age`として整数型のオプションプロパティを持つ」という意味です。 SHACL : SHACLはRDFグラフ上で定義 され、検証ロジックをRDFのトリプル形式 で記述します。 SPARQLクエリを使って検証を行うことができるため、より複雑なルールや推論が可能です。 例(Turtle形式): ex:PersonShape a sh:NodeShape ; sh:targetClass foaf:Person ; sh:property [ sh:path foaf:name ; sh:datatype xsd:string ; ] ; sh:property [ sh:path foaf:age ; sh:datatype xsd:integer ; sh:maxCount 1 ; ] . * これは「`PersonShape`は、`foaf:name`として文字列型、`foaf:age`として整数型で1つまでのプロパティを持つ」という意味です。 3. 検証方法 ShEx : ...

Omeka SのModel Viewerモジュールを試す

Omeka SのModel Viewerモジュールを試す

概要 Model Viewerは、Omeka S向けのモジュールであり、3Dモデル用のビューワーであるthree.jsを統合しています。 https://github.com/Daniel-KM/Omeka-S-module-ModelViewer 本記事では、本モジュールの使用方法について説明します。 参考 以下で、IIIFを用いた3Dモデルの公開方法を紹介しています。こちらも参考にしてください。 インストール 他の一般的なモジュールのインストール方法と同じです。 使用方法 メディアの詳細画面において、以下のように、3Dビューアが表示されます。 まとめ 操作性についてはUniversal Viewerのほうが優れているように感じましたが、参考になりましたら幸いです。

@samvera/rampビューアのFiles/Markersタブの使用方法

@samvera/rampビューアのFiles/Markersタブの使用方法

概要 IIIF Audio/Visualに対応したビューアの一つである@samvera/rampビューアのFiles/Markersタブの使用方法について調べたので備忘録です。 マニュアル Filesについては、以下に記載がありました。 https://samvera-labs.github.io/ramp/#supplementalfiles また、Markersについては、以下に記載があります。 https://samvera-labs.github.io/ramp/#markersdisplay 使用するデータ 『県政ニュース 第1巻』(県立長野図書館)を使用します。 https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177 Filesタブ renderingプロパティを読むと記載されています。renderingプロパティについては、以下のCookbookにも掲載されています。 https://iiif.io/api/cookbook/recipe/0046-rendering/ 以下のようなスクリプトにより、マニフェストファイルにrenderingプロパティを追加します。 def add_rendering(self, manifest_path): manifest = self.load_manifest(manifest_path) japan_search_id = manifest.homepage[1].id.split("/")[-1] japan_search_api_url = f"https://jpsearch.go.jp/api/item/{japan_search_id}" rendering = ResourceItem( label={ "ja": ["アイテム参照API"], }, id=japan_search_api_url, type="Dataset", format="application/json" ) manifest.rendering = rendering output_path = f"{self.input_dir}/manifest_rendering.json" with open(output_path, "w") as f: f.write(manifest.json(indent=2)) return output_path 以下のようなマニフェストファイルが作成されます。 { "@context": "http://iiif.io/api/presentation/3/context.json", "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/manifest.json", "type": "Manifest", "label": { "ja": [ "県政ニュース 第1巻" ] }, "requiredStatement": { "label": { "ja": [ "Attribution" ] }, "value": { "ja": [ "『県政ニュース 第1巻』(県立長野図書館)を改変" ] } }, "rendering": [ { "id": "https://jpsearch.go.jp/api/item/sdcommons_npl-02FT0102974177", "type": "Dataset", "label": { "ja": [ "アイテム参照API" ] }, "format": "application/json" } ], ... } ビューアでの表示例は以下です。 ...

Omeka SのOAI-PMHリポジトリのresumptionTokenの不具合への対応

Omeka SのOAI-PMHリポジトリのresumptionTokenの不具合への対応

概要 Omeka SのOAI-PMHリポジトリのresumptionTokenにおいて、有効期限内にもかかわらず、以下のように、[badResumptionToken]が出力される事例に遭遇しました。 この不具合への対処方法についてメモします。 対応 以下のファイルについて、$currentTimeと$expirationTimeを比較する処理を加えたところ、有効期限内のtokenが残るようになりました。 ... private function resumeListResponse($token): void { $api = $this->serviceLocator->get('ControllerPluginManager')->get('api'); $expiredTokens = $api->search('oaipmh_repository_tokens', [ 'expired' => true, ])->getContent(); foreach ($expiredTokens as $expiredToken) { $currentTime = new \DateTime(); // 追加 $expirationTime = $expiredToken->expiration(); // 追加 if (!$expiredToken || $currentTime > $expirationTime) { // 追加 $api->delete('oaipmh_repository_tokens', $expiredToken->id()); } // 追加 } 上記のような対応をしなくてもうまくいくケースがあったので、PHPのバージョン等による違いがあるのかもしれません。 参考 参考までに、OAI-PMHリポジトリモジュールによって作成されるテーブルの中身を確認しました。 ...

(非標準)Omeka SのOAI-PMH RepositoryモジュールでDeleteレコードを出力してみる

(非標準)Omeka SのOAI-PMH RepositoryモジュールでDeleteレコードを出力してみる

概要 Omeka SのOAI-PMH RepositoryモジュールでDeleteレコードを出力してみましたので、備忘録です。 背景 以下のモジュールを使用することにより、OAI-PMHのリポジトリ機能を構築することができます。 https://omeka.org/s/modules/OaiPmhRepository/ ただ、確認した限り、Deleteレコードを出力する機能はないようでした。 関連モジュール Omekaの標準機能では、削除されたリソースを保存する機能はないかと思います。 一方、以下のモジュールは削除されたリソースを保持する機能を追加します。 https://github.com/biblibre/omeka-s-module-Necropolis 本モジュールを有効化することにより、以下のように、リソースがいつ誰によって削除されたかを記録できるようになりました。 OAI-PMH Repositoryモジュールへの応用 上記のモジュールで作成される削除されたリソースの情報が格納されるテーブルを使って、Deleteレコードの出力を試みます。 以下のファイルのlistResponse関数に追記します。 private function listResponse($verb, $metadataPrefix, $cursor, $set, $from, $until): void { /** * @var \Omeka\Api\Adapter\Manager $apiAdapterManager * @var \Doctrine\ORM\EntityManager $entityManager */ $apiAdapterManager = $this->serviceLocator->get('Omeka\ApiAdapterManager'); $entityManager = $this->serviceLocator->get('Omeka\EntityManager'); $itemRepository = $entityManager->getRepository(\Omeka\Entity\Item::class); $qb = $itemRepository->createQueryBuilder('omeka_root'); $qb->select('omeka_root'); $query = new ArrayObject; $expr = $qb->expr(); // 以下を追加 if ($set === 'o:deleted') { $settings = $this->serviceLocator->get('Omeka\Settings'); $namespaceId = $settings->get('oaipmhrepository_namespace_id', 'default_namespace'); // 削除済みレコードを necropolis_resource テーブルから取得する $deletedResourceRepository = $entityManager->getRepository(\Necropolis\Entity\NecropolisResource::class); // カスタムエンティティ // oaipmhrepository_expose_mediaに応じて、mediaとitemの取得を分ける $exposeMedia = $settings->get('oaipmhrepository_expose_media', false); // デフォルトはfalse(itemのみ) if ($exposeMedia) { $qb = $deletedResourceRepository->createQueryBuilder('necropolis_resource'); } else { // Itemのみを取得する $qb = $deletedResourceRepository->createQueryBuilder('necropolis_resource') ->andWhere('necropolis_resource.resourceType = :itemType') ->setParameter('itemType', 'Omeka\Entity\Item'); } // 日付フィルタリング if ($from) { $qb->andWhere($expr->gte('necropolis_resource.deleted', ':from')); $qb->setParameter('from', $from); } if ($until) { $qb->andWhere($expr->lte('necropolis_resource.deleted', ':until')); $qb->setParameter('until', $until); } // 結果の制限とオフセット $qb->setMaxResults($this->_listLimit); $qb->setFirstResult($cursor); $paginator = new Paginator($qb, false); $rows = count($paginator); if ($rows == 0) { $this->throwError(self::OAI_ERR_NO_RECORDS_MATCH, new Message('No records match the given criteria.')); // @translate } else { if ($verb == 'ListIdentifiers') { $method = 'appendHeader'; } elseif ($verb == 'ListRecords') { $method = 'appendRecord'; } $verbElement = $this->document->createElement($verb); $this->document->documentElement->appendChild($verbElement); foreach ($paginator as $deletedEntity) { // 削除されたリソースの情報をOAI-PMHレスポンスに追加 $header = $this->document->createElement('header'); $header->setAttribute('status', 'deleted'); // 削除済みレコードとして設定 $identifier = $this->document->createElement('identifier', 'oai:' . $namespaceId . ":" . $deletedEntity->getId()); $header->appendChild($identifier); $datestamp = $this->document->createElement('datestamp', $deletedEntity->getDeleted()->format('Y-m-d\TH:i:s\Z')); $header->appendChild($datestamp); $verbElement->appendChild($header); } // Resumption Token の処理 if ($rows > ($cursor + $this->_listLimit)) { $token = $this->createResumptionToken($verb, $metadataPrefix, $cursor + $this->_listLimit, $set, $from, $until); $tokenElement = $this->document->createElement('resumptionToken', (string) $token->id()); $tokenElement->setAttribute('expirationDate', $token->expiration()->format('Y-m-d\TH:i:s\Z')); $tokenElement->setAttribute('completeListSize', (string) $rows); $tokenElement->setAttribute('cursor', (string) $cursor); $verbElement->appendChild($tokenElement); } elseif ($cursor != 0) { $tokenElement = $this->document->createElement('resumptionToken'); $verbElement->appendChild($tokenElement); } } return; } ... OAI-PMH標準には合致していない実装方法ですが、setにo:deletedを指定すると、削除レコードを返却することができます。 ...

iiif-prezi3を使って、動画に目次を付与する

iiif-prezi3を使って、動画に目次を付与する

概要 iiif-prezi3を使って、動画に目次を付与する方法に関する備忘録です。 セグメントの検出 Amazon Rekognitionのビデオセグメントの検出を用います。 https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/segments.html 以下などでサンプルコードが公開されています。 https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/segment-example.html 使用するデータ 『県政ニュース 第1巻』(県立長野図書館)を使用します。 https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177 マニフェストファイルへの反映 以下の記事などを参考に、マニフェストファイルが作成済みであるとします。 以下のようなスクリプトにより、マニフェストファイルにvttファイルを追加します。 from iiif_prezi3 import Manifest, AnnotationPage, Annotation, ResourceItem, config, HomepageItem, KeyValueString #| export class IiifClient: def load_manifest(self, manifest_path): with open(manifest_path, "r") as f: manifest_json = json.load(f) manifest = Manifest(**manifest_json) return manifest def add_segment(self, manifest_path): manifest = self.load_manifest(manifest_path) path = f"{self.input_dir}/output_segment.json" with open(path, "r") as f: data = json.load(f) range_id = f"{self.prefix}/range" range_toc = manifest.make_range( id=f"{range_id}/r", label="Table of Contents" ) canvas_id = manifest.items[0].id for s in data["Segments"]: s_type = s["Type"] if s_type != "SHOT": continue index = s["Shot Index"] start = s["Start Timestamp (milliseconds)"] / 1000 end = s["End Timestamp (milliseconds)"] / 1000 range_seg = range_toc.make_range( id=f"{range_id}/r{index}", label=f"Segment {index}", ) range_seg.items.append({ "id": f"{canvas_id}#t={start},{end}", "type": "Canvas" }) output_path = f"{self.input_dir}/manifest_segment.json" with open(output_path, "w") as f: f.write(manifest.json(indent=2)) return output_path 以下のようなマニフェストファイルが作成されます。 ...

iiif-prezi3を使って、動画に字幕を設定する

iiif-prezi3を使って、動画に字幕を設定する

概要 iiif-prezi3を使って、動画に字幕を設定する方法に関する備忘録です。 字幕の作成 OpenAIのAPIを使用して字幕ファイルを作成しました。動画のファイルを音声ファイルに変換しています。 from openai import OpenAI from pydub import AudioSegment from dotenv import load_dotenv class VideoClient: def __init__(self): load_dotenv(verbose=True) api_key = os.getenv("OPENAI_API_KEY") self.client = OpenAI(api_key=api_key) def get_transcriptions(self, input_movie_path): audio = AudioSegment.from_file(input_movie_path) # 一時ファイルにオーディオを書き込む with tempfile.NamedTemporaryFile(suffix=".mp3") as temp_audio_file: audio.export(temp_audio_file.name, format="mp3") # MP3形式でエクスポート temp_audio_file.seek(0) # ファイルポインタを先頭に戻す # Whisper APIでトランスクリプトを取得 with open(temp_audio_file.name, "rb") as audio_file: # Whisper APIでトランスクリプトを取得 transcript = self.client.audio.transcriptions.create( model="whisper-1", file=audio_file, response_format="vtt" ) return transcript 使用するデータ 『県政ニュース 第1巻』(県立長野図書館)を使用します。 https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177 マニフェストファイルへの反映 以下の記事などを参考に、マニフェストファイルが作成済みであるとします。 以下のようなスクリプトにより、マニフェストファイルにvttファイルを追加します。 from iiif_prezi3 import Manifest, AnnotationPage, Annotation, ResourceItem, config, HomepageItem, KeyValueString #| export class IiifClient: def load_manifest(self, manifest_path): with open(manifest_path, "r") as f: manifest_json = json.load(f) manifest = Manifest(**manifest_json) return manifest def add_vtt(self, manifest_simple_path): manifest = self.load_manifest(manifest_simple_path) vtt_url = f"{self.prefix}/video.vtt" canvas = manifest.items[0] vtt_anno_page = AnnotationPage(id=f"{canvas.id}/page2") canvas.annotations = [ vtt_anno_page, ] vtt_body = ResourceItem(id=vtt_url, type="Text", format="text/vtt") vtt_anno = Annotation( id=f"{vtt_anno_page.id}/a1", motivation="supplementing", body=vtt_body, target=canvas.id, label = "WebVTT Transcript (machine-generated)" ) vtt_anno_page.add_item(vtt_anno) with open(f"{self.input_dir}/manifest_vtt.json", "w") as f: f.write(manifest.json(indent=2)) 以下のようなマニフェストファイルが作成されます。 ...

iiif-prezi3を使って、動画にアノテーションを付与する

iiif-prezi3を使って、動画にアノテーションを付与する

概要 iiif-prezi3を使って、動画にアノテーションを付与する方法に関する備忘録です。 アノテーションの付与 Amazon Rekognitionのlabel detectionを用います。 https://docs.aws.amazon.com/rekognition/latest/dg/labels.html?pg=ln&sec=ft 以下などでサンプルコードが公開されています。 https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/labels-detecting-labels-video.html 特に、GetLabelDetectionにおける集計をSEGMENTSにすることで、StartTimestampMillisとEndTimestampMillisを得ることができます。 ただし、以下の点に注意が必要です。 SEGMENTS による集計の場合、境界ボックス付きの検出されたインスタンスに関する情報は返されません。 使用するデータ 『県政ニュース 第1巻』(県立長野図書館)を使用します。 https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177 マニフェストファイルへの反映 以下の記事などを参考に、マニフェストファイルが作成済みであるとします。 以下のようなスクリプトにより、マニフェストファイルにvttファイルを追加します。 from iiif_prezi3 import Manifest, AnnotationPage, Annotation, ResourceItem, config, HomepageItem, KeyValueString #| export class IiifClient: def load_manifest(self, manifest_path): with open(manifest_path, "r") as f: manifest_json = json.load(f) manifest = Manifest(**manifest_json) return manifest def add_label_segment(self, manifest_path): manifest = self.load_manifest(manifest_path) label_path = f"{self.input_dir}/output_label_seg.json" with open(label_path, "r") as f: label_seg = json.load(f) canvas = manifest.items[0] labels = label_seg["Labels"] anno_page_id = f"{canvas.id}/page1" anno_page = AnnotationPage(id=anno_page_id) canvas.annotations.append(anno_page) for i in range(len(labels)): label = labels[i] start = label["StartTimestamp"] / 1000 end = label["EndTimestamp"] / 1000 name = label["Label"]["Name"] anno_id = f"{anno_page_id}/a{i}" anno = Annotation( id=anno_id, motivation="tagging", target=canvas.id + "#t=" + str(start) + "," + str(end), body={ "type": "TextualBody", "value": name, "format": "text/plain", } ) anno_page.add_item( anno ) output_path = f"{self.input_dir}/manifest_label_seg.json" with open(output_path, "w") as f: f.write(manifest.json(indent=2)) return output_path 以下のようなマニフェストファイルが作成されます。 ...

GakuNin RDMとAmazon S3を接続し、Archivematicaでファイルを処理する

GakuNin RDMとAmazon S3を接続し、Archivematicaでファイルを処理する

概要 GakuNin RDMとAmazon S3を接続し、Archivematicaでファイルを処理する方法に関する備忘録です。 https://rcos.nii.ac.jp/service/rdm/ 背景 以下の記事で、ArchivematicaでAmazon S3を処理対象とする方法を記載しました。 これにより、指定したバケットにファイルやフォルダをアップロードすることにより、それらをArchivematicaの処理対象として、AIPやDIPを作成することができます。 ただし、このままではプロジェクトのメンバー毎にIAMユーザを作成する必要がありました。 GakuNin RDMの利用 今回はメンバー全員がGakuNin RDMのプロジェクトのメンバーとして登録されていました。 そこで、プロジェクトにAmazon S3を接続して、GakuNin RDMからS3にファイルをアップロードできるようにしてみます。 これにより、IAMユーザの管理が不要になります。 設定方法 アドオンを選択します。 Amazon S3を有効にします。 IAMユーザで作成したアクセスキーIDとシークレットアクセスキーを入力することで、バケットの一覧が表示されます。 結果、GakuNin RDMからAmazon S3にファイルをアップロードできるようになりました。 Archivematicaからも同バケットを以下のように参照できるため、ここからAIPなどを作成することができます。 まとめ GakuNin RDMを利用可能な方に限られてしまいますが、参考になりましたら幸いです。

iiif-prezi3を使って、動画に関するIIIF v3マニフェストを作成する

iiif-prezi3を使って、動画に関するIIIF v3マニフェストを作成する

概要 iiif-prezi3を使って、動画に関するIIIF v3マニフェストを作成する機会がありましたので、備忘録です。 https://github.com/iiif-prezi/iiif-prezi3 参考 IIIFマニフェストファイルの例、およびiiif-prezi3を使った実装例は、IIIF Cookbookで公開されています。 以下、動画に関するIIIF v3マニフェストを作成する例です。 https://iiif.io/api/cookbook/recipe/0003-mvm-video/ iiif-prezi3を使った実装例は以下で公開されています。 https://iiif-prezi.github.io/iiif-prezi3/recipes/0003-mvm-video/ from iiif_prezi3 import Manifest, AnnotationPage, Annotation, ResourceItem, config config.configs['helpers.auto_fields.AutoLang'].auto_lang = "en" manifest = Manifest(id="https://iiif.io/api/cookbook/recipe/0003-mvm-video/manifest.json", label="Video Example 3") canvas = manifest.make_canvas(id="https://iiif.io/api/cookbook/recipe/0003-mvm-video/canvas") anno_body = ResourceItem(id="https://fixtures.iiif.io/video/indiana/lunchroom_manners/high/lunchroom_manners_1024kb.mp4", type="Video", format="video/mp4") anno_page = AnnotationPage(id="https://iiif.io/api/cookbook/recipe/0003-mvm-video/canvas/page") anno = Annotation(id="https://iiif.io/api/cookbook/recipe/0003-mvm-video/canvas/page/annotation", motivation="painting", body=anno_body, target=canvas.id) hwd = {"height": 360, "width": 480, "duration": 572.034} anno_body.set_hwd(**hwd) hwd["width"] = 640 canvas.set_hwd(**hwd) anno_page.add_item(anno) canvas.add_item(anno_page) print(manifest.json(indent=2)) まとめ 他にも多くのサンプルや実装例が公開されています。参考になりましたら幸いです。

Next.jsでアンダースコアから始まるURLセグメントを利用する

Next.jsでアンダースコアから始まるURLセグメントを利用する

概要 </api/_search> のようなAPIを作成するにあたり、アンダースコアから始まるURLセグメントを作成する方法を調べましたので、備忘録です。 方法 以下に記載がありました。 https://nextjs.org/docs/app/building-your-application/routing/colocation#:~:text=js file conventions.-,Good to know,-While not a 以下、日本語訳です。 URLセグメントでアンダースコアから始まる部分を作成するには、フォルダー名に %5F(アンダースコアのURLエンコード形式)を付けてください。例: %5FfolderName。 /api/%5Fsearch/route.ts のようにファイルを作成することで、解決することができました。 まとめ 参考になりましたら幸いです。

@elastic/search-uiのsetFilterの不具合対応

@elastic/search-uiのsetFilterの不具合対応

概要 @elastic/search-uiのsetFilterについて、不具合が報告されています。 https://github.com/elastic/search-ui/issues/1057 この不具合について、すでに以下のコミットで修正されています。 https://github.com/elastic/search-ui/pull/1058 ただ、2024年10月7日時点において、上記の対応がなされた最新版がリリースされていません。 そこで、独自にビルドしてリリースすることを試みましたので、その手順に関する備忘録です。 修正 まず、リポジトリをフェッチしました。 https://github.com/nakamura196/search-ui そして、以下の修正を加えました。 https://github.com/nakamura196/search-ui/commit/f7c7dc332086ca77a2c488f3de8780bbeb683324 具体的には、package.jsonと.npmrcに変更を加えました。 パッケージの公開 以下の手順により、パッケージを公開します。 1. GitHub パーソナルアクセストークンを生成 GitHub Package Registry にアクセスするためには、GitHub のパーソナルアクセストークンが必要です。以下の手順でトークンを作成します: GitHubのアカウントにログインします。 右上のプロフィール画像をクリックし、「Settings」を選択。 左側のメニューから「Developer settings」を選択。 「Personal access tokens (classic)」を選択し、「Generate new token」をクリック。 必要な権限(write:packages と read:packages など)を選択して、トークンを生成します。 2. npm にログイン 次に、GitHub Package Registry にログインするために以下のコマンドを実行します: npm login --registry=https://npm.pkg.github.com 実行すると、以下の情報が要求されます: Username: GitHub のユーザー名 Password: 先ほど生成したパーソナルアクセストークン Email: GitHub に登録しているメールアドレス 3. パッケージを再度公開 認証が完了したら、再度 npm publish を実行してパッケージを公開します。 npm publish --registry=https://npm.pkg.github.com 結果、以下のようなページが作成されました。 利用 利用するリポジトリにおいて、以下を作成します。vercelでのビルドを想定して、GITHUB_TOKENは環境変数から参照するようにしました。 @nakamura196:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} まとめ 結局上記の方法では、もともとの課題であったsetFilterの修正はうまくいかなかったのですが、パッケージの公開方法に関して、参考になる部分がありましたら幸いです。 ...

Omeka Sのテーマの一覧を視覚的に確認するページを作成するプログラム

Omeka Sのテーマの一覧を視覚的に確認するページを作成するプログラム

概要 以下の記事で、Omeka Sのテーマの一覧を視覚的に確認するページを紹介しました。 上記のページ作成に使用したプログラムを以下のリポジトリで公開しました。 https://github.com/nakamura196/OmekaS まとめ 同様の作業を行うにあたり、参考になりましたら幸いです。

rico-converterを試す

rico-converterを試す

概要 rico-converterを試す機会がありましたので、備忘録です。 https://github.com/ArchivesNationalesFR/rico-converter 以下のように説明されています。 A tool to convert EAC-CPF and EAD 2002 XML files to RDF datasets conforming to Records in Contexts Ontology (RiC-O) (機会翻訳)EAC-CPFやEAD 2002のXMLファイルを、Records in Contexts Ontology(RiC-O)に準拠したRDFデータセットに変換するためのツール 変換する 以下に説明があります。 https://archivesnationalesfr.github.io/rico-converter/en/GettingStarted.html まず、以下から最新のzipファイルをダウンロードして、展開します。 https://github.com/ArchivesNationalesFR/rico-converter/releases/latest サンプルデータとして、input-eacとinput-eadがあるので、これらをRDFに変換します。 input-eac 本フォルダ内にあるinput-eac/FRAN_NP_051151.xmlについて、ChatGPTによる解説は以下です。 ! このXMLファイルは、EAC-CPF(Encoded Archival Context for Corporate Bodies, Persons, and Families)という形式で記述されており、フランスの文化省(Ministère de la Culture)などの組織に関する情報を体系的に整理したものです。主な要素は次の通りです: • Controlセクション: レコードに関するメタデータを含んでいます。これには、レコードID、言語の宣言、更新履歴、使用した資料の出典などが含まれます。 • Identityセクション: 法人(ここではフランスの文化省)に関する基本的な情報が記載されています。文化省の名前が変遷してきたことを示す複数の名称や、各名称が使われていた期間も詳細に記述されています。 • Descriptionセクション: 文化省の存在期間、法的地位、主要な機能、任務、さらにその歴史的な変遷に関する詳細な説明が含まれています。たとえば、1959年に設立され、法令に基づいてどのように運営されてきたか、組織の構造がどのように変化したかが説明されています。 • Relationsセクション: 他の組織や人物との関係を示しています。ここには、フランス国内の関連機関や教育機関、さらには歴代の文化相との関係が詳細に記載されており、外部のリソース(アーカイブやオンラインの情報)へのリンクも含まれています。 このXMLは、アーカイブや組織に関する情報を標準化された形式で記述し、他のデータベースやシステムと連携することを目的としています。特に、組織の歴史や関係性を追跡する際に有用です。 以下を実行します。 sh ricoconverter.sh デフォルトのまま実行すると、output-eac-20241005といったフォルダが作成され、その中にRDFファイルが出力されます。 input-ead 本フォルダ内にあるinput-ead/FRAN_IR_003500.xmlについて、ChatGPTによる解説は以下です。 ...

Hugging Face SpacesとYOLOv5モデル(顔コレデータセットで学習済み)を使った推論アプリの構築

Hugging Face SpacesとYOLOv5モデル(顔コレデータセットで学習済み)を使った推論アプリの構築

概要 Hugging Face Spacesと、YOLOv5モデル(顔コレデータセットで学習済み)を使った推論アプリを作成しました。 人文学オープンデータ共同利用センターが公開する顔コレデータセットは以下です。 Yingtao Tian, Chikahiko Suzuki, Tarin Clanuwat, Mikel Bober-Irizar, Alex Lamb, Asanobu Kitamoto, “KaoKore: A Pre-modern Japanese Art Facial Expression Dataset”, arXiv:2002.08595. http://codh.rois.ac.jp/face/dataset/ 推論アプリは、以下のURLからお試しいただけます。 https://huggingface.co/spaces/nakamura196/yolov5-face また以下のURLからソースコードや学習済みモデルをダウンロードすることができます。同様のアプリケーションを開発される際の参考になれば幸いです。 https://huggingface.co/spaces/nakamura196/yolov5-face/tree/main なお、アプリケーションの開発には、以下のSpaceを参考にしています。 https://huggingface.co/spaces/pytorch/YOLOv5 使い方 画像をアップロードするか、Examplesから画像を選択してご利用いただけます。以下のように認識結果を確認することができます。 『文正草子』(日本古典籍データセット(国文研所蔵)CODH配信) まとめ 検出精度が十分でない点がありますが、参考になりましたら幸いです。 現在はYOLOv5を用いたモデルですが、今後より新しいモデルも試してみたいと思います。

ModuleNotFoundError: No module named 'huggingface_hub.utils._errors'対応

ModuleNotFoundError: No module named 'huggingface_hub.utils._errors'対応

概要 Hugging FaceのSpacesにアプリをデプロイした際、以下のエラーが発生しました。このエラーに対する備忘録です。 Creating new Ultralytics Settings v0.0.6 file ✅ View Ultralytics Settings with 'yolo settings' or at '/home/user/.config/Ultralytics/settings.json' Update Settings with 'yolo settings key=value', i.e. 'yolo settings runs_dir=path/to/dir'. For help see https://docs.ultralytics.com/quickstart/#ultralytics-settings. WARNING ⚠️ DetectMultiBackend failed: No module named 'huggingface_hub.utils._errors' Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/yolov5/helpers.py", line 38, in load_model model = DetectMultiBackend( File "/usr/local/lib/python3.10/site-packages/yolov5/models/common.py", line 338, in __init__ result = attempt_download_from_hub(w, hf_token=hf_token) File "/usr/local/lib/python3.10/site-packages/yolov5/utils/downloads.py", line 150, in attempt_download_from_hub from huggingface_hub.utils._errors import RepositoryNotFoundError ModuleNotFoundError: No module named 'huggingface_hub.utils._errors' During handling of the above exception, another exception occurred: 参考 以下の記事が参考になりました。 ...

Omeka Sで3Dモデルを公開する

Omeka Sで3Dモデルを公開する

概要 Omeka Sで3Dモデルを公開する方法について調べてみましたので、備忘録です。 結果、以下のように3DモデルをOmeka Sで扱うことができました。 https://omeka.aws.ldas.jp/s/sample/item/43 バージョン 使用するOmeka Sおよびモジュールのバージョンは以下です。 Omeka S 4.1.1 Common 3.4.62 IIIF Server 3.6.21 Universal Viewer 3.6.9 モジュールのインストール Common, IIIF Server, Universal Viewerモジュールをインストールします。 モジュールの設定 IIIF Serverモジュールについて、設定を2点行います。 まず、Default IIIF api version of the manifestを3にします。 また、Default IIIF image api versionをNo image serverにします。 glbファイルのアップロード 以下で公開されているglbファイルを利用させていただきます。 https://ft-lab.github.io/gltf.html 以下のように、apple.glbをメディアとして、新規のアイテムを登録します。 結果、サイトの詳細ページにおいて、Universal Viewer上に3Dモデルが表示されます。 https://omeka.aws.ldas.jp/s/sample/item/43 作成されるマニフェストファイルは以下です。 { "@context": "http://iiif.io/api/presentation/3/context.json", "id": "https://omeka.aws.ldas.jp/iiif/3/43/manifest", "type": "Manifest", "label": { "none": [ "glb" ] }, "metadata": [ { "label": { "none": [ "Title" ] }, "value": { "none": [ "glb" ] } } ], "rights": "https://rightsstatements.org/vocab/CNE/1.0/", "provider": [ { "id": "https://omeka.aws.ldas.jp/", "type": "Agent", "label": { "none": [ "Omeka S" ] } } ], "viewingDirection": "left-to-right", "seeAlso": [ { "id": "https://omeka.aws.ldas.jp/api/items/43", "type": "Dataset", "label": { "none": [ "Api rest json-ld" ] }, "format": "application/ld+json", "profile": "https://omeka.aws.ldas.jp/api-context" } ], "homepage": [ { "id": "https://omeka.aws.ldas.jp/s/aaa/item/43", "type": "Text", "label": { "none": [ "Resource in site: aaa" ] }, "format": "text/html", "language": [ "ja" ] } ], "items": [ { "@context": "http://iiif.io/api/presentation/3/context.json", "id": "https://omeka.aws.ldas.jp/iiif/3/43/canvas/p1", "type": "Canvas", "label": { "none": [ "1" ] }, "rights": "https://rightsstatements.org/vocab/CNE/1.0/", "items": [ { "id": "https://omeka.aws.ldas.jp/iiif/3/43/annotation-page/44", "type": "AnnotationPage", "items": [ { "id": "https://omeka.aws.ldas.jp/iiif/3/43/annotation/44", "type": "Annotation", "motivation": "painting", "body": { "id": "https://omeka.aws.ldas.jp/files/original/c28f7107525270b03b4314e734bbdd318d0342a4.glb", "type": "Model", "format": "model/gltf-binary", "service": [], "height": null, "width": null, "duration": null }, "target": "https://omeka.aws.ldas.jp/iiif/3/43/canvas/p1", "label": { "none": [ "[Untitled]" ] } } ] } ] } ] } まとめ Omeka Sと関連モジュールを使用することで、比較的に3Dモデルを公開することができました。 ...

pythonを使ってcvatのデータを操作する

pythonを使ってcvatのデータを操作する

概要 pythonを使ってcvatのデータを操作する機会がありましたので、備忘録です。 セットアップ 今回はDockerを使って起動します。 git clone https://github.com/cvat-ai/cvat --depth 1 cd cvat docker compose up -d アカウントの作成 http://localhost:8080にアクセスして、アカウントを作成します。 Pythonによる操作 まず、以下のライブラリをインストールします。 pip install cvat-sdk アカウントの情報を.envに記載します。 host=http://localhost:8080 username= password= インスタンスの作成 import os from dotenv import load_dotenv import json from cvat_sdk.api_client import Configuration, ApiClient, models, apis, exceptions from cvat_sdk.api_client.models import PatchedLabeledDataRequest import requests from io import BytesIO load_dotenv(verbose=True) host = os.environ.get("host") username = os.environ.get("username") password = os.environ.get("password") configuration = Configuration( host=host, username=username, password=password ) api_client = ApiClient(configuration) タスクの作成 task_spec = { 'name': '文字の検出', "labels": [{ "name": "文字", "color": "#ff00ff", "attributes": [ { "name": "score", "mutable": True, "input_type": "text", "values": [""] } ] }], } try: # Apis can be accessed as ApiClient class members # We use different models for input and output data. For input data, # models are typically called like "*Request". Output data models have # no suffix. (task, response) = api_client.tasks_api.create(task_spec) except exceptions.ApiException as e: # We can catch the basic exception type, or a derived type print("Exception when trying to create a task: %s\n" % e) print(task) 以下のような結果が得られます。 ...

Omeka SでのCSRF: Value is required and can’t be emptyエラーへの対応

Omeka SでのCSRF: Value is required and can’t be emptyエラーへの対応

概要 Omeka Sにおいて、多くのメディアが関連づけられたアイテムを保存しようとする際、CSRF: Value is required and can’t be emptyというエラーメッセージが表示され、保存が完了しない事象に遭遇しました。 本記事では、このエラーへの対処方法について説明します。 関連記事 以下の記事などで言及されています。既知のエラーのようで、php.iniを変更する必要があると述べられています。 https://forum.omeka.org/t/csrf-value-is-required-and-cant-be-empty/15421 https://github.com/omeka/omeka-s/issues/1472 対処方法 以下にチャットGPTによる回答を掲載します。 php.ini で max_input_vars を設定するには、以下の手順を実行してください。 php.ini ファイルを開きます。php.ini の場所は、環境によって異なるため、以下のいずれかのディレクトリにあることが多いです: /etc/php/8.x/apache2/php.ini (Apache) /etc/php/8.x/cli/php.ini (CLI) /etc/php/8.x/fpm/php.ini (PHP-FPM) max_input_vars の値を設定または変更します。設定するために、次の行を探します。または、存在しない場合は新しく追加します。 max_input_vars = 1000 1000 という値はデフォルトですが、必要に応じて大きな数に変更できます。例えば、5000に変更する場合は次のようにします。 max_input_vars = 5000 php.ini ファイルを保存します。 Webサーバーを再起動して設定を反映させます。例えば、Apache を使っている場合は、以下のコマンドを実行します。 sudo systemctl restart apache2 PHP-FPM を使用している場合は、次のように実行します。 sudo systemctl restart php8.x-fpm これで max_input_vars が設定され、Webサーバーがその設定を反映します。 まとめ 今回はmax_input_vars = 5000に設定することで、上記エラーを解消することができました。 同様のことでお困りの方の参考になりましたら幸いです。