Skill: chenruao/feishu-literature-manager (version 1.1.1)

--- name: feishu-literature-manager description: Automated literature retrieval and Feishu Bitable management. Use when user requests to create a literature database, search PubMed for specific topics, or manage research papers in Feishu tables. Triggers on phrases like "create a literature table", "search papers and add to Feishu", "build a research database", "补充文献", "添加文献到表格", or "检索文献并建立表格". Supports complete workflow from topic definition to populated Feishu table with all required fields including Chinese translations, impact factors, and reference formatting. Also supports supplementing existing databases with new papers. --- # Feishu Literature Manager ## Overview This skill automates the complete workflow of creating a literature database in Feishu Bitable, from PubMed search to fully populated table with all required metadata including Chinese translations, impact factors, and formatted references. **Two Main Workflows:** 1. **Create New Database**: Create a new Feishu Bitable from scratch 2. **Supplement Existing Database**: Add new papers to an existing table (avoiding duplicates) ## Workflow Decision Tree ### Workflow A: Create New Database ``` User provides: topic + number of papers ↓ 1. Create Feishu Bitable ↓ 2. Create all 17 required fields ↓ 3. Search PubMed for papers ↓ 4. Parse and validate results ↓ 5. Extract complete metadata ↓ 6. Translate titles and abstracts ↓ 7. Add papers to table (PARALLEL CALLS) ↓ 8. Set table permissions (full_access) ↓ 9. Report completion ``` ### Workflow B: Supplement Existing Database ⭐ NEW ``` User provides: research topic + specific focus + table URL + number of papers ↓ 1. Parse table URL to get app_token and table_id ↓ 2. Get existing records to extract current PMIDs ↓ 3. Search PubMed with focused keywords ↓ 4. Filter out existing PMIDs (deduplication) ↓ 5. Score and rank papers by relevance ↓ 6. Select top N papers ↓ 7. Fetch XML data for selected papers ↓ 8. Parse metadata and translate ↓ 9. Add papers to table (PARALLEL CALLS - 5-10 at a time) ↓ 10. Report completion with summary ``` ## Batch Processing for Large Tasks **IMPORTANT: When retrieving more than 5 papers, process in batches!** **Why Batch Processing is Necessary:** 1. **Token limitations** - Large numbers of papers require extensive translation work 2. **Quality assurance** - Each batch ensures complete field information before proceeding 3. **Better user experience** - Users receive regular progress updates 4. **Error recovery** - Easier to resume if interrupted **Batch Processing Workflow:** ``` User requests: topic + N papers (N > 5) ↓ Calculate batches: ceil(N / 5) ↓ For each batch (5 papers): 1. Fetch PMIDs for this batch 2. Retrieve XML data 3. Parse metadata 4. Translate titles and abstracts 5. Add all 5 papers to table with COMPLETE fields 6. Report batch completion ↓ Continue to next batch ↓ All batches complete → Report final status ``` **Example: User requests 10 papers** ``` Batch 1 (Papers 1-5): - Fetch PMIDs 1-5 - Get XML data - Parse and translate - Add 5 papers with complete fields (including 摘要) - Report: "第一批完成，已添加5篇文献" Batch 2 (Papers 6-10): - Fetch PMIDs 6-10 - Get XML data - Parse and translate - Add 5 papers with complete fields (including 摘要) - Report: "第二批完成，已添加5篇文献" Final Report: "任务全部完成！共添加10篇文献" ``` **Critical Rules:** 1. **Never start a new batch until current batch is COMPLETE** - COMPLETE means ALL 17 fields filled, including 摘要（英文）and 摘要（中文） - Report completion before starting next batch 2. **Progress Reporting:** - After each batch: "第X批完成，已添加Y篇文献，剩余Z篇" - Keep user informed of progress 3. **Token Management:** - Monitor token usage - If running low, inform user and continue in next conversation - Save state (PMIDs retrieved, current batch) for resumption 4. **Quality over Speed:** - Better to complete fewer papers with full information - Than many papers with incomplete fields ## ⭐ PARALLEL API CALLS - BEST PRACTICE **CRITICAL: Always use parallel API calls when adding multiple papers!** ### Why Parallel Calls? Sequential API calls are SLOW. Each call waits for response before next call. Parallel calls submit multiple requests simultaneously, dramatically improving efficiency. ### How to Make Parallel Calls In tool calls, submit MULTIPLE `feishu_bitable_create_record` calls in the SAME function_calls block: ``` // CORRECT: Parallel calls (5-10 papers at once) <function_calls> <invoke name="feishu_bitable_create_record"> <parameter name="app_token">xxx</parameter> <parameter name="table_id">xxx</parameter> <parameter name="fields">{paper 1 data}</parameter> </invoke> <invoke name="feishu_bitable_create_record"> <parameter name="app_token">xxx</parameter> <parameter name="table_id">xxx</parameter> <parameter name="fields">{paper 2 data}</parameter> </invoke> <invoke name="feishu_bitable_create_record"> <parameter name="app_token">xxx</parameter> <parameter name="table_id">xxx</parameter> <parameter name="fields">{paper 3 data}</parameter> </invoke> ... (up to 10 calls at once) </function_calls> // WRONG: Sequential calls (SLOW!) <function_calls> <invoke name="feishu_bitable_create_record">...</invoke> </function_calls> // Wait for response... <function_calls> <invoke name="feishu_bitable_create_record">...</invoke> </function_calls> // Wait for response... ``` ### Recommended Batch Size - **5-10 papers per parallel call** - Optimal balance of speed and reliability - **Maximum 10 papers** - Avoid overwhelming the API ### Example: Adding 10 Papers Efficiently ```python # Step 1: Prepare all paper data papers_data = [prepare_paper_data(p) for p in papers[:10]] # Step 2: Make parallel calls (submit all at once) # All 10 feishu_bitable_create_record calls in same function_calls block results = parallel_add_papers(papers_data) # Step 3: Report results print(f"Successfully added {len(results)} papers in one batch!") ``` ## ⭐ SUPPLEMENTING EXISTING DATABASE - Step by Step ### Overview When user provides: - **Research topic**: e.g., "胃神经内分泌肿瘤" - **Specific focus**: e.g., "药物临床研究", "手术治疗", "诊断方法" - **Table URL**: Feishu Bitable link - **Number of papers**: How many to add ### Step 1: Parse Table URL and Get Existing PMIDs ```python # Extract app_token from URL # URL format: https://xxx.feishu.cn/base/APP_TOKEN?table=TABLE_ID # Or: https://xxx.feishu.cn/wiki/xxx?table=TABLE_ID # Get existing records records = feishu_bitable_list_records( app_token=app_token, table_id=table_id, page_size=500 # Get all records ) # Extract existing PMIDs existing_pmids = set() for record in records['records']: pmid = record['fields'].get('PMID') if pmid: existing_pmids.add(pmid) print(f"Existing records: {len(records['records'])}, Unique PMIDs: {len(existing_pmids)}") ``` ### Step 2: Build Focused Search Query ```python # Combine research topic with specific focus # Example: "胃神经内分泌肿瘤" + "药物临床研究" # Search terms for different focuses: FOCUS_KEYWORDS = { "药物临床研究": [ "chemotherapy", "targeted therapy", "PRRT", "somatostatin analog", "everolimus", "sunitinib", "octreotide", "lanreotide", "immunotherapy", "PD-1", "PD-L1", "temozolomide", "capecitabine", "177Lu", "Lutetium", "clinical trial", "phase", "randomized", "treatment" ], "手术治疗": [ "surgery", "resection", "gastrectomy", "endoscopic resection", "lymph node dissection", "surgical outcome" ], "诊断方法": [ "diagnosis", "biomarker", "PET/CT", "endoscopy", "pathology", "immunohistochemistry", "molecular marker" ], "预后评估": [ "prognosis", "survival", "outcome", "risk factor", "nomogram" ] } # Build search query search_query = f"({topic}) AND ({' OR '.join(focus_keywords)})" ``` ### Step 3: Search PubMed and Filter ```python # Search PubMed response = curl(f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term={search_query}&retmax=50&retmode=json&sort=pub_date") new_pmids = [pmid for pmid in response['idlist'] if pmid not in existing_pmids] print(f"Found {len(response['idlist'])} papers, {len(new_pmids)} are new") ``` ### Step 4: Score and Rank Papers by Relevance ```python # Fetch detailed info for new PMIDs xml_data = curl(f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id={','.join(new_pmids)}&retmode=xml") # Score each paper def score_paper(paper, focus): score = 0 title = paper['title'].lower() abstract = paper['abstract'].lower()[:500] # High relevance: keyword in title for kw in FOCUS_KEYWORDS.get(focus, []): if kw.lower() in title: score += 3 if kw.lower() in abstract: score += 1 # Neuroendocrine in title if 'neuroendocrine' in title or 'net' in title: score += 2 # Clinical trial/phase in title if 'trial' in title or 'phase' in title: score += 2 return score # Sort by score scored_papers = [(score_paper(p, focus), p) for p in papers] scored_papers.sort(reverse=True, key=lambda x: x[0]) # Select top N selected_papers = [p for score, p in scored_papers[:number_requested]] ``` ### Step 5: Add Papers with Parallel Calls ```python # Prepare all paper data papers_data = [prepare_full_paper_data(p) for p in selected_papers] # Make PARALLEL API calls (5-10 at a time) # Submit all feishu_bitable_create_record calls in SAME function_calls block results = parallel_add_papers(papers_data) # Report results print(f"✅ Added {len(results)} papers successfully!") ``` ## Core Capabilities ### 1. Table Field Structure (17 Required Fields) **Primary Field:** - 文献标题（中文） - Text (主字段, use Chinese title as primary field) **Basic Information:** - 文献题目（英文） - Text - 文献题目（中文） - Text - 发表年月 - DateTime (timestamp in milliseconds) - 第一作者 - Text - 第一作者单位 - Text - 通讯作者 - Text - 期刊名称 - Text **Abstracts:** - 摘要（英文） - Text (structured with BACKGROUND, METHODS, RESULTS, CONCLUSION) - 摘要（中文） - Text (structured with 【背景】【目的】【方法】【结果】【结论】) **Identifiers:** - PMID - Text - DOI - Text **Metadata:** - 免费全文链接 - URL (format: `{"link": "URL", "text": "PMC免费全文"}` or `{"link": "DOI_URL", "text": "DOI链接"}`) - SCI分区 - Text (e.g., "JCR Q1", "JCR Q2", "中文核心期刊") - 中科院分区 - Text (e.g., "医学1区", "医学2区", "中文核心期刊") - 影响因子 - Number (MUST be numeric, not string!) - 国标参考文献格式 - Text (GB/T 7714-2015 format) ### 2. Step-by-Step Workflow **Step 1: Create Feishu Bitable** ```python # Use feishu_bitable_create_app app = feishu_bitable_create_app( name="文献库标题", folder_token="optional_folder_token" ) # Save app_token and default table_id from response ``` **Step 2: Create Fields** ```python # Use feishu_bitable_create_field for each of the 17 fields # Field types: Text=1, Number=2, DateTime=5, URL=15 # Example: feishu_bitable_create_field( app_token=app_token, table_id=table_id, field_name="文献题目（英文）", field_type=1 # Text ) ``` **Step 3: Search PubMed** ```bash # Search PubMed using E-utilities API curl -s "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=SEARCH_TERM&retmax=NUMBER&retmode=json&sort=pub_date" # Extract PMIDs from response # Fetch detailed information for each PMID curl -s "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=PMID1,PMID2,...&retmode=xml" ``` **Step 4: Parse PubMed XML** ```python # Extract from XML: # - Title (ArticleTitle) # - Authors (LastName + ForeName initial) # - First author affiliation # - Corresponding author (last author or from affiliation) # - Journal name # - Publication date (Year, Month) # - DOI # - PMC ID (if available) # - Volume, Issue, Pages # - Abstract (all AbstractText elements with labels) ``` **Step 5: Translate to Chinese** ```python # Translate title to Chinese # Translate abstract to Chinese with structured format: # 【背景】... 【目的】... 【方法】... 【结果】... 【结论】... ``` **Step 6: Get Journal Metadata** ```python # Look up impact factor for journal # Determine SCI partition (JCR Q1/Q2/Q3/Q4) # Determine CAS partition (医学1区/2区/3区/4区) # Use references/impact_factors.md for common journals ``` **Step 7: Format Reference** ```python # GB/T 7714-2015 format: # Author1, Author2, Author3, et al. Title[J]. Journal Abbrev, Year, Volume(Issue): Pages. DOI: xxx. # Example: # Fazio N, La Salvia A. Immune Checkpoint Inhibitors in High-grade Gastroenteropancreatic Neuroendocrine Neoplasms[J]. Endocr Rev, 2026, 47(2): 178-190. DOI: 10.1210/endrev/bnaf037. ``` **Step 8: Add to Feishu Table** ```python # Use feishu_bitable_create_record # IMPORTANT: 影响因子 must be numeric, not string! # IMPORTANT: 免费全文链接 must be JSON format: {"link": "URL", "text": "text"} record = feishu_bitable_create_record( app_token=app_token, table_id=table_id, fields={ "PMID": pmid, "文献题目（英文）": english_title, "文献题目（中文）": chinese_title, "发表年月": timestamp_ms, # DateTime as milliseconds "第一作者": first_author, "第一作者单位": first_affiliation, "通讯作者": corresponding_author, "期刊名称": journal, "摘要（英文）": english_abstract, "摘要（中文）": chinese_abstract, "DOI": doi, "免费全文链接": {"link": url, "text": text}, "SCI分区": sci_partition, "中科院分区": cas_partition, "影响因子": impact_factor, # Number, not string! "国标参考文献格式": reference, "文献标题（中文）": chinese_title # Primary field (中文标题作为主字段) } ) ``` **Step 9: Set Table Permissions** ```python # Use feishu_perm to set full_access permission for the user # This allows the user to fully manage the table (view, edit, manage permissions, delete) feishu_perm( action="add", [REDACTED_SECRET], # Use the Bitable's app_token type="bitable", member_type="openid", # User's open_id member_id=user_open_id, # User's open_id (e.g., "ou_xxx") perm="full_access" # Full management permission ) # Permission levels: # - "view": View only # - "edit": Can edit # - "full_access": Full access (can manage permissions) # Note: feishu_perm tool must be enabled in configuration: # channels.feishu.tools.perm = true ``` ### 3. User Reporting Requirements **Report after each step:** 1. ✅ **Table Created**: "已创建飞书多维表格，表格名称：[名称]，表格ID：[ID]" 2. ✅ **Fields Created**: "已创建所有必填字段" 3. ✅ **PubMed Search**: "已从PubMed检索到 [N] 篇文献" 4. ✅ **Deduplication**: "去重后剩余 [N] 篇新文献" 5. ✅ **Metadata Extraction**: "正在提取第 [X]/[N] 篇文献的详细信息..." 6. ✅ **Translation**: "正在翻译中文标题和摘要..." 7. ✅ **Adding to Table**: "正在添加第 [X]/[N] 篇文献到表格..." 8. ✅ **Permissions Set**: "已为您设置表格管理权限" 9. ✅ **Completion**: "✅ 任务完成！共添加 [N] 篇文献到表格中，表格现有 [M] 条记录，您拥有完全管理权限" ### 4. Common Pitfalls to Avoid **❌ WRONG:** - String impact factor: `"影响因子": "15.3"` - Plain URL: `"免费全文链接": "https://..."` - Missing Chinese translation - Incomplete abstract (missing sections) **✅ CORRECT:** - Numeric impact factor: `"影响因子": 15.3` - JSON URL: `"免费全文链接": {"link": "https://...", "text": "PMC免费全文"}` - Complete translations - Structured abstracts with all sections ### 5. Quality Checklist Before adding each paper, verify: - [ ] All 17 fields are present - [ ] 影响因子 is numeric - [ ] 免费全文链接 is JSON format - [ ] 发表年月 is timestamp in milliseconds - [ ] 摘要（中文） has structured format - [ ] 国标参考文献格式 follows GB/T 7714-2015 - [ ] No duplicate PMIDs in table ### 6. Permission Management **IMPORTANT**: Always set table permissions for the user after creating the table. **Configuration Required:** The `feishu_perm` tool must be enabled in the OpenClaw configuration: ```bash openclaw config set channels.feishu.tools.perm true openclaw gateway restart # Restart gateway to apply changes ``` **Why It's Important:** - Without proper permissions, users cannot fully manage tables they create - `full_access` permission allows users to: - View and edit table content - Manage permissions (add/remove collaborators) - Delete the table - Export and share the table **How to Get User's open_id:** The user's open_id is available in the inbound context metadata: ```json { "sender_id": "ou_xxxxxxxxxxxx", "sender": "User Name" } ``` **Permission Levels:** - `view`: Read-only access - `edit`: Can view and edit content - `full_access`: Full management access (recommended for table owners) ## Resources ### scripts/ - `pubmed_search.py` - PubMed E-utilities API wrapper - `parse_pubmed_xml.py` - XML parsing utilities ### references/ - `field_mapping.md` - Complete field definitions and types - `impact_factors.md` - Common journal impact factors and partitions - `reference_format.md` - GB/T 7714-2015 formatting rules

