Skip to content

feat: ワークスペース登録で arXiv ID からの自動補完に対応 - #1947

Open
mhaya wants to merge 1 commit into
develop_v2.1.0from
feat/workspace-arxiv-id-autofill
Open

mhaya wants to merge 1 commit into
develop_v2.1.0from
feat/workspace-arxiv-id-autofill

Conversation

@mhaya

@mhaya mhaya commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

概要

ワークスペースのアイテム登録画面(/workspace/item_registration)の DOI 入力欄で、DOI に加えて arXiv ID からも arXiv のメタデータを自動補完できるようにします。

目的

これまで arXiv への問い合わせは search_query=doi:<DOI> だけでした。この検索は arXiv のレコードに登録された出版社 DOI(arxiv:doi)が対象のため、次のケースでは補完できませんでした。

  • arXiv ID(2301.00001 など)を入力した場合
  • arXiv 自身の DOI(10.48550/arXiv.xxxx)を入力した場合
  • 出版社 DOI が登録されていないプレプリントの場合

変更内容

  • api.py: extract_arxiv_id() を追加。arXiv ID と判定した入力は id_list= で問い合わせ、それ以外は従来どおり search_query=doi: で問い合わせる
    • 受け付ける形式: 新形式 2301.00001 / 旧形式 hep-th/9901001、arXiv: 接頭辞、vN 付き、arxiv.org/abs|pdf の URL、10.48550/arXiv. の DOI
    • 通常の DOI は 10. で始まるため、arXiv ID と誤判定しない
  • config.py: WEKO_WORKSPACE_ARXIV_ID_API_URL を追加(既存の WEKO_WORKSPACE_ARXIV_API_URL は変更なし)
  • utils.py: arXiv の応答に entry が無い(該当なし)場合とエラー entry の場合は、補完なしとして [] を返す。複数件ヒットしたときは先頭の 1 件を使う
    • 従来は arXiv に無い DOI を入れるだけで KeyError になり、「An error have occurred!」と表示されていた
  • 入力欄のプレースホルダーを「DOIまたはarXiv IDを入力してください。」に変更し、JS のアラート文言も合わせた
  • 翻訳: 既存の .mo には .po に無い訳(Creative Commons のライセンス名など 18 件)が入っていたため、.po から再コンパイルせず、既存の .mo に今回の 1 件だけを差し替えた

動作確認

  • scripts/ci/run-local.sh weko-workspace: 293 passed, 1 xfailed
    • 追加テスト: extract_arxiv_id の判定 23 パターン、id_list の URL 生成、該当なし・エラー entry・空リスト・複数件の扱い
  • arXiv API の実際の応答で確認
    • id_list=1706.03762 → 1 件ヒット
    • search_query=doi:10.48550/arXiv.1706.03762 → 0 件(entry なし)。DOI 検索では arXiv DOI が引けないことと、該当なしの応答形式を確認
  • 画面での確認方法: ワークスペース登録画面で 1706.03762 または https://arxiv.org/abs/1706.03762 を入力して「取得」を押し、「arXiv MetaData」が選べるようになることを確認する

補足

  • black / isort はファイル全体に未適用のため、今回は実行していない(実行すると無関係な差分が大量に出る)。追加・変更した行については flake8 の指摘なし

🤖 Generated with Claude Code

Summary by Sourcery

Support arXiv ID-based metadata autofill in workspace item registration while making unsuccessful arXiv lookups return no results safely.

New Features:

  • Enable workspace item registration to retrieve arXiv metadata from supported arXiv IDs, URLs, and arXiv DOIs in addition to publisher DOIs.

Bug Fixes:

  • Handle missing, invalid, empty, and multiple arXiv API entries without raising errors, using the first valid result when multiple entries are returned.

Enhancements:

  • Update the registration input guidance and localized messages to accept either a DOI or arXiv ID.

Tests:

  • Add coverage for arXiv identifier extraction, ID-based API requests, and arXiv response edge cases.

@sourcery-ai

sourcery-ai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

