| |
| """Check CC BY 4.0 license metadata for the GRL submission package.""" |
|
|
| from __future__ import annotations |
|
|
| import json |
| import re |
| from pathlib import Path |
|
|
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| PUBLISH_ROOT = ROOT.parent / "grl_publish" |
| OUT = ROOT / "submission_final" |
|
|
|
|
| def read(path: Path) -> str: |
| return path.read_text(encoding="utf-8", errors="replace") if path.exists() else "" |
|
|
|
|
| def has(path: Path, needle: str) -> bool: |
| return needle.lower() in read(path).lower() |
|
|
|
|
| def yaml_license_ok(readme: Path) -> bool: |
| text = read(readme) |
| if not text.startswith("---"): |
| return False |
| parts = text.split("---", 2) |
| if len(parts) < 3: |
| return False |
| return re.search(r"(?mi)^license:\s*cc-by-4\.0\s*$", parts[1]) is not None |
|
|
|
|
| def main() -> int: |
| OUT.mkdir(parents=True, exist_ok=True) |
| failures: list[str] = [] |
| warnings: list[str] = [] |
|
|
| main_tex = ROOT / "main.tex" |
| readme_reproduce = ROOT / "README_reproduce.md" |
| citation = ROOT / "CITATION.cff" |
| license_file = ROOT / "LICENSE" |
| notice_file = ROOT / "NOTICE" |
| publish_readme = PUBLISH_ROOT / "README.md" |
| publish_license = PUBLISH_ROOT / "LICENSE" |
| publish_notice = PUBLISH_ROOT / "NOTICE" |
| publish_citation = PUBLISH_ROOT / "CITATION.cff" |
|
|
| for path in [license_file, publish_license]: |
| if not path.exists(): |
| failures.append(f"Missing LICENSE: {path}") |
| elif not has(path, "CC-BY-4.0") or not has(path, "https://creativecommons.org/licenses/by/4.0/"): |
| failures.append(f"LICENSE does not contain CC-BY-4.0 and license URL: {path}") |
|
|
| for path in [notice_file, publish_notice]: |
| if not path.exists(): |
| failures.append(f"Missing NOTICE: {path}") |
| elif not has(path, "snr_bias") or not has(path, "CC BY 4.0"): |
| failures.append(f"NOTICE missing snr_bias archive URL/name or CC BY 4.0: {path}") |
|
|
| if not has(readme_reproduce, "CC BY 4.0"): |
| failures.append("README_reproduce.md does not contain CC BY 4.0.") |
| if not has(publish_readme, "CC BY 4.0"): |
| failures.append("grl_publish/README.md does not contain CC BY 4.0.") |
| if not yaml_license_ok(publish_readme): |
| failures.append("grl_publish/README.md Hugging Face YAML metadata missing license: cc-by-4.0.") |
|
|
| for path in [citation, publish_citation]: |
| if not path.exists(): |
| failures.append(f"Missing CITATION.cff: {path}") |
| elif not has(path, "CC-BY-4.0") or not has(path, "snr_bias"): |
| failures.append(f"CITATION.cff missing CC-BY-4.0 or snr_bias archive URL/name: {path}") |
|
|
| manuscript = read(main_tex) |
| forbidden = [ |
| "no separate license file", |
| "license file was present", |
| "TBD", |
| "placeholder", |
| "Requires author confirmation", |
| "email to be added", |
| "Affiliation to be added", |
| ] |
| for token in forbidden: |
| if token.lower() in manuscript.lower(): |
| failures.append(f"Forbidden manuscript text remains: {token}") |
|
|
| required_open_research = [ |
| "CC BY 4.0", |
| "snr_bias", |
| "Hugging Face", |
| "SeismicX-Cont", |
| "DOI: 10.57967/hf/9006", |
| "SeisDispFusion-NCF", |
| "DOI: 10.57967/hf/9114", |
| r"\citeA{yu2026snrbias}", |
| r"\citeA{yu2026seismicxcont}", |
| r"\citeA{yu2026seisdispfusionncf}", |
| ] |
| for token in required_open_research: |
| if token not in manuscript: |
| failures.append(f"Open Research missing required text: {token}") |
|
|
| bad_creditx1 = [ |
| "CREDIT-X1local data are redistributed", |
| "CREDIT-X1local waveforms are redistributed", |
| "raw CREDIT-X1local data are included", |
| ] |
| for token in bad_creditx1: |
| if token.lower() in manuscript.lower(): |
| failures.append(f"Potential false redistribution claim: {token}") |
|
|
| if not PUBLISH_ROOT.exists(): |
| warnings.append(f"Publish archive root not found: {PUBLISH_ROOT}") |
|
|
| status = "PASS" if not failures else "FAIL" |
| report = { |
| "overall_status": status, |
| "failures": failures, |
| "warnings": warnings, |
| "checked_paths": { |
| "manuscript_root": str(ROOT), |
| "publish_root": str(PUBLISH_ROOT), |
| "main_tex": str(main_tex), |
| "publish_readme": str(publish_readme), |
| }, |
| } |
| (OUT / "license_check.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") |
|
|
| lines = [ |
| "# License Check", |
| "", |
| f"- Overall status: `{status}`", |
| "", |
| "## Failures", |
| "", |
| *([f"- {item}" for item in failures] or ["- None"]), |
| "", |
| "## Warnings", |
| "", |
| *([f"- {item}" for item in warnings] or ["- None"]), |
| ] |
| (OUT / "license_check.md").write_text("\n".join(lines) + "\n", encoding="utf-8") |
| print(json.dumps(report, indent=2)) |
| return 0 if status == "PASS" else 1 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|