path: references/field_mapping.md
content: # Field Mapping Reference

## Feishu Bitable Fields (17 Required Fields)

### Field Types
- `Text` = type 1
- `Number` = type 2
- `DateTime` = type 5
- `URL` = type 15

## Complete Field List

| # | Field Name (Chinese) | Field Name (English) | Type | Required | Notes |
|---|---------------------|---------------------|------|----------|-------|
| 1 | 胃神经内分泌肿瘤文献库（陈博士版） | Literature Database Name | Text | Yes | **Primary field** - Use Chinese title |
| 2 | 文献题目（英文） | English Title | Text | Yes | From PubMed ArticleTitle |
| 3 | 文献题目（中文） | Chinese Title | Text | Yes | **Must translate** from English |
| 4 | 发表年月 | Publication Date | DateTime | Yes | **Timestamp in milliseconds** |
| 5 | 第一作者 | First Author | Text | Yes | Format: "LastName F" |
| 6 | 第一作者单位 | First Author Affiliation | Text | Yes | From first AffiliationInfo/Affiliation |
| 7 | 通讯作者 | Corresponding Author | Text | Yes | Usually last author or from affiliation |
| 8 | 期刊名称 | Journal Name | Text | Yes | Full journal title |
| 9 | 摘要（英文） | English Abstract | Text | Yes | Structured with all sections |
| 10 | 摘要（中文） | Chinese Abstract | Text | Yes | **Must translate** with structured format |
| 11 | PMID | PubMed ID | Text | Yes | From PMID element |
| 12 | DOI | Digital Object Identifier | Text | Yes | From ArticleId with IdType="doi" |
| 13 | 免费全文链接 | Free Full Text Link | URL | Yes | **JSON format required** |
| 14 | SCI分区 | SCI Partition | Text | Yes | JCR Q1/Q2/Q3/Q4 or "中文核心期刊" |
| 15 | 中科院分区 | CAS Partition | Text | Yes | 医学1区/2区/3区/4区 or "中文核心期刊" |
| 16 | 影响因子 | Impact Factor | Number | Yes | **MUST be numeric, not string!** |
| 17 | 国标参考文献格式 | GB/T 7714-2015 Reference | Text | Yes | Follow GB/T 7714-2015 standard |