ワークスペース登録のメタデータ自動補完を、通常 DOI に加えて arXiv ID・各種 URL・arXiv DOI からも利用できるようにし、arXiv API の未該当・エラー応答で失敗しないよう入力判定、問い合わせ先、応答処理、UI 文言、テストを更新した。

Sequence diagram for arXiv ID metadata lookup

sequenceDiagram
    actor User
    participant UI as WorkspaceRegistrationUI
    participant arXivURL
    participant ArxivAPI as arXivAPI
    participant Utils as MetadataUtils

    User->>UI: Enter DOI or arXiv ID
    User->>UI: Click metadata retrieval
    UI->>arXivURL: __init__(input)
    arXivURL->>arXivURL: extract_arxiv_id(input)
    alt arXiv ID detected
        UI->>arXivURL: _create_url()
        arXivURL->>ArxivAPI: GET id_list=arXivID
    else Regular DOI
        UI->>arXivURL: _create_url()
        arXivURL->>ArxivAPI: GET search_query=doi:DOI
    end
    ArxivAPI-->>Utils: API response
    Utils->>Utils: get_arXiv_record_data(input, item_type_id)
    alt Valid entry
        Utils-->>UI: Metadata result
    else No entry or error entry
        Utils-->>UI: Empty result
    end
Loading

Flow diagram for arXiv identifier extraction

flowchart LR
    Input["DOI, arXiv ID, URL, or arXiv DOI"] --> Extract[extract_arxiv_id]
    Extract -->|arXiv ID formats match| ID["Normalized arXiv ID"]
    Extract -->|Regular DOI or invalid input| DOI["No arXiv ID"]
    ID --> IDURL["id_list API URL"]
    DOI --> DOIURL["search_query=doi API URL"]
Loading

File-Level Changes

Change Details Files
arXiv ID を入力として認識し、ID 検索と DOI 検索を使い分けるようにした。
  • 新旧形式、接頭辞、バージョン、abs/pdf URL、arXiv DOI から ID を抽出する判定処理を追加
  • arXiv ID は id_list、通常の DOI は従来の search_query=doi: で問い合わせる URL 生成に変更
  • ID 検索用 API URL 設定を追加
  • 入力形式の判定と生成 URL をテストで網羅
modules/weko-workspace/weko_workspace/api.py
modules/weko-workspace/weko_workspace/config.py
modules/weko-workspace/tests/test_api.py
arXiv API の該当なし・エラー・複数件応答を安全に処理するようにした。
  • entry が存在しない、空、またはエラー entry の場合は空結果を返す
  • 複数 entry は先頭の 1 件に正規化して後続の補完処理へ渡す
  • 各応答形式と先頭 entry の採用をテスト
modules/weko-workspace/weko_workspace/utils.py
modules/weko-workspace/tests/test_utils.py
登録画面で arXiv ID を入力できることを UI 文言に反映した。
  • プレースホルダーと未入力アラートを DOI または arXiv ID 対応に変更
  • 対応する翻訳カタログとコンパイル済み翻訳を更新
modules/weko-workspace/weko_workspace/templates/weko_workspace/item_register.html
modules/weko-workspace/weko_workspace/static/js/weko_workspace/workspace_register.js
modules/weko-workspace/weko_workspace/translations/en/LC_MESSAGES/messages.po
modules/weko-workspace/weko_workspace/translations/en/LC_MESSAGES/messages.mo
modules/weko-workspace/weko_workspace/translations/ja/LC_MESSAGES/messages.po
modules/weko-workspace/weko_workspace/translations/ja/LC_MESSAGES/messages.mo
modules/weko-workspace/weko_workspace/translations/messages.pot

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a2abe3d8-6d30-4139-b27f-d98a064718aa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

🔍 Claude レビュー統合

Claude の追加指摘 1 件 — 🔴 高 0 / 🟠 中 0 / 🟡 低 1

# 出所 箇所 指摘 判定 修正案
1 Claude modules/weko-workspace/weko_workspace/utils.py:2447 arXiv ID で取得した論文は arxiv:doi を持たないことが多く、relation の DOI 値が None になる 🟡 追加指摘(低) あり

