はじめに
前回の記事では、Notionの Publications データベースを研究業績の正本として管理し、MarkdownやHTMLに出力する運用を紹介しました。
前回記事
関連GitHubリポジトリ
この記事では、その続きとして、同じNotion DBからresearchmap V2 CSVを生成し、researchmap上の既存業績を更新する運用を紹介します。
前回の基本方針は次の通りです。
Notion Publications DB
↓
fetch
↓
cache/publications.json
↓
Markdown / HTML / TXT
今回はここに、researchmap CSV出力を追加します。
Notion Publications DB
↓
fetch
↓
cache/publications.json
↓
researchmap V2 CSV
↓
researchmapへアップロード
さらに、researchmapからエクスポートしたCSVを使って、researchmap上の業績IDをNotionへ書き戻すことで、次回以降は既存業績を安全に更新できるようにします。
この記事で扱うこと
この記事で扱うのは、researchmap APIではなく、researchmapのCSVインポート・エクスポートを使う運用です。
扱うこと:
- Notion DBからresearchmap V2 CSVを生成する
- researchmapへCSVをアップロードする
- researchmapからCSVをエクスポートする
- エクスポートCSVとNotionデータを照合する
-
Researchmap IDをNotionへ書き戻す - 次回以降は
update / doc / ID指定でCSVを生成する
扱わないこと:
- researchmap APIで直接更新する
- researchmap側の編集内容をNotionへ自動で取り込む
- 完全な双方向同期を作る
- すべてのresearchmap業績種別に完全対応する
この運用では、あくまで Notion DBを正本 とします。researchmapからエクスポートしたCSVは、既存業績IDを確認するための参照データとして使います。
ResearchmapのCSV仕様については、こちらの情報を参照しています。
全体像
初回は、Notion側にresearchmapの業績IDがありません。そのため、まずは insert / merge としてCSVを生成します。
初回:
Notion
↓
researchmap CSV
↓
researchmapにアップロード
↓
researchmapからCSVをエクスポート
↓
Notion項目と照合
↓
Researchmap IDをNotionへ書き戻す
一度 Researchmap ID がNotionに入れば、次回以降は既存業績IDを指定して更新できます。
次回以降:
Notion with Researchmap ID
↓
researchmap CSV
↓
update / doc / ID指定
↓
researchmap上の既存業績を更新
この切り替えが重要です。
Researchmap ID なし
→ insert / merge / IDなし
Researchmap ID あり
→ update / doc / ID指定
IDなしで毎回 insert すると、重複登録や類似判定エラーの原因になります。継続運用では、researchmap側の業績IDをNotionに保持し、それを使って更新する方が安全です。
前提
前回の記事で、以下ができている前提です。
- Notionに
PublicationsDBがある - Notionに
AuthorsDBがある -
PublicationsDBからAuthorsDBへrelationしている - Notion integrationを作成済み
-
NOTION_KEY,database_id,authors_db_idが環境変数に入っている -
uv run notion-export fetchでNotion DBを取得できる
また、この記事ではPython環境管理に uv を使います。
researchmap CSVの基本
researchmap V2 CSVには、通常のCSVとは少し違う約束があります。
代表的には次のような点です。
- 1行目に業績種別を書く
- 2行目にヘッダを書く
- 3行目以降にデータを書く
- 未設定項目は空欄ではなく
nullと書く -
アクション名とアクションタイプが重要 - 既存業績を更新する場合は
IDを指定する
例えば、講演・口頭発表等の場合、1行目は次のようになります。
presentations
2行目にヘッダが入り、3行目以降にデータが入ります。
presentations
アクション名,アクションタイプ,類似業績マージ優先度,ID,タイトル(日本語),タイトル(英語),...
insert,merge,null,null,...,...
この記事では、主に presentations、つまり講演・口頭発表等のCSVを例に説明します。
Notion側に追加するプロパティ
前回のNotion DBに、researchmap連携用のプロパティを追加します。
最低限追加したいものは次の通りです。
| プロパティ | 型 | 用途 |
|---|---|---|
Researchmap ID |
rich text | researchmap側の業績ID |
Sync to Researchmap |
checkbox | researchmapへ出力するか |
Researchmap Type |
select | researchmap上の種別を上書きしたい場合 |
Language Code |
select |
jpn / eng
|
Country Code |
select / rich text |
JPN, USA, CAN など |
Invited |
checkbox | 招待講演か |
Visibility |
select |
disclosed, researchers_only, closed
|
Major |
checkbox | 主要業績か |
Researchmap Sync Status |
select | 照合状態の管理用 |
実際には、すべてを毎回手入力する必要はありません。
空欄でもよいもの
以下は、空欄ならスクリプト側で推定・補完する運用にできます。
| プロパティ | 補完方針 |
|---|---|
Researchmap Type |
Type から推定 |
Language Code |
Type から推定 |
Visibility |
disclosed |
Major |
FALSE |
Researchmap Sync Status |
任意の管理用ステータス |
例えば、Researchmap Type は次のように推定できます。
Original Paper -> published_papers
Review paper -> published_papers
International conference -> presentations
国内学会 -> presentations
Preprint -> misc
Language Code も、おおむね次のように推定できます。
国内学会 -> jpn
International conference -> eng
Original Paper -> eng
Review paper -> eng
Preprint -> eng
手入力した方がよいもの
一方で、次のものは手入力した方が安全です。
| プロパティ | 理由 |
|---|---|
Sync to Researchmap |
researchmapに出すかどうかは人間判断 |
Country Code |
国際会議の会場国は自動推定しにくい |
Invited |
招待講演かどうかは自動では分からない |
国内学会であれば Country Code = JPN と自動補完できますが、国際会議は USA, CAN, SGP などを明示的に入れておく方が安全です。
国コードの例
researchmapの 国・地域 には、3文字の国コードを入れます。
よく使いそうな例です。
| 国 | Country Code |
|---|---|
| 日本 | JPN |
| アメリカ | USA |
| カナダ | CAN |
| シンガポール | SGP |
| オーストラリア | AUS |
| ドイツ | DEU |
| アイルランド | IRL |
| イタリア | ITA |
Notionの Country Code には、この3文字コードを入れておきます。
config.yamlの設定
Notion側のプロパティ名と、スクリプト内部で使う名前を config.yaml で対応づけます。
例です。
properties:
name: Name
title: Name
authors_relation: Authors list
type: Type
journal: Journal
venue: Journal
volume: Volume
issue: Issues
publication_date: Publication date
pages: Page
doi: DOI
place: Place
duration: Duration
presentation: Presentation
tags: Tags
researchmap_id: Researchmap ID
researchmap_type: Researchmap Type
sync_to_researchmap: Sync to Researchmap
researchmap_sync_status: Researchmap Sync Status
language_code: Language Code
country_code: Country Code
invited: Invited
visibility: Visibility
major: Major
arxiv_id: arXiv ID
arxiv_url: arXiv URL
journal_status: Journal Status
related_journal_paper: Related Journal Paper
補完ルールも設定しておくと便利です。
researchmap:
default_visibility: disclosed
default_major: false
default_language_code:
国内学会: jpn
International conference: eng
Original Paper: eng
Review paper: eng
Preprint: eng
default_country_code:
国内学会: JPN
type_mapping:
Original Paper: published_papers
Review paper: published_papers
International conference: presentations
国内学会: presentations
Preprint: misc
presentation_type_mapping:
Oral: oral_presentation
Poster: poster_presentation
preprint_misc_type: technical_report
Notionから最新データを取得する
Notionを更新したら、まずキャッシュを更新します。
uv run notion-export fetch \
--config config.yaml \
--cache cache/publications.json
通常運用では、cache/publications.json を毎回上書きして構いません。別名保存は、デバッグや比較をしたいときだけで十分です。
researchmap用CSVを生成する
講演・口頭発表等、つまり presentations 用のCSVを生成します。
uv run notion-export researchmap export-csv \
--config config.yaml \
--cache cache/publications.json \
--target presentations \
--output outputs/researchmap_presentations.csv
先頭を確認します。
sed -n '1,10p' outputs/researchmap_presentations.csv
1行目が presentations、2行目がヘッダ、3行目以降がデータになっていればOKです。
CSVの中身を確認する
researchmapへアップロードする前に、actionやIDの状態を確認します。
uv run python - <<'PY'
import csv
from pathlib import Path
from collections import Counter
path = Path("outputs/researchmap_presentations.csv")
with path.open(encoding="utf-8-sig", newline="") as f:
rows = list(csv.reader(f))
header = rows[1]
records = rows[2:]
idx = {h: i for i, h in enumerate(header)}
print("records:", len(records))
print("actions:", Counter(r[idx["アクション名"]] for r in records))
print("action types:", Counter(r[idx["アクションタイプ"]] for r in records))
print("ids set:", sum(1 for r in records if r[idx["ID"]] not in {"", "null"}))
print("ids null:", sum(1 for r in records if r[idx["ID"]] in {"", "null"}))
print("countries:", Counter(r[idx["国・地域"]] for r in records))
PY
初回で Researchmap ID がまだない場合は、次のようになります。
actions: Counter({'insert': 58})
action types: Counter({'merge': 58})
ids null: 58
Researchmap ID が書き戻された後は、次のようになります。
actions: Counter({'update': 58})
action types: Counter({'doc': 58})
ids set: 58
この状態なら、既存のresearchmap業績をID指定で更新できます。
初回登録
初回は、Notion側に Researchmap ID がありません。そのため、CSVは基本的に次のようになります。
insert,merge,null,null,...
このCSVをresearchmapにアップロードします。
アップロード後、researchmap側で業績IDが付与されます。このIDをNotionに書き戻すことで、次回以降は既存業績を更新できるようになります。
researchmapからCSVをエクスポートする
researchmapへのアップロード後、researchmapから対象業績のCSVをエクスポートします。
この記事では、例として presentations のエクスポートCSVを使います。
ファイル名は例えば次のようにします。
rm_presentations.csv
このCSVには、researchmap側の ID が含まれています。これをNotion側の業績と照合して、Researchmap ID として書き戻します。
researchmapエクスポートCSVを確認する
まず、researchmapからエクスポートしたCSVの概要を確認します。
uv run notion-export researchmap inspect-csv \
--csv rm_presentations.csv
出力例です。
kind: presentations
record count: 58
with ID: 58
without ID: 0
action names: insert=58
action types: merge=58
duplicate title candidates: 0
researchmapからのエクスポートCSVでは、アクション名 が insert になっていることがあります。ただし、重要なのは ID が入っていることです。
Notionとresearchmap CSVを照合する
次に、Notionの業績とresearchmap CSVの業績を照合します。
現状の match-csv は presentations の照合を対象にしています。
uv run notion-export researchmap match-csv \
--config config.yaml \
--cache cache/publications.json \
--csv rm_presentations.csv \
--output outputs/researchmap_match_report.csv
照合レポートを確認します。
sed -n '1,40p' outputs/researchmap_match_report.csv
件数を集計するには、次のようにします。
uv run python - <<'PY'
import csv
from pathlib import Path
from collections import Counter
path = Path("outputs/researchmap_match_report.csv")
with path.open(encoding="utf-8-sig", newline="") as f:
rows = list(csv.DictReader(f))
print("rows:", len(rows))
print(Counter(r.get("status") for r in rows))
for r in rows[:20]:
print(r.get("status"), r.get("notion_title"), "=>", r.get("researchmap_id"), r.get("reason"))
PY
すべて対応できていれば、例えば次のようになります。
Counter({'matched': 58})
ambiguous や not_found がある場合は、手動確認します。
Researchmap IDをNotionへ書き戻す
現状の match-csv / sync-ids は presentations の照合を対象にしています。
まずはdry-runで確認します。
uv run notion-export researchmap sync-ids \
--config config.yaml \
--cache cache/publications.json \
--csv rm_presentations.csv \
--dry-run
この段階ではNotionには書き込みません。
出力例です。
Write candidates: 58
Skipped: 0
問題なければ、--apply を付けて実際に書き戻します。
uv run notion-export researchmap sync-ids \
--config config.yaml \
--cache cache/publications.json \
--csv rm_presentations.csv \
--apply
成功すると、Notion側の Researchmap ID にIDが入ります。
その後、再fetchして確認します。
uv run notion-export fetch \
--config config.yaml \
--cache cache/publications.json
uv run python - <<'PY'
import json
from pathlib import Path
data = json.loads(Path("cache/publications.json").read_text(encoding="utf-8"))
with_id = [x for x in data if x.get("researchmap_id")]
print("items with Researchmap ID:", len(with_id))
for item in with_id[:10]:
print(item.get("title"), "=>", item.get("researchmap_id"))
PY
次回以降の更新
Researchmap ID がNotionに入った後にCSVを生成すると、該当行は update / doc / ID指定 になります。
uv run notion-export researchmap export-csv \
--config config.yaml \
--cache cache/publications.json \
--target presentations \
--output outputs/researchmap_presentations.csv
確認します。
uv run python - <<'PY'
import csv
from pathlib import Path
from collections import Counter
path = Path("outputs/researchmap_presentations.csv")
with path.open(encoding="utf-8-sig", newline="") as f:
rows = list(csv.reader(f))
header = rows[1]
records = rows[2:]
idx = {h: i for i, h in enumerate(header)}
print("records:", len(records))
print("actions:", Counter(r[idx["アクション名"]] for r in records))
print("action types:", Counter(r[idx["アクションタイプ"]] for r in records))
print("ids set:", sum(1 for r in records if r[idx["ID"]] not in {"", "null"}))
print("ids null:", sum(1 for r in records if r[idx["ID"]] in {"", "null"}))
PY
期待する状態です。
actions: Counter({'update': 58})
action types: Counter({'doc': 58})
ids set: 58
ids null: 0
これで、次回以降はresearchmap上の既存業績を更新できます。
論文CSVへの拡張
この記事では主に presentations を例にしましたが、同じ考え方で published_papers も出力できます。
uv run notion-export researchmap export-csv \
--config config.yaml \
--cache cache/publications.json \
--target published_papers \
--output outputs/researchmap_papers.csv
論文の場合は、次のNotionプロパティが使われます。
| Notion | researchmap |
|---|---|
Name |
タイトル |
Authors list |
著者 |
Journal |
誌名 |
Publication date |
出版年月 |
Volume |
巻 |
Issues |
号 |
Page |
ページ |
DOI |
DOI |
Original Paper や Review paper は、基本的に published_papers として出力します。
Preprintをどう扱うか
プレプリントの記事ももちろん管理可能です。
arXivなどのpreprintもNotion DBに入れる場合、researchmap上では misc として出力します。
Notion側では、例えば次のプロパティを使います。
| プロパティ | 用途 |
|---|---|
Type |
Preprint |
Journal |
arXiv |
DOI |
DOIがあれば |
arXiv ID |
arXiv ID |
arXiv URL |
arXiv URL |
Journal Status |
preprint, under_review, published など |
Related Journal Paper |
査読済み版との対応 |
出力時には、Preprint を misc に対応させ、misc_type = technical_report として扱います。
Preprint -> misc
misc_type -> technical_report
査読済み論文になった場合は、PreprintとOriginal Paperを別レコードとして残し、Related Journal Paper で紐づけると管理しやすいです。
ハマりどころ
actionが空で怒られる
researchmap CSVでは、業績CSVに アクション名 が必要です。
CSVの先頭列が空になっていると、インポート時にエラーになります。
対策として、Researchmap ID の有無に応じて、次のように自動出力します。
Researchmap IDなし -> insert / merge
Researchmap IDあり -> update / doc
未設定は空欄ではなくnull
researchmap CSVでは、未設定項目に空欄ではなく null を入れる必要があります。
通常のCSV感覚で空欄にすると、意図しない挙動やエラーの原因になります。
IDなしで毎回insertすると危険
Researchmap ID なしで毎回 insert / merge すると、既存業績との類似判定に依存します。重複やエラーの原因になりやすいので、初回登録後はresearchmap export CSVからIDを回収し、Notionに書き戻すのが安全です。
国コードがnullになる
国内学会は JPN と自動補完できますが、国際会議の国コードは手入力した方が安全です。
Country Code に次のような3文字コードを入れます。
USA
CAN
SGP
AUS
DEU
IRL
ITA
Notion側の値はすべて埋めなくてよい
Researchmap Type, Language Code, Visibility, Major は、空欄でも多くの場合は自動補完できます。
一方で、Sync to Researchmap, Country Code, Invited は人間が判断して入れる方が安全です。
通常の運用まとめ
初回
1. Notionに業績を入力
2. Sync to Researchmap をチェック
3. Country Codeなど必要項目を入力
4. fetch
5. researchmap CSVを生成
6. researchmapにアップロード
7. researchmapからCSVをエクスポート
8. match-csvで照合
9. sync-idsでResearchmap IDを書き戻す
次回以降
1. Notionを更新
2. fetch
3. researchmap CSVを生成
4. update/doc/ID指定のCSVをresearchmapにアップロード
この運用にすると、researchmap側で直接業績を編集する必要がかなり減ります。
まとめ
この記事では、Notion DBを正本としてresearchmap V2 CSVを生成・更新する運用を紹介しました。
ポイントは次の通りです。
- Notionを正本にする
- researchmap CSVはNotionから生成する
- 初回は
insert / merge - researchmap export CSVから
Researchmap IDを回収する - 次回以降は
update / doc / ID指定 -
Country Codeなど、一部の値はNotion側で明示しておく - researchmap側の編集内容は原則としてNotionへ自動取り込みしない
研究業績はWebサイト、CV、researchmapなど複数の場所で使われます。Notionを正本にしておくと、それぞれを手作業で更新する必要が減り、表記ゆれや更新漏れも抑えられます。