## Field Value Formats

### DateTime (发表年月)
- **Format**: Milliseconds since Unix epoch (1970-01-01 00:00:00 UTC)
- **Example**: 1767196800000 (represents 2026-01-01)
- **Conversion**:
  ```python
  import calendar
  from datetime import datetime
  
  # From year and month to timestamp
  dt = datetime(2026, 3, 1)  # March 2026
  timestamp_ms = int(calendar.timegm(dt.timetuple()) * 1000)
  ```

### URL (免费全文链接)
- **Format**: JSON object with "link" and "text" keys
- **PMC Example**: `{"link": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC123456/", "text": "PMC免费全文"}`
- **DOI Example**: `{"link": "https://doi.org/10.1234/example", "text": "DOI链接"}`

### Number (影响因子)
- **Format**: Numeric value, NOT string
- **Correct**: `15.3`
- **Wrong**: `"15.3"`
- **Zero for Chinese journals**: `0`

### Text (摘要)
- **English**: Structured with labels (BACKGROUND:, METHODS:, RESULTS:, CONCLUSION:)
- **Chinese**: Structured with labels (【背景】... 【目的】... 【方法】... 【结果】... 【结论】...)

## Common Mistakes to Avoid

1. ❌ **String impact factor**: `"影响因子": "15.3"`
   ✅ **Correct**: `"影响因子": 15.3`