1. 🟡 [低] arXiv ID で取得した論文は arxiv:doi を持たないことが多く、relation の DOI 値が None になる(Claude の追加指摘)

modules/weko-workspace/weko_workspace/utils.py:2447 (1/2 パス)

これまで arXiv の検索は DOI 指定だけだったので、ヒットした entry には arxiv:doi が付いていました。今回 id_list 検索を追加したため、ジャーナル DOI が無い preprint も取得できます。その場合 get_arXiv_relation_data は @​value: None の DOI/isVersionOf 要素を無条件に作り、空の関連識別子がメタデータ補完に混ざります。

修正案

data_doi が falsy のときは DOI 用の relation 要素を追加しないようにする(if data_doi: で囲む)。arxiv:doi が無い entry を使うテストも追加する。

根拠
new_data_doi['@value'] = data_doi  # data_doi は entry.get('arxiv:doi') で、None になり得る

確認: utils.py:2446-2447 と 2538-2565 を読みました。None チェックはありません。build_record_model が None 値をどう扱うかは未確認です。


次にすること: 検証した範囲では修正が必要な問題は見つかりませんでした。arXivURL の呼び出し元は utils.py の get_arXiv_record_data だけで、views.py のエンドポイントはそこから呼ばれており、戻り値の形も変わっていません。ID は正規表現で検証してから id_list に使っているため、URL への入力混入も起きません。既存のレビューは自動生成の要約とスキップ通知だけで、裁定すべき指摘スレッドはありませんでした。


モデル sonnet / 2 回実行して和集合 / コスト $0.2750。同じ入力でも結果が揺れるため複数回まわし、一部のパスでしか挙がらなかったものには回数を添えています

他レビューを踏まえた自動レビューです。誤りが含まれることがあります。

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 5 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="modules/weko-workspace/weko_workspace/api.py" line_range="483" />
<code_context>
         """
         if not doi:
             raise ValueError('DOI is required.')
+        self._arxiv_id = extract_arxiv_id(doi)
         self._doi = "/".join(doi.strip().strip("/").split("/")[-2:])
         if timeout:
</code_context>
<issue_to_address>
**Cached lookups return wrong metadata**

When two ID/item-type combinations whose concatenated values are identical are requested within the cache timeout, `cached_api_json` builds its cache key by concatenating the ID and item-type ID without a separator, so `get_arXiv_record_data` can return the first lookup's cached record for the other combination and the caller receives incorrect metadata.

Build the cache key from an unambiguous representation of both the ID and item-type ID.
</issue_to_address>

### Comment 2
<location path="modules/weko-workspace/weko_workspace/api.py" line_range="457" />
<code_context>
+    """
+    if not isinstance(value, str):
+        return None
+    value = ARXIV_PREFIX_PATTERN.sub('', value.strip().rstrip('/'))
+    if value.lower().endswith('.pdf'):
+        value = value[:-4]
</code_context>
<issue_to_address>
**Valid arXiv URLs fail autofill**

When a user pastes an arXiv abstract or PDF URL containing a query string or fragment, `extract_arxiv_id` removes the URL prefix but leaves the query string or fragment attached, so `ARXIV_ID_PATTERN` rejects the identifier and the lookup falls back to DOI search without finding arXiv metadata.

Remove the query string or fragment before validating the extracted arXiv identifier.

Also at `modules/weko-workspace/weko_workspace/api.py:438-442`.
</issue_to_address>

### Comment 3
<location path="modules/weko-workspace/weko_workspace/utils.py" line_range="2404-2406" />
<code_context>
+    if isinstance(entry, list):
+        entry = entry[0] if entry else None
+        feed['entry'] = entry
+    if not isinstance(entry, dict) \
+            or '/api/errors' in str(entry.get('id', '')):
+        return result
     api_data = get_arXiv_data_by_key(api_response, 'all')
     items = ItemTypes.get_by_id(item_type_id)
</code_context>
<issue_to_address>
**API outage appears as no match**

When the arXiv service responds with a non-200 status, `arXivURL.get_data` leaves `response` empty and `error` blank for non-200 statuses, so this guard returns `[]`; the view then reports an ordinary no-result response instead of surfacing the service failure.

