Track journal special issue deadlines
All checks were successful
Build and deploy static pages / build-and-push (push) Successful in 12s
All checks were successful
Build and deploy static pages / build-and-push (push) Successful in 12s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -222,7 +222,8 @@ def _conf_body(fm: dict, digests: list[dict] | None = None, base_path: str = "")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def _journal_body(fm: dict, digests: list[dict] | None = None, base_path: str = "") -> str:
|
||||
def _journal_body(fm: dict, digests: list[dict] | None = None, base_path: str = "",
|
||||
special_issues: list[dict] | None = None) -> str:
|
||||
q = fm.get("scimago_quartile") or "—"
|
||||
sjr = fm.get("scimago_sjr") or "—"
|
||||
h = fm.get("scimago_h_index") or "—"
|
||||
@@ -253,6 +254,25 @@ def _journal_body(fm: dict, digests: list[dict] | None = None, base_path: str =
|
||||
suffix = f"— {count} papers" if count else "*(digest pending)*"
|
||||
lines.append(f"| **Latest digest** | [{title} {year}]({base_path}/digests/{slug}/) {suffix} |")
|
||||
|
||||
if special_issues:
|
||||
lines += ["\n## Special Issues\n"]
|
||||
lines += [
|
||||
"| Title | Abstract | Paper deadline | Notification | Guest Editors |",
|
||||
"|---|---|---|---|---|",
|
||||
]
|
||||
for si in special_issues:
|
||||
si_title = si.get("title", "Special Issue")
|
||||
abstract = si.get("abstract_deadline") or ""
|
||||
paper = si.get("submission_deadline") or si.get("abstract_deadline") or "TBD"
|
||||
notif = si.get("notification") or ""
|
||||
editors = ", ".join(si.get("guest_editors", [])) or "—"
|
||||
abs_cell = abstract if (abstract and abstract != paper) else ""
|
||||
cfp_url = si.get("cfp_url", "")
|
||||
if cfp_url:
|
||||
si_title = f"[{si_title}]({cfp_url})"
|
||||
lines.append(f"| {si_title} | {abs_cell} | {paper} | {notif} | {editors} |")
|
||||
lines.append("")
|
||||
|
||||
if digests:
|
||||
lines += ["\n## Previous Editions\n"]
|
||||
for d in sorted(digests, key=lambda x: x["year"], reverse=True):
|
||||
@@ -278,13 +298,25 @@ def _journal_body(fm: dict, digests: list[dict] | None = None, base_path: str =
|
||||
# Date helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _parse_date(s: str) -> str | None:
|
||||
"""'Apr 1, 2026' → '2026-04-01', or None if unparseable."""
|
||||
def _parse_date(s: str, month_only: bool = False) -> str | None:
|
||||
"""'Apr 1, 2026' → '2026-04-01', or None if unparseable.
|
||||
|
||||
With month_only, also accepts a bare month and year ('Jan 2027' →
|
||||
'2027-01-01'). Journal special issues often announce notifications at
|
||||
month granularity; conference deadlines are always full dates, so this
|
||||
stays opt-in to keep their output unchanged.
|
||||
"""
|
||||
for fmt in ("%b %d, %Y", "%B %d, %Y"):
|
||||
try:
|
||||
return datetime.strptime(s.strip(), fmt).strftime("%Y-%m-%d")
|
||||
except ValueError:
|
||||
pass
|
||||
if month_only:
|
||||
for fmt in ("%b %Y", "%B %Y"):
|
||||
try:
|
||||
return datetime.strptime(s.strip(), fmt).strftime("%Y-%m-%d")
|
||||
except ValueError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
@@ -429,6 +461,29 @@ def _calendar_events(venues: dict, deadlines: dict, base_path: str = "") -> list
|
||||
if location:
|
||||
ev["extendedProps"] = {"location": location}
|
||||
events.append(ev)
|
||||
|
||||
# Journal special issue deadlines — one purple series, distinct from conferences
|
||||
journal_color = "#7b2cbf"
|
||||
for journal in venues.get("journals") or []:
|
||||
acronym = journal.get("acronym", "")
|
||||
url = f"{base_path}/venues/journals/{acronym.lower()}/"
|
||||
for si in journal.get("special_issues") or []:
|
||||
si_title = si.get("title", "Special Issue")
|
||||
cfp_url = si.get("cfp_url", "")
|
||||
for field, label in [
|
||||
("abstract_deadline", "abstract deadline"),
|
||||
("submission_deadline", "paper deadline"),
|
||||
("notification", "notification"),
|
||||
]:
|
||||
# Notifications are often month-only ('Jan 2027'); deadlines are not.
|
||||
iso = _parse_date(si.get(field) or "", month_only=(field == "notification"))
|
||||
if iso:
|
||||
ev = {"title": f"{acronym} SI — {si_title} — {label}",
|
||||
"start": iso, "url": url, "color": journal_color,
|
||||
"allDay": True}
|
||||
if cfp_url:
|
||||
ev["extendedProps"] = {"cfp_url": cfp_url}
|
||||
events.append(ev)
|
||||
return events
|
||||
|
||||
|
||||
@@ -441,7 +496,9 @@ def _calendar_body(venues: dict, icore: dict, deadlines: dict, base_path: str =
|
||||
pass
|
||||
return datetime.max
|
||||
|
||||
rows = []
|
||||
lines: list[str] = []
|
||||
|
||||
conf_rows = []
|
||||
for conf in venues.get("conferences") or []:
|
||||
acronym = conf.get("acronym", "")
|
||||
rank = icore.get(acronym.upper(), "—") or "—"
|
||||
@@ -451,24 +508,57 @@ def _calendar_body(venues: dict, icore: dict, deadlines: dict, base_path: str =
|
||||
notif = dl_data.get("notification") or ""
|
||||
event = dl_data.get("event_dates") or "—"
|
||||
location = dl_data.get("location") or ""
|
||||
rows.append((acronym, abstract, paper, notif, event, location, rank))
|
||||
conf_rows.append((acronym, conf.get("full_name", ""), abstract, paper,
|
||||
notif, event, location, rank))
|
||||
|
||||
rows.sort(key=lambda r: (parse_deadline(r[2]), r[0]))
|
||||
conf_rows.sort(key=lambda r: (parse_deadline(r[3]), r[0]))
|
||||
|
||||
lines = [
|
||||
"\n| Conference | Abstract | Paper deadline | Notification | Event dates | Location | ICORE |",
|
||||
"|---|---|---|---|---|---|---|",
|
||||
lines += [
|
||||
"\n## Conference Deadlines\n",
|
||||
"| Venue | Name | Abstract | Paper deadline | Notification | Event dates | Location | ICORE |",
|
||||
"|---|---|---|---|---|---|---|---|",
|
||||
]
|
||||
for acronym, abstract, paper, notif, event, location, rank in rows:
|
||||
for acronym, full_name, abstract, paper, notif, event, location, rank in conf_rows:
|
||||
abs_cell = abstract if (abstract and abstract != paper) else ""
|
||||
link = f"[{acronym}]({base_path}/venues/conferences/{acronym.lower()}/)"
|
||||
lines.append(
|
||||
f"| [{acronym}]({base_path}/venues/conferences/{acronym.lower()}/) "
|
||||
f"| {abs_cell} | {paper} | {notif} | {event} | {location} | {rank} |"
|
||||
f"| {link} | {full_name} | {abs_cell} | {paper} | {notif} "
|
||||
f"| {event} | {location} | {rank} |"
|
||||
)
|
||||
|
||||
si_rows = []
|
||||
for journal in venues.get("journals") or []:
|
||||
acronym = journal.get("acronym", "")
|
||||
for si in journal.get("special_issues") or []:
|
||||
abstract = si.get("abstract_deadline") or ""
|
||||
paper = si.get("submission_deadline") or si.get("abstract_deadline") or "TBD"
|
||||
notif = si.get("notification") or ""
|
||||
si_title = si.get("title", "Special Issue")
|
||||
cfp_url = si.get("cfp_url", "")
|
||||
if cfp_url:
|
||||
si_title = f"[{si_title}]({cfp_url})"
|
||||
editors = ", ".join(si.get("guest_editors", [])) or "—"
|
||||
si_rows.append((acronym, si_title, abstract, paper, notif, editors))
|
||||
|
||||
si_rows.sort(key=lambda r: (parse_deadline(r[3]), r[0]))
|
||||
|
||||
if si_rows:
|
||||
lines += [
|
||||
"\n## Journal Special Issues\n",
|
||||
"| Venue | Title | Abstract | Paper deadline | Notification | Guest Editors |",
|
||||
"|---|---|---|---|---|---|",
|
||||
]
|
||||
for acronym, si_title, abstract, paper, notif, editors in si_rows:
|
||||
abs_cell = abstract if (abstract and abstract != paper) else ""
|
||||
link = f"[{acronym}]({base_path}/venues/journals/{acronym.lower()}/)"
|
||||
lines.append(
|
||||
f"| {link} | {si_title} | {abs_cell} | {paper} | {notif} | {editors} |"
|
||||
)
|
||||
|
||||
lines += [
|
||||
"",
|
||||
"> Deadlines sourced from conference websites and [WikiCFP](http://www.wikicfp.com/).",
|
||||
"> Deadlines sourced from conference websites, [WikiCFP](http://www.wikicfp.com/), "
|
||||
"and journal special issue CFPs.",
|
||||
"",
|
||||
]
|
||||
return "\n".join(lines) + "\n"
|
||||
@@ -586,7 +676,8 @@ def gen_journals(venues: dict, scimago: dict[str, dict], root: Path,
|
||||
}
|
||||
fm = merge_fm(existing_fm, new_fm, PRESERVED_VENUE_FIELDS)
|
||||
digests = (venue_digests or {}).get(acronym)
|
||||
write_hugo_file(path, fm, _journal_body(fm, digests, base_path))
|
||||
write_hugo_file(path, fm, _journal_body(fm, digests, base_path,
|
||||
journal.get("special_issues")))
|
||||
count += 1
|
||||
print(f" {path.relative_to(root.parent)}", file=sys.stderr)
|
||||
return count
|
||||
|
||||
Reference in New Issue
Block a user