2. ❌ **Plain URL**: `"免费全文链接": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC123456/"`
   ✅ **Correct**: `"免费全文链接": {"link": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC123456/", "text": "PMC免费全文"}`

3. ❌ **DateTime as string**: `"发表年月": "2026-03-01"`
   ✅ **Correct**: `"发表年月": 1767196800000`

4. ❌ **Missing Chinese translation**
   ✅ **Correct**: Always translate title and abstract

5. ❌ **Incomplete abstract** (missing sections)
   ✅ **Correct**: Include all sections (BACKGROUND, METHODS, RESULTS, CONCLUSION)

## Field Creation Example

```python
# Create field using feishu_bitable_create_field
feishu_bitable_create_field(
    app_token="app_token",
    table_id="table_id",
    field_name="文献题目（英文）",
    field_type=1  # Text type
)

# DateTime field
feishu_bitable_create_field(
    app_token="app_token",
    table_id="table_id",
    field_name="发表年月",
    field_type=5  # DateTime type
)

# Number field
feishu_bitable_create_field(
    app_token="app_token",
    table_id="table_id",
    field_name="影响因子",
    field_type=2  # Number type
)

# URL field
feishu_bitable_create_field(
    app_token="app_token",
    table_id="table_id",
    field_name="免费全文链接",
    field_type=15  # URL type
)
```