Log non-200 responses and set an error value so the view can distinguish service failures from genuine no-match results.
</issue_to_address>

### Comment 4
<location path="modules/weko-workspace/tests/test_utils.py" line_range="3689" />
<code_context>
+        {"feed": {"entry": {"id": "http://arxiv.org/api/errors#incorrect_id_format_for_abc", "title": "Error"}}},
+        {"feed": {"entry": []}},
+    ]:
+        with patch('weko_workspace.utils.arXivURL.get_data', return_value={'response': response, 'error': ''}):
+            with patch('weko_workspace.utils.get_arXiv_data_by_key') as mock_by_key:
+                current_cache.delete('arXiv_data2301.000011')
</code_context>
<issue_to_address>
**Added tests fail Black formatting**

When the module's `black .` formatting check runs, black reformats these newly added long patch and fixture lines, so the module's formatting check reports findings instead of passing.

Reformat the added test expressions with Black.

Also at `modules/weko-workspace/tests/test_utils.py:3697`.
</issue_to_address>

### Comment 5
<location path="modules/weko-workspace/weko_workspace/api.py" line_range="483" />
<code_context>
         """
         if not doi:
             raise ValueError('DOI is required.')
+        self._arxiv_id = extract_arxiv_id(doi)
         self._doi = "/".join(doi.strip().strip("/").split("/")[-2:])
         if timeout:
</code_context>
<issue_to_address>
**arXiv IDs remain documented as DOIs**

`arXivURL` now accepts arXiv IDs as well as DOIs, but its constructor documentation still describes only a DOI parameter. Callers relying on that contract can overlook the newly supported input forms.

Update the constructor and `get_arXiv_record_data` docstrings to state that they accept either a DOI or an arXiv identifier/URL.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and if the ID lookup returns incorrect metadata, that metadata could be carried into a workspace record when the user completes registration; the record can be corrected, but reverting will not undo records already created. Reverting does stop future lookups from this feature.

Blocking findings: modules/weko-workspace/weko_workspace/api.py:483, modules/weko-workspace/weko_workspace/api.py:457, modules/weko-workspace/weko_workspace/utils.py:2406


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

"""
if not doi:
raise ValueError('DOI is required.')
self._arxiv_id = extract_arxiv_id(doi)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Cached lookups return wrong metadata

When two ID/item-type combinations whose concatenated values are identical are requested within the cache timeout, cached_api_json builds its cache key by concatenating the ID and item-type ID without a separator, so get_arXiv_record_data can return the first lookup's cached record for the other combination and the caller receives incorrect metadata.

Build the cache key from an unambiguous representation of both the ID and item-type ID.

Prompt for AI agents
In `modules/weko-workspace/weko_workspace/api.py` at line 483:

**Cached lookups return wrong metadata**

When two ID/item-type combinations whose concatenated values are identical are requested within the cache timeout, `cached_api_json` builds its cache key by concatenating the ID and item-type ID without a separator, so `get_arXiv_record_data` can return the first lookup's cached record for the other combination and the caller receives incorrect metadata.

Build the cache key from an unambiguous representation of both the ID and item-type ID.

