Metadata-Version: 2.5
Name: journapi
Version: 1.0.0
Summary: A standard, extensible SDK for fetching scholarly artifact metadata (journals via ISSN Portal first, more sources to come)
Project-URL: Homepage, https://cnb.cool/xqitw/journapi
Project-URL: Repository, https://cnb.cool/xqitw/journapi
Project-URL: Issues, https://cnb.cool/xqitw/journapi/-/issues
Author: journapi Contributors
License: MIT
License-File: LICENSE
Keywords: cli,issn,journal,metadata,scholarly,sdk
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.11
Requires-Dist: hishel<0.2,>=0.1.5
Requires-Dist: httpx>=0.27
Requires-Dist: parsel>=1.9
Requires-Dist: pydantic>=2.7
Requires-Dist: rich>=13.7
Requires-Dist: stamina>=24.3
Requires-Dist: typer>=0.12
Provides-Extra: chinese
Requires-Dist: jieba>=0.42; extra == 'chinese'
Requires-Dist: pypinyin>=0.51; extra == 'chinese'
Provides-Extra: dev
Requires-Dist: mcp<2,>=1.2.0; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp<2,>=1.2.0; extra == 'mcp'
Description-Content-Type: text/markdown

# journapi

[![Latest Release](https://cnb.cool/xqitw/journapi/-/badge/release)](https://cnb.cool/xqitw/journapi/-/badge/release.link)
[![PyPI](https://img.shields.io/pypi/v/journapi.svg)](https://pypi.org/project/journapi/)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
[![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB.svg)](https://www.python.org)
![badge](https://cnb.cool/xqitw/journapi/-/badge/git/latest/ci/pipeline-as-code?branch=main)
![badge](https://cnb.cool/xqitw/journapi/-/badge/git/latest/ci/git-clone-yyds)
![badge](https://cnb.cool/xqitw/journapi/-/badge/star)
![badge](https://cnb.cool/xqitw/journapi/-/badge/fork)

标准、可扩展的学术制品元数据检索 SDK + CLI —— 首个数据源为期刊（通过公开的 ISSN Portal），提供 Provider 抽象层，为后续更多数据源（文献、DOI 等）预留扩展能力。

> **项目状态**：stable。ISSN Portal 公开网页接口已实现并经独立复审验证；官方订阅 API（api.issn.org, REST + JWT）提供完整 Provider 骨架（需订阅凭据端到端验证）。

## 功能特性

- **期刊检索** — 按刊名、ISSN、eISSN、ISSN-L 搜索，返回 ISSN、ISSN-L、ISSN-H、标题、介质、国家等
- **单条记录** — 按 ISSN 精确查询，含 ISSN-L / ISSN-H、正式题名、出版频率、语言、年份等
- **ISSN-L 集群** — 展开同一刊物全部介质版本（Print/Online），含频率、语言、创刊年份
- **ISSN-H 家族信息** — 展示历史沿革家族标识与成员数（家族明细需订阅）
- **Web UI** — 内置浏览器界面，搜索 / 精确查询双模式，可视化浏览
- **MCP 服务器** — `journapi mcp` 一行接入 Claude Code / Cursor 等智能体（可选依赖）
- **中文支持** — **中国 ISSN 中心源**（issn.nlc.cn）：为国内中文刊查询困难特意新增，**中文刊名检索优先使用**（Portal 索引为拼音转写，中文直查命中率低），原生子串匹配含出版单位/主办单位等本地化元数据；查得的 ISSN 可回 Portal 交叉验证补全国际化元数据；另保留 Portal 拼音转写链路（可选依赖）
- **礼貌抓取** — 限速（robots.txt Crawl-delay 1s）、重试退避、可选磁盘缓存（RFC 9111，认证请求绝不落盘）
- **Provider 抽象** — 注册式多数据源，官方订阅 API 自动降级到公开源
- **CLI + JSON** — 表格或 JSON 输出，方便脚本与 Agent 集成

## 架构概览

```text
           +------------------------------------+
           |          Public API 层             |
           |   ArtifactSearchClient (门面)      |
           +----------------+-------------------+
                            |
          +-----------------+-----------------+
          |                 |                 |
          v                 v                 v
   +------------+   +------------+   +------------+
   |  issn_portal|   |issn_portal_|   |  第三方    |
   |   (公开网页)|   |  api(订阅) |   |  Provider  |
   +------+-----+   +------+-----+   +------+-----+
          |                 |                 |
          +-----------------+-----------------+
                            |
                            v
          +------------------------------------+
          |         Provider 抽象层            |
          |   ArtifactProvider (ABC)          |
          +----------------+-------------------+
                            |
                            v
          +------------------------------------+
          |         基础设施层                 |
          |  HTTP 客户端 | 限速 | 重试 | 缓存  |
          +------------------------------------+
                            |
                            v
          +------------------------------------+
          |         应用层                     |
          |  CLI (search/get/cluster/web/mcp)  |
          |  Web UI (内置浏览器界面)           |
          |  MCP Server (智能体接入, [mcp])    |
          |  Agent Skill (AI 助手集成)         |
          +------------------------------------+
```

## 项目结构

```text
journapi/
├── src/journapi/            SDK 核心
│   ├── api.py               ArtifactSearchClient 门面 + Provider 注册表
│   ├── models.py            统一数据模型（JournalRecord / SearchOptions 等）
│   ├── provider.py          ArtifactProvider 抽象基类
│   ├── http.py              HTTP 客户端（限速 / 重试 / 缓存）
│   ├── validation.py        ISSN 校验唯一来源（格式 + mod-11 校验位）
│   ├── mcp_server.py        MCP 服务器适配层（[mcp] extra，智能体接入）
│   ├── sources/             数据源
│   │   ├── issn_portal/     ISSN Portal 期刊源
│   │   │   ├── provider.py  公开网页源（search / get / cluster）
│   │   │   └── api_client.py 官方订阅 API（REST + JWT）
│   │   └── nlc_issn/        中国 ISSN 中心源（中文刊名友好，issue #34）
│   ├── cli/                 CLI 入口（search / get / cluster / web / mcp）
│   └── web/                 内置 Web UI
│       ├── server.py        服务逻辑（模板加载 + 路由 + JSON API）
│       └── templates/       前端模板（base / search / record / cluster）
├── skills/                  AI Agent 技能
│   └── journapi-skill/     期刊检索技能（SKILL.md）
├── examples/                可运行示例
├── docs/                    详细文档
└── tests/                   单元测试
```

## 安装

包已发布到 PyPI（包名 `journapi`）。三种使用方式：

### 1. uvx 免安装直接运行（推荐）

```bash
uvx journapi --help
uvx journapi search "Hearing research"
```

### 2. pip 安装（长期使用 / 脚本内调用）

```bash
pip install journapi             # 仅 CLI
pip install "journapi[chinese]"  # 含中文刊名分词支持
```

### 3. uv tool 全局安装

```bash
uv tool install journapi
journapi search "Hearing research"
```

### 开发环境

```bash
git clone https://cnb.cool/xqitw/journapi.git
cd journapi
uv sync --extra chinese --extra dev
```

## 快速开始

### Python API

```python
import asyncio
from journapi import ArtifactSearchClient, SearchOptions


async def main():
    async with ArtifactSearchClient() as client:
        # 1) 按刊名 / ISSN / eISSN / ISSN-L 搜索
        results = await client.search("Hearing research")
        for rec in results.items:
            print(rec.issn, rec.issn_l, rec.issn_h, rec.title)

        # 2) 按 ISSN 精确查询（含 ISSN-L / ISSN-H）
        rec = await client.get("0964-1998")
        print(rec.issn_l, rec.issn_h)  # 0964-1998 / 9063-7704

        # 3) 展开 ISSN-L 集群
        members = await client.cluster_issnl("0378-5955")
        for m in members:
            print(m.issn, m.medium, m.title)


asyncio.run(main())
```

完整示例见 [examples/](examples/) 目录。

### CLI

```bash
# 搜索期刊（刊名 / ISSN / eISSN / ISSN-L）
# 中文刊名直查（中国 ISSN 中心源，无需转拼音）
uv run journapi search 自动化学报 --provider nlc_issn --json
# nlc 记录回查 Portal 补全国际化元数据（ISSN-L 集群 / 语种 / 频率）
uv run journapi get 0254-4156 --provider nlc_issn --enrich-portal
journapi search "Hearing research"
journapi search 0378-5955 --json
journapi search "hearing" --media online --country USA --page-size 50

# 单条记录
journapi get 0964-1998

# ISSN-L 集群
journapi cluster 0378-5955

# Web UI（浏览器界面）
journapi web --host 127.0.0.1 --port 8787
# 然后浏览器打开 http://127.0.0.1:8787
```

### Web UI

`journapi web` 启动内置浏览器界面，支持：

- **🔍 搜索** — 按刊名 / 关键词模糊搜索，返回结果列表
- **🎯 精确查询** — 输入 ISSN / eISSN / ISSN-L 直接获取单条记录
- **ISSN-L 集群** — 查看刊物全部介质版本
- **ISSN-H 家族** — 展示历史沿革标识与成员数

### MCP 服务器（AI Agents）

`journapi mcp` 以 Model Context Protocol 把检索能力暴露给智能体
（Claude Code、Cursor、Cline 等），无需经手 shell。需要 `[mcp]` extra：

```bash
uv tool install "journapi[mcp]"   # 或 pip install "journapi[mcp]"
```

**Claude Code 一行接入：**

```bash
claude mcp add journapi -- uvx --from "journapi[mcp]" journapi mcp
```

**Cursor / Cline 配置片段（stdio）：**

```json
{
  "mcpServers": {
    "journapi": {
      "command": "uvx",
      "args": ["--from", "journapi[mcp]", "journapi", "mcp"]
    }
  }
}
```

暴露 3 个工具：

| 工具 | 说明 |
| --- | --- |
| `search_journals` | 按刊名 / ISSN 检索（`source`: 中文刊名必用 `nlc_issn`，默认 Portal；`page_size` 仅接受 20/30/50/100；country / media 过滤仅 Portal 有效） |
| `get_journal` | 按 ISSN 精确取号（mod-11 校验位本地把关；`enrich=true` 时 nlc 记录自动回 Portal 交叉验证补全国际化元数据） |
| `cluster_issnl` | 展开 ISSN-L 集群全部介质版本 |

> 说明：默认 `stdio` transport 即标准智能体接入方式；另提供 `streamable-http`
> （`journapi mcp --transport streamable-http`）仅供本机调试——HTTP transport
> 无内置鉴权能力，请勿直接暴露到公网。

## 模块说明

| 模块 | 说明 |
| --- | --- |
| `src/journapi/api.py` | 门面 + Provider 注册表（`register_provider` / `list_providers`） |
| `src/journapi/models.py` | 统一数据模型：`JournalRecord` / `SearchResult` / `SearchOptions` |
| `src/journapi/http.py` | HTTP 客户端：限速、重试退避、可选磁盘缓存（hishel / RFC 9111） |
| `src/journapi/sources/issn_portal/` | ISSN Portal 期刊数据源（公开网页 + 官方订阅 API） |
| `src/journapi/cli/` | CLI：`search` / `get` / `cluster` / `web` / `mcp` |
| `src/journapi/web/` | 内置 Web UI（模板 + 路由 + JSON API） |
| `src/journapi/mcp_server.py` | MCP 服务器适配层（FastMCP 三工具 + stderr 日志 + 诊断文本契约） |
| `skills/` | AI Agent 技能（`npx skills` 可安装） |

## 环境变量

| 变量 | 使用方 | 用途 | 何时需要 |
| --- | --- | --- | --- |
| `ISSN_PORTAL_USERNAME` | `issn_portal_api` | 官方订阅 API 用户名 | 使用官方订阅 API 时（也可通过构造参数传入） |
| `ISSN_PORTAL_PASSWORD` | `issn_portal_api` | 官方订阅 API 密码 | 使用官方订阅 API 时（也可通过构造参数传入） |

## Skills

通用智能体技能，通过 `npx skills` 安装。

```bash
npx skills add https://cnb.cool/xqitw/journapi.git
```

## 关键设计

- **Provider 抽象层** — 所有数据源实现 `ArtifactProvider` 接口，上层 API / CLI / Web 与具体源解耦
- **统一数据模型** — `JournalRecord` 跨数据源一致，字段可空性按数据层级区分（搜索卡片 / 详情页 / 集群页）
- **礼貌抓取** — 内置限速（robots.txt Crawl-delay 1s）、重试退避、可选磁盘缓存（RFC 9111 + 并发防击穿）
- **自动降级** — 官方订阅 API 不可用时自动回退到公开网页源
- **enrich 合并** — `get()` 自动从 ISSN-L 集群页合并频率/语言/年份，从搜索卡片反查 ISSN-H 家族成员数
- **前端模板化** — Web UI 前端为独立模板文件，Python 仅做 `{{TOKEN}}` 替换，无内嵌 HTML

## 合规说明

公开门户面向人工浏览。journapi 遵循其 `robots.txt`：`/resource/ISSN/` 允许抓取，`/resource/ISSN-L/` 与 `/resource/ISSN-H/` 禁止抓取，并强制 1s 爬取延迟。生产 / 批量场景请订阅[官方搜索 API](https://portal.issn.org/services) 并使用订阅 Provider。

## 文档

- [API 参考](docs/API.md) — 客户端、模型、错误、Web 服务路由
- [数据源调研](docs/sources.md) — ISSN Portal 公开接口能力边界
- [示例](examples/) — 可运行脚本

## 贡献指南

欢迎参与贡献！详细的贡献规范和开发流程请参考 [CONTRIBUTING.md](./CONTRIBUTING.md)。

## 许可证

[MIT](./LICENSE)