path: references/impact_factors.md
content: # Impact Factors and Journal Partitions

## High-Impact Journals (IF > 10)

| Journal Name | IF | JCR | CAS | Abbreviation |
|-------------|-----|-----|-----|--------------|
| Nature medicine | 58.7 | Q1 | 医学1区 | Nat Med |
| Endocrine reviews | 15.3 | Q1 | 医学1区 | Endocr Rev |
| Gut | 23.1 | Q1 | 医学1区 | Gut |
| Nature | 50.5 | Q1 | 医学1区 | Nature |
| Lancet | 168.9 | Q1 | 医学1区 | Lancet |
| New England Journal of Medicine | 158.5 | Q1 | 医学1区 | N Engl J Med |
| JAMA | 120.7 | Q1 | 医学1区 | JAMA |
| British Medical Journal | 93.3 | Q1 | 医学1区 | BMJ |
| Cancer Cell | 50.3 | Q1 | 医学1区 | Cancer Cell |
| Journal of Clinical Oncology | 42.1 | Q1 | 医学1区 | J Clin Oncol |

## Moderate-Impact Journals (IF 5-10)

| Journal Name | IF | JCR | CAS | Abbreviation |
|-------------|-----|-----|-----|--------------|
| International Journal of Cancer | 6.4 | Q1 | 医学1区 | Int J Cancer |
| Clinical Gastroenterology and Hepatology | 11.5 | Q1 | 医学1区 | Clin Gastroenterol Hepatol |
| Gastric Cancer | 6.2 | Q1 | 医学2区 | Gastric Cancer |
| Neuroendocrinology | 5.5 | Q2 | 医学2区 | Neuroendocrinology |
| Annals of Oncology | 51.8 | Q1 | 医学1区 | Ann Oncol |
| European Journal of Cancer | 10.0 | Q1 | 医学1区 | Eur J Cancer |