"""
if not isinstance(value, str):
return None
value = ARXIV_PREFIX_PATTERN.sub('', value.strip().rstrip('/'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Valid arXiv URLs fail autofill

When a user pastes an arXiv abstract or PDF URL containing a query string or fragment, extract_arxiv_id removes the URL prefix but leaves the query string or fragment attached, so ARXIV_ID_PATTERN rejects the identifier and the lookup falls back to DOI search without finding arXiv metadata.

Remove the query string or fragment before validating the extracted arXiv identifier.

Also at modules/weko-workspace/weko_workspace/api.py:438-442.

Prompt for AI agents
In `modules/weko-workspace/weko_workspace/api.py` at line 457:

**Valid arXiv URLs fail autofill**

When a user pastes an arXiv abstract or PDF URL containing a query string or fragment, `extract_arxiv_id` removes the URL prefix but leaves the query string or fragment attached, so `ARXIV_ID_PATTERN` rejects the identifier and the lookup falls back to DOI search without finding arXiv metadata.

Remove the query string or fragment before validating the extracted arXiv identifier.

Also at `modules/weko-workspace/weko_workspace/api.py:438-442`.

Comment on lines +2404 to +2406
if not isinstance(entry, dict) \
or '/api/errors' in str(entry.get('id', '')):
return result

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · API outage appears as no match

When the arXiv service responds with a non-200 status, arXivURL.get_data leaves response empty and error blank for non-200 statuses, so this guard returns []; the view then reports an ordinary no-result response instead of surfacing the service failure.

Log non-200 responses and set an error value so the view can distinguish service failures from genuine no-match results.

Prompt for AI agents
In `modules/weko-workspace/weko_workspace/utils.py` at lines 2404-2406:

**API outage appears as no match**

When the arXiv service responds with a non-200 status, `arXivURL.get_data` leaves `response` empty and `error` blank for non-200 statuses, so this guard returns `[]`; the view then reports an ordinary no-result response instead of surfacing the service failure.

Log non-200 responses and set an error value so the view can distinguish service failures from genuine no-match results.

{"feed": {"entry": {"id": "http://arxiv.org/api/errors#incorrect_id_format_for_abc", "title": "Error"}}},
{"feed": {"entry": []}},
]:
with patch('weko_workspace.utils.arXivURL.get_data', return_value={'response': response, 'error': ''}):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ Low · Added tests fail Black formatting

When the module's black . formatting check runs, black reformats these newly added long patch and fixture lines, so the module's formatting check reports findings instead of passing.

Reformat the added test expressions with Black.

Also at modules/weko-workspace/tests/test_utils.py:3697.

Prompt for AI agents
In `modules/weko-workspace/tests/test_utils.py` at line 3689:

**Added tests fail Black formatting**

When the module's `black .` formatting check runs, black reformats these newly added long patch and fixture lines, so the module's formatting check reports findings instead of passing.

Reformat the added test expressions with Black.

Also at `modules/weko-workspace/tests/test_utils.py:3697`.

From AGENTS.md L70-73:

- フォーマッター: **Black** を使用(`black .` でソースコードを整形)
- リンター: **Flake8** を使用(`flake8` で静的解析チェック)
- インポート順の整理: **isort** を使用(`isort .` でインポート並び替え)
- これらのフォーマットチェックはコミット前に必ず実行し、指摘がない状態にしてください

"""
if not doi:
raise ValueError('DOI is required.')
self._arxiv_id = extract_arxiv_id(doi)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ Low · arXiv IDs remain documented as DOIs

arXivURL now accepts arXiv IDs as well as DOIs, but its constructor documentation still describes only a DOI parameter. Callers relying on that contract can overlook the newly supported input forms.

Update the constructor and get_arXiv_record_data docstrings to state that they accept either a DOI or an arXiv identifier/URL.

Prompt for AI agents
In `modules/weko-workspace/weko_workspace/api.py` at line 483:

**arXiv IDs remain documented as DOIs**

`arXivURL` now accepts arXiv IDs as well as DOIs, but its constructor documentation still describes only a DOI parameter. Callers relying on that contract can overlook the newly supported input forms.

Update the constructor and `get_arXiv_record_data` docstrings to state that they accept either a DOI or an arXiv identifier/URL.

ワークスペースのアイテム登録画面で、DOI に加えて arXiv ID でも
arXiv からメタデータを自動補完できるようにする。

- arXiv ID(新形式 2301.00001 / 旧形式 hep-th/9901001、arXiv: 接頭辞・
  バージョン付き)、arxiv.org の abs/pdf URL、arXiv DOI(10.48550/arXiv.)
  を判定し、arXiv API の id_list で問い合わせる
- 通常の DOI は従来どおり search_query=doi: で問い合わせる
- arXiv の応答に entry が無い(該当なし)、またはエラー entry の場合は
  補完なしとして扱う(従来は KeyError でエラー表示になっていた)
- 入力欄のプレースホルダーとアラート文言を更新

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant