Spaces:
Sleeping
Sleeping
File size: 6,129 Bytes
54d594e 7d94330 54d594e 7d94330 54d594e 7d94330 54d594e 8203f48 54d594e 7d94330 54d594e 7d94330 54d594e 7d94330 54d594e 7d94330 54d594e 59b6c18 7d94330 59b6c18 7d94330 59b6c18 7d94330 59b6c18 7d94330 59b6c18 7d94330 59b6c18 7d94330 59b6c18 7d94330 59b6c18 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | """
SCImago CSV loader β builds in-memory ISSN β journal dict at startup.
Used PRIVATELY for quartile lookup only. Never exposed as branded data source in UI.
Changes from v1.0:
- _norm_issn removed β uses utils.norm_issn
- sourceid now included in ISSN index entries (was missing β broke verify_links)
"""
import csv
import re
import os
from typing import Optional
from utils import norm_issn
_scimago_cache: dict = {}
def load_scimago(csv_path: str = None) -> dict:
global _scimago_cache
if _scimago_cache:
return _scimago_cache
if csv_path is None:
csv_path = os.path.join(os.path.dirname(__file__), "scimagojr_2025.csv")
index = {}
try:
with open(csv_path, encoding="latin-1", newline="") as f:
reader = csv.DictReader(f, delimiter=",")
for row in reader:
issn_raw = row.get("Issn", "")
issn_parts = re.split(r"[,;\s]+", issn_raw)
entry = {
"title": row.get("Title", "").strip(),
"rank": row.get("Rank", "").strip(),
"quartile": row.get("SJR Best Quartile", "").strip(),
"h_index": row.get("H index", "").strip(),
"categories": row.get("Categories", "").strip(),
"areas": row.get("Areas", "").strip(),
"publisher": row.get("Publisher", "").strip(),
"open_access":row.get("Open Access", "No").strip(),
"country": row.get("Country", "").strip(),
"sourceid": row.get("Sourceid", "").strip(), # FIX: was missing
}
for part in issn_parts:
normed = norm_issn(part)
if normed:
index[normed] = entry
_scimago_cache = index
print(f"[SCImago] Loaded {len(index):,} ISSN entries.")
except Exception as e:
print(f"[SCImago] Failed: {e}")
_scimago_cache = {}
return _scimago_cache
def lookup_any(issns: list) -> Optional[dict]:
index = load_scimago()
for issn in issns:
normed = norm_issn(str(issn)) if issn else None
if normed and normed in index:
result = dict(index[normed])
result["matched_issn"] = normed
return result
return None
# ββ Subject search index (separate from ISSN index) ββββββββββββββββββββββ
_subject_cache: list = []
def load_subject_index(csv_path: str = None) -> list:
"""Load full journal list for subject browsing β separate from ISSN lookup."""
global _subject_cache
if _subject_cache:
return _subject_cache
if csv_path is None:
csv_path = os.path.join(os.path.dirname(__file__), "scimagojr_2025.csv")
rows = []
seen_titles = set()
try:
with open(csv_path, encoding="latin-1", newline="") as f:
reader = csv.DictReader(f, delimiter=",")
for row in reader:
title = row.get("Title", "").strip()
if not title or title in seen_titles:
continue
seen_titles.add(title)
issn_raw = row.get("Issn", "")
issn_parts = re.split(r"[,;\s]+", issn_raw)
issns = [norm_issn(p) for p in issn_parts if norm_issn(p)]
primary_issn = issns[0] if issns else None
try:
h = int(row.get("H index", "0").strip() or 0)
except Exception:
h = 0
rows.append({
"title": title,
"issn": primary_issn,
"quartile": row.get("SJR Best Quartile", "").strip(),
"h_index": h,
"categories": row.get("Categories", "").strip().lower(),
"areas": row.get("Areas", "").strip().lower(),
"publisher": row.get("Publisher", "").strip(),
"open_access":row.get("Open Access", "No").strip(),
"country": row.get("Country", "").strip(),
"rank": row.get("Rank", "").strip(),
"sourceid": row.get("Sourceid", "").strip(),
})
_subject_cache = rows
print(f"[SCImago] Subject index loaded: {len(rows):,} unique journals.")
except Exception as e:
print(f"[SCImago] Subject index failed: {e}")
_subject_cache = []
return _subject_cache
def search_by_subject(category: str, area: str, top_n: int = 15) -> list:
"""
Search journals by normalised category + area strings from GPT.
Returns top_n sorted by h_index descending.
Tries: exact category match -> partial category -> area match.
"""
rows = load_subject_index()
cat_lower = category.lower().strip() if category else ""
area_lower = area.lower().strip() if area else ""
def score(r):
cats = r["categories"]
areas = r["areas"]
if cat_lower and cat_lower in cats:
return 3
if cat_lower:
words = [w for w in cat_lower.split() if len(w) > 3]
if any(w in cats for w in words):
return 2
if area_lower and area_lower in areas:
return 1
if area_lower:
words = [w for w in area_lower.split() if len(w) > 3]
if any(w in areas for w in words):
return 1
return 0
scored = [(score(r), r) for r in rows]
matched = [(s, r) for s, r in scored if s > 0]
if not matched:
return []
matched.sort(key=lambda x: (-x[0], -x[1]["h_index"]))
return [r for _, r in matched[:top_n]]
def get_all_areas() -> list:
"""Return sorted unique area names for frontend hints."""
rows = load_subject_index()
areas = set()
for r in rows:
for a in r["areas"].split(";"):
a = a.strip().title()
if a:
areas.add(a)
return sorted(areas)
|