## Lower-Impact Journals (IF 3-5)

| Journal Name | IF | JCR | CAS | Abbreviation |
|-------------|-----|-----|-----|--------------|
| World Journal of Gastroenterology | 4.3 | Q2 | 医学3区 | World J Gastroenterol |
| Human Pathology | 3.2 | Q2 | 医学3区 | Hum Pathol |
| Virchows Archiv | 3.4 | Q2 | 医学3区 | Virchows Arch |
| Cancer Epidemiology | 2.4 | Q3 | 医学4区 | Cancer Epidemiol |
| Journal of Clinical Medicine | 3.9 | Q2 | 医学3区 | J Clin Med |
| Frontiers in Oncology | 3.5 | Q2 | 医学3区 | Front Oncol |
| Frontiers in Immunology | 7.3 | Q1 | 医学2区 | Front Immunol |
| Autoimmunity | 3.2 | Q2 | 医学3区 | Autoimmunity |

## Specialty Journals (IF < 3)

| Journal Name | IF | JCR | CAS | Abbreviation |
|-------------|-----|-----|-----|--------------|
| Annals of Diagnostic Pathology | 1.7 | Q3 | 医学4区 | Ann Diagn Pathol |
| Cureus | 1.0 | Q3 | 医学4区 | Cureus |
| Clinical Journal of Gastroenterology | 2.2 | Q3 | 医学4区 | Clin J Gastroenterol |
| Journal of Medical Biochemistry | 2.0 | Q3 | 医学4区 | J Med Biochem |
| The American Surgeon | 1.0 | Q3 | 医学4区 | Am Surg |
| Surgical Oncology | 2.5 | Q2 | 医学3区 | Surg Oncol |
| Journal of Visceral Surgery | 3.0 | Q2 | 医学3区 | J Visc Surg |

## Chinese Journals (No IF)

| Journal Name | IF | JCR | CAS | Notes |
|-------------|-----|-----|-----|-------|
| 中华肝脏病杂志 | 0 | 中文核心期刊 | 中文核心期刊 | Zhonghua Gan Zang Bing Za Zhi |
| 中华胃肠外科杂志 | 0 | 中文核心期刊 | 中文核心期刊 | Zhonghua Wei Chang Wai Ke Za Zhi |

## New Journals (Not Yet Indexed)

| Journal Name | IF | JCR | CAS | Notes |
|-------------|-----|-----|-----|-------|
| Endocrine Oncology | 0 | 新刊待评估 | 新刊待评估 | Endocr Oncol |
| DEN Open | 0 | 新刊待评估 | 新刊待评估 | DEN Open |
| Surgery Case Reports | 0 | 新刊待评估 | 新刊待评估 | Surg Case Rep |

## Impact Factor Lookup

When adding a new journal not in this list:

1. Search journal name in PubMed or Web of Science
2. Check Journal Citation Reports (JCR) for IF and partition
3. Check Chinese Academy of Sciences (CAS) partition
4. Common IF ranges:
   - IF > 10: Q1, 医学1区
   - IF 5-10: Q1-Q2, 医学1-2区
   - IF 3-5: Q2-Q3, 医学3区
   - IF 1-3: Q3-Q4, 医学4区
   - IF < 1 or new: 新刊待评估 or Q4, 医学4区

## CAS Partition Reference

The Chinese Academy of Sciences (CAS) partition system:
- **医学1区** (Medical Area 1): Top-tier journals, IF typically > 10
- **医学2区** (Medical Area 2): High-quality journals, IF typically 5-10
- **医学3区** (Medical Area 3): Good journals, IF typically 3-5
- **医学4区** (Medical Area 4): Standard journals, IF typically 1-3
- **中文核心期刊** (Chinese Core Journal): Chinese-language journals

## JCR Partition Reference

The Journal Citation Reports (JCR) partition system:
- **Q1**: Top 25% of journals in category
- **Q2**: 25-50% of journals in category
- **Q3**: 50-75% of journals in category
- **Q4**: Bottom 25% of journals in category

## Notes

- Impact factors change annually (usually announced in June)
- CAS partitions are updated periodically
- Always use the most current data available
- For new journals without IF, use "新刊待评估" or estimate based on similar journals


path: scripts/parse_pubmed_xml.py
content: #!/usr/bin/env python3
"""
PubMed XML parsing utilities for extracting article metadata
"""

import xml.etree.ElementTree as ET
import json

def parse_pubmed_article(xml_string):
    """
    Parse a single PubMed article from XML
    
    Args:
        xml_string: XML string from PubMed efetch
    
    Returns:
        Dictionary with article metadata
    """
    root = ET.fromstring(xml_string)
    articles = []
    
    for article in root.findall('.//PubmedArticle'):
        data = {}
        
        # PMID
        pmid = article.find('.//PMID')
        data['pmid'] = pmid.text if pmid is not None else None
        
        # Title
        title = article.find('.//ArticleTitle')
        data['title'] = title.text if title is not None else None
        
        # Authors
        authors = []
        for author in article.findall('.//Author'):
            lastname = author.find('LastName')
            forename = author.find('ForeName')
            if lastname is not None:
                name = lastname.text
                if forename is not None and forename.text:
                    name += f" {forename.text[0]}"
                authors.append(name)
        data['authors'] = authors
        
        # First Author Affiliation
        affiliation = article.find('.//AffiliationInfo/Affiliation')
        data['first_affiliation'] = affiliation.text if affiliation is not None else None
        
        # Corresponding Author (usually last author or from affiliation)
        data['corresponding_author'] = authors[-1] if authors else None
        
        # Journal
        journal = article.find('.//Journal/Title')
        data['journal'] = journal.text if journal is not None else None
        
        # Journal Abbreviation
        journal_abbrev = article.find('.//Journal/ISOAbbreviation')
        data['journal_abbrev'] = journal_abbrev.text if journal_abbrev is not None else data['journal']
        
        # Publication Date
        pub_date = article.find('.//PubDate')
        year = None
        month = None
        if pub_date is not None:
            year_elem = pub_date.find('Year')
            month_elem = pub_date.find('Month')
            if year_elem is not None:
                year = year_elem.text
            if month_elem is not None:
                month_text = month_elem.text
                months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04',
                         'May': '05', 'Jun': '06', 'Jul': '07', 'Aug': '08',
                         'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
                month = months.get(month_text, month_text)
        data['year'] = year
        data['month'] = month
        
        # DOI
        doi = None
        for article_id in article.findall('.//ArticleId'):
            if article_id.get('IdType') == 'doi':
                doi = article_id.text
        data['doi'] = doi
        
        # PMC ID
        pmc_id = None
        for article_id in article.findall('.//ArticleId'):
            if article_id.get('IdType') == 'pmc':
                pmc_id = article_id.text
        data['pmc_id'] = pmc_id
        
        # Volume, Issue, Pages
        volume = article.find('.//Journal/JournalIssue/Volume')
        issue = article.find('.//Journal/JournalIssue/Issue')
        pagination = article.find('.//Pagination/MedlinePgn')
        
        data['volume'] = volume.text if volume is not None else None
        data['issue'] = issue.text if issue is not None else None
        data['pages'] = pagination.text if pagination is not None else None
        
        # Abstract (structured with labels)
        abstract_parts = []
        for abstract_text in article.findall('.//Abstract/AbstractText'):
            label = abstract_text.get('Label')
            text = abstract_text.text or ''
            if label:
                abstract_parts.append(f"{label}: {text}")
            else:
                abstract_parts.append(text)
        data['abstract'] = '\n\n'.join(abstract_parts) if abstract_parts else None
        
        articles.append(data)
    
    return articles

def format_gb_reference(article_data):
    """
    Format reference in GB/T 7714-2015 format
    
    Args:
        article_data: Dictionary with article metadata
    
    Returns:
        Formatted reference string
    """
    # Authors (max 3, then et al.)
    authors = article_data.get('authors', [])
    if len(authors) > 3:
        author_str = f"{authors[0]}, {authors[1]}, {authors[2]}, et al."
    else:
        author_str = ", ".join(authors)
    
    # Title
    title = article_data.get('title', '')
    
    # Journal
    journal = article_data.get('journal_abbrev', article_data.get('journal', ''))
    
    # Year, Volume, Issue, Pages
    year = article_data.get('year', '')
    volume = article_data.get('volume', '')
    issue = article_data.get('issue', '')
    pages = article_data.get('pages', '')
    
    # DOI
    doi = article_data.get('doi', '')
    
    # Format: Author. Title[J]. Journal, Year, Volume(Issue): Pages. DOI: xxx.
    ref = f"{author_str}. {title}[J]. {journal}, {year}"
    
    if volume:
        if issue:
            ref += f", {volume}({issue})"
        else:
            ref += f", {volume}"
    
    if pages:
        ref += f": {pages}"
    
    if doi:
        ref += f". DOI: {doi}."
    else:
        ref += "."
    
    return ref

if __name__ == "__main__":
    # Example usage
    sample_xml = """
    <PubmedArticleSet>
        <PubmedArticle>
            <MedlineCitation>
                <PMID>12345678</PMID>
                <Article>
                    <ArticleTitle>Sample Article Title</ArticleTitle>
                    <AuthorList>
                        <Author>
                            <LastName>Smith</LastName>
                            <ForeName>John</ForeName>
                        </Author>
                        <Author>
                            <LastName>Doe</LastName>
                            <ForeName>Jane</ForeName>
                        </Author>
                    </AuthorList>
                    <Journal>
                        <Title>Sample Journal</Title>
                        <ISOAbbreviation>Sample J</ISOAbbreviation>
                        <JournalIssue>
                            <Volume>10</Volume>
                            <Issue>2</Issue>
                            <PubDate>
                                <Year>2026</Year>
                                <Month>Mar</Month>
                            </PubDate>
                        </JournalIssue>
                    </Journal>
                    <Pagination>
                        <MedlinePgn>100-110</MedlinePgn>
                    </Pagination>
                    <Abstract>
                        <AbstractText Label="BACKGROUND">Background text.</AbstractText>
                        <AbstractText Label="METHODS">Methods text.</AbstractText>
                        <AbstractText Label="RESULTS">Results text.</AbstractText>
                        <AbstractText Label="CONCLUSION">Conclusion text.</AbstractText>
                    </Abstract>
                </Article>
            </MedlineCitation>
            <PubmedData>
                <ArticleIdList>
                    <ArticleId IdType="doi">10.1234/sample.2026</ArticleId>
                </ArticleIdList>
            </PubmedData>
        </PubmedArticle>
    </PubmedArticleSet>
    """
    
    articles = parse_pubmed_article(sample_xml)
    for article in articles:
        print(json.dumps(article, indent=2))
        print("\nGB Reference:", format_gb_reference(article))


path: scripts/pubmed_search.py
content: #!/usr/bin/env python3
"""
PubMed E-utilities API wrapper for literature search
"""

import urllib.request
import urllib.parse
import json
import time

def search_pubmed(search_term, max_results=100, sort="pub_date"):
    """
    Search PubMed using E-utilities API
    
    Args:
        search_term: Search query (e.g., "gastric neuroendocrine tumor")
        max_results: Maximum number of results to return
        sort: Sort order (pub_date, relevance, author, journal)
    
    Returns:
        List of PMIDs
    """
    base_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
    
    params = {
        "db": "pubmed",
        "term": search_term,
        "retmax": max_results,
        "retmode": "json",
        "sort": sort
    }
    
    url = f"{base_url}?{urllib.parse.urlencode(params)}"
    
    try:
        with urllib.request.urlopen(url) as response:
            data = json.loads(response.read().decode())
            pmids = data.get("esearchresult", {}).get("idlist", [])
            return pmids
    except Exception as e:
        print(f"Error searching PubMed: {e}")
        return []

def fetch_pubmed_details(pmids):
    """
    Fetch detailed information for PMIDs
    
    Args:
        pmids: List of PMIDs (max 200 per request)
    
    Returns:
        XML string with article details
    """
    if not pmids:
        return None
    
    base_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi"
    
    # PubMed recommends max 200 IDs per request
    pmid_str = ",".join(pmids[:200])
    
    params = {
        "db": "pubmed",
        "id": pmid_str,
        "retmode": "xml"
    }
    
    url = f"{base_url}?{urllib.parse.urlencode(params)}"
    
    try:
        with urllib.request.urlopen(url) as response:
            return response.read().decode()
    except Exception as e:
        print(f"Error fetching PubMed details: {e}")
        return None

def check_pmc_availability(pmid):
    """
    Check if a PMID has PMC full text available
    
    Args:
        pmid: PubMed ID
    
    Returns:
        PMC ID if available, None otherwise
    """
    base_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/elink.fcgi"
    
    params = {
        "dbfrom": "pubmed",
        "db": "pmc",
        "id": pmid,
        "retmode": "json"
    }
    
    url = f"{base_url}?{urllib.parse.urlencode(params)}"
    
    try:
        with urllib.request.urlopen(url) as response:
            data = json.loads(response.read().decode())
            linksets = data.get("linksets", [])
            if linksets and "linksetdbs" in linksets[0]:
                for linksetdb in linksets[0]["linksetdbs"]:
                    if linksetdb.get("dbto") == "pmc":
                        links = linksetdb.get("links", [])
                        if links:
                            return f"PMC{links[0]}"
        return None
    except Exception as e:
        print(f"Error checking PMC availability: {e}")
        return None

if __name__ == "__main__":
    # Example usage
    pmids = search_pubmed("gastric neuroendocrine tumor", max_results=10)
    print(f"Found {len(pmids)} PMIDs: {pmids}")
    
    if pmids:
        xml = fetch_pubmed_details(pmids[:2])
        print(f"Fetched XML for {len(pmids[:2])} articles")


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)