Report Structure

This notebook is available to use, reproduce, and validate the report-structure results for ClimateKG. Source notebook: report-structure.ipynb

This notebook is anchored to The Rock, the shared reference note for the ClimateKG ER model.

Key references: - ERM model README - Class instance counts notebook - The Rock reference

The canonical corpus hierarchy is:

Work (Q2) -> Report Series (Q3) -> Report (Q4) -> Text Division (Q5) -> Chapter (Q6)

The graph logic in the ER model is defined through P3 = Part of and P4 = Parts. The live Wikibase is checked against that intended model in order to record what actually exists today, not to assume the whole hierarchy is present.

Query UI: https://climatekg.tibwiki.io/query/

SPARQL endpoint: https://climatekg.tibwiki.io/query/proxy/sparql

Show code
import os
import pandas as pd
from SPARQLWrapper import JSON
from IPython.display import display

from wikibase_auth import DEFAULT_SPARQL_ENDPOINT, DEFAULT_WIKIBASE_URL, build_sparql_client

os.environ["CLIMATEKG_WIKIBASE_URL"] = "https://climatekg.tibwiki.io"
os.environ["CLIMATEKG_SPARQL_ENDPOINT"] = "https://climatekg.tibwiki.io/query/proxy/sparql"
os.environ.setdefault("CLIMATEKG_SPARQL_USERNAME", "ckg")
os.environ.setdefault("CLIMATEKG_SPARQL_PASSWORD", "fairdata")

SPARQL_ENDPOINT = os.getenv("CLIMATEKG_SPARQL_ENDPOINT", DEFAULT_SPARQL_ENDPOINT)
WIKIBASE_URL = os.getenv("CLIMATEKG_WIKIBASE_URL", DEFAULT_WIKIBASE_URL).rstrip("/")
ENTITY_NS = f"{WIKIBASE_URL}/entity/"
PROPERTY_NS = f"{WIKIBASE_URL}/prop/direct/"

print(f"Endpoint: {SPARQL_ENDPOINT}")
print("Libraries imported successfully")

print(f"Entity namespace (RDF prefix): {ENTITY_NS}")
print(f"Property namespace (RDF prefix): {PROPERTY_NS}")
print(f"Example item page: {WIKIBASE_URL}/wiki/Item:Q2")
print(f"Example property page: {WIKIBASE_URL}/wiki/Property:P1")
'ckg'
'fairdata'
Endpoint: https://climatekg.tibwiki.io/query/proxy/sparql
Libraries imported successfully
Entity namespace (RDF prefix): https://climatekg.tibwiki.io/entity/
Property namespace (RDF prefix): https://climatekg.tibwiki.io/prop/direct/
Example item page: https://climatekg.tibwiki.io/wiki/Item:Q2
Example property page: https://climatekg.tibwiki.io/wiki/Property:P1
Show code
class_query = f'''
PREFIX wd: <{ENTITY_NS}>
PREFIX wdt: <{PROPERTY_NS}>

SELECT ?class ?classLabel (COUNT(?item) AS ?count)
WHERE {{
  VALUES ?class {{ wd:Q2 wd:Q3 wd:Q4 wd:Q5 wd:Q6 }}
  OPTIONAL {{ ?item wdt:P1 ?class . }}
  SERVICE wikibase:label {{ bd:serviceParam wikibase:language "en". }}
}}
GROUP BY ?class ?classLabel
ORDER BY ?class
'''

sparql = build_sparql_client(SPARQL_ENDPOINT)
sparql.setQuery(class_query)
sparql.setReturnFormat(JSON)
class_results = sparql.query().convert()
class_rows = class_results.get("results", {}).get("bindings", [])
class_df = pd.DataFrame([{k: v["value"] for k, v in row.items()} for row in class_rows])

print(f"Class counts returned: {len(class_df)}")
display(class_df)
Class counts returned: 5
class classLabel count
0 https://climatekg.tibwiki.io/entity/Q2 Work 2
1 https://climatekg.tibwiki.io/entity/Q3 Report Series 1
2 https://climatekg.tibwiki.io/entity/Q4 Report 7
3 https://climatekg.tibwiki.io/entity/Q5 Text Division 10
4 https://climatekg.tibwiki.io/entity/Q6 Chapter 87
Show code
hierarchy_model = pd.DataFrame([
    {"level": 1, "class": "Q2", "label": "Work", "role": "top-level corpus"},
    {"level": 2, "class": "Q3", "label": "Report Series", "role": "series container"},
    {"level": 3, "class": "Q4", "label": "Report", "role": "report container"},
    {"level": 4, "class": "Q5", "label": "Text Division", "role": "division / section"},
    {"level": 5, "class": "Q6", "label": "Chapter", "role": "leaf content unit"},
])

hierarchy_view = hierarchy_model.copy()
hierarchy_view["class_uri"] = hierarchy_view["class"].map(lambda v: f"{WIKIBASE_URL}/entity/{v}")
hierarchy_view = hierarchy_view.merge(
    class_df.rename(columns={"class": "class_uri", "classLabel": "current_label", "count": "items_in_graph"}),
    on="class_uri",
    how="left"
)
hierarchy_view["items_in_graph"] = pd.to_numeric(hierarchy_view["items_in_graph"], errors="coerce").fillna(0).astype(int)

print("Canonical report hierarchy model vs current graph")
display(hierarchy_view[["level", "label", "role", "items_in_graph"]].rename(columns={"label": "expected_label"}))
print("This view shows the intended hierarchy from Work down to Chapter together with the live count of items currently represented in the graph.")
Canonical report hierarchy model vs current graph
level expected_label role items_in_graph
0 1 Work top-level corpus 2
1 2 Report Series series container 1
2 3 Report report container 7
3 4 Text Division division / section 10
4 5 Chapter leaf content unit 87
This view shows the intended hierarchy from Work down to Chapter together with the live count of items currently represented in the graph.

Relationship check

Note: this validation block is intentionally kept as a checkpoint. It confirms whether the live graph contains the expected P3 = Part of and P4 = Parts links before the hierarchy is interpreted more broadly. We will return to it when the model is checked against the current data.

This section is a follow-up validation step: it checks whether the graph actually contains the expected P3 / P4 structure that links the hierarchy together.

Show code
p3_query = f'''
PREFIX wd: <{ENTITY_NS}>
PREFIX wdt: <{PROPERTY_NS}>

SELECT ?item ?itemLabel ?itemType ?itemTypeLabel ?parent ?parentLabel
WHERE {{
  ?item wdt:P3 ?parent .
  OPTIONAL {{ ?item wdt:P1 ?itemType . }}
  SERVICE wikibase:label {{ bd:serviceParam wikibase:language "en". }}
}}
ORDER BY ?itemLabel
LIMIT 200
'''

p4_query = f'''
PREFIX wdt: <{PROPERTY_NS}>
SELECT ?parent ?parentLabel ?child ?childLabel
WHERE {{
  ?parent wdt:P4 ?child .
  SERVICE wikibase:label {{ bd:serviceParam wikibase:language "en". }}
}}
ORDER BY ?parentLabel ?childLabel
LIMIT 200
'''

p3 = build_sparql_client(SPARQL_ENDPOINT)
p3.setQuery(p3_query)
p3.setReturnFormat(JSON)
p3_results = p3.query().convert()
p3_rows = p3_results.get("results", {}).get("bindings", [])
p3_df = pd.DataFrame([{k: v["value"] for k, v in row.items()} for row in p3_rows])

p4 = build_sparql_client(SPARQL_ENDPOINT)
p4.setQuery(p4_query)
p4.setReturnFormat(JSON)
p4_results = p4.query().convert()
p4_rows = p4_results.get("results", {}).get("bindings", [])
p4_df = pd.DataFrame([{k: v["value"] for k, v in row.items()} for row in p4_rows])

print("Direct P3 edges found:")
display(p3_df)
print("Direct P4 edges found:")
display(p4_df)
Direct P3 edges found:
item parent itemType itemLabel itemTypeLabel parentLabel
0 https://climatekg.tibwiki.io/entity/Q2089 https://climatekg.tibwiki.io/entity/Q10 https://climatekg.tibwiki.io/entity/Q2087 1.5DS Acronym Special Report: Global Warming of 1.5°C
1 https://climatekg.tibwiki.io/entity/Q1167 https://climatekg.tibwiki.io/entity/Q77 https://climatekg.tibwiki.io/entity/Q1 1.5°C pathway Category Working Group I: Climate Change 2021 – The Phy...
2 https://climatekg.tibwiki.io/entity/Q1167 https://climatekg.tibwiki.io/entity/Q150 https://climatekg.tibwiki.io/entity/Q1 1.5°C pathway Category Working Group III: Climate Change 2022 – Mitig...
3 https://climatekg.tibwiki.io/entity/Q1168 https://climatekg.tibwiki.io/entity/Q10 https://climatekg.tibwiki.io/entity/Q1 1.5°C warmer worlds Category Special Report: Global Warming of 1.5°C
4 https://climatekg.tibwiki.io/entity/Q1169 https://climatekg.tibwiki.io/entity/Q77 https://climatekg.tibwiki.io/entity/Q1 13C Category Working Group I: Climate Change 2021 – The Phy...
... ... ... ... ... ... ...
195 https://climatekg.tibwiki.io/entity/Q2660 https://climatekg.tibwiki.io/entity/Q35 https://climatekg.tibwiki.io/entity/Q2087 AWD Acronym Special Report: Climate Change and Land
196 https://climatekg.tibwiki.io/entity/Q3069 https://climatekg.tibwiki.io/entity/Q77 https://climatekg.tibwiki.io/entity/Q2087 AZM Acronym Working Group I: Climate Change 2021 – The Phy...
197 https://climatekg.tibwiki.io/entity/Q1172 https://climatekg.tibwiki.io/entity/Q77 https://climatekg.tibwiki.io/entity/Q1 Ablation Category Working Group I: Climate Change 2021 – The Phy...
198 https://climatekg.tibwiki.io/entity/Q1173 https://climatekg.tibwiki.io/entity/Q77 https://climatekg.tibwiki.io/entity/Q1 Abrupt change Category Working Group I: Climate Change 2021 – The Phy...
199 https://climatekg.tibwiki.io/entity/Q1173 https://climatekg.tibwiki.io/entity/Q106 https://climatekg.tibwiki.io/entity/Q1 Abrupt change Category Working Group II: Climate Change 2022 – Impact...

200 rows × 6 columns

Direct P4 edges found:

Full hierarchy contents

This table shows the items currently populating the report hierarchy, with their class, direct parent/child links, and the corresponding wiki page when it exists. It is the complete live view of the structure that the model describes.

Show code
full_hierarchy_query = f'''
PREFIX wd: <{ENTITY_NS}>
PREFIX wdt: <{PROPERTY_NS}>

SELECT ?item ?itemLabel ?class ?classLabel ?parent ?parentLabel ?child ?childLabel
WHERE {{
  VALUES ?class {{ wd:Q2 wd:Q3 wd:Q4 wd:Q5 wd:Q6 }}
  ?item wdt:P1 ?class .
  OPTIONAL {{ ?item wdt:P3 ?parent . }}
  OPTIONAL {{ ?item wdt:P4 ?child . }}
  SERVICE wikibase:label {{ bd:serviceParam wikibase:language "en". }}
}}
ORDER BY ?class ?itemLabel
'''

full_hierarchy = build_sparql_client(SPARQL_ENDPOINT)
full_hierarchy.setQuery(full_hierarchy_query)
full_hierarchy.setReturnFormat(JSON)
full_hierarchy_results = full_hierarchy.query().convert()
full_hierarchy_rows = full_hierarchy_results.get("results", {}).get("bindings", [])
full_hierarchy_df = pd.DataFrame([
    {
        "class": None,
        "classLabel": None,
        "item": None,
        "itemLabel": None,
        "parent": None,
        "parentLabel": None,
        "child": None,
        "childLabel": None,
    } | {k: v["value"] for k, v in row.items()}
    for row in full_hierarchy_rows
])

if not full_hierarchy_df.empty:
    full_hierarchy_df["item_id"] = full_hierarchy_df["item"].str.rsplit("/", n=1, expand=True)[1]
    full_hierarchy_df["wiki_url"] = full_hierarchy_df["item_id"].map(lambda v: f"{WIKIBASE_URL}/wiki/Item:{v}")
    full_hierarchy_df["nested_structure"] = full_hierarchy_df.apply(
        lambda row: f"{row['parentLabel']} -> {row['itemLabel']}" if pd.notna(row.get('parentLabel')) else row['itemLabel'],
        axis=1,
    )
    full_hierarchy_df = full_hierarchy_df[[
        "classLabel",
        "itemLabel",
        "item",
        "parent",
        "item_id",
        "wiki_url",
        "parentLabel",
        "childLabel",
        "nested_structure",
    ]].rename(columns={
        'classLabel': 'class',
        'itemLabel': 'name',
        'item': 'item',
        'parent': 'parent',
        'item_id': 'P number',
        'wiki_url': 'wiki_link',
        'parentLabel': 'parent_label',
        'childLabel': 'part',
        'nested_structure': 'nested_structure',
    })
    display(full_hierarchy_df)
else:
    print("No items found for the canonical hierarchy in the current graph.")

print("This table shows the live population of the report hierarchy and how each item is nested within it.")
class name item parent P number wiki_link parent_label part nested_structure
0 Work IPCC Sixth Assessment Report https://climatekg.tibwiki.io/entity/Q7 None Q7 https://climatekg.tibwiki.io/wiki/Item:Q7 None None IPCC Sixth Assessment Report
1 Work Report Series https://climatekg.tibwiki.io/entity/Q3 None Q3 https://climatekg.tibwiki.io/wiki/Item:Q3 None None Report Series
2 Report Series IPCC Sixth Assessment Report https://climatekg.tibwiki.io/entity/Q7 None Q7 https://climatekg.tibwiki.io/wiki/Item:Q7 None None IPCC Sixth Assessment Report
3 Report Climate Change 2023: Synthesis Report. Contrib... https://climatekg.tibwiki.io/entity/Q189 https://climatekg.tibwiki.io/entity/Q7 Q189 https://climatekg.tibwiki.io/wiki/Item:Q189 IPCC Sixth Assessment Report None IPCC Sixth Assessment Report -> Climate Change...
4 Report Special Report: Climate Change and Land https://climatekg.tibwiki.io/entity/Q35 https://climatekg.tibwiki.io/entity/Q7 Q35 https://climatekg.tibwiki.io/wiki/Item:Q35 IPCC Sixth Assessment Report None IPCC Sixth Assessment Report -> Special Report...
... ... ... ... ... ... ... ... ... ...
102 Chapter Tropical Forests https://climatekg.tibwiki.io/entity/Q149 https://climatekg.tibwiki.io/entity/Q137 Q149 https://climatekg.tibwiki.io/wiki/Item:Q149 Cross-Chapter Papers (Text Division) None Cross-Chapter Papers (Text Division) -> Tropic...
103 Chapter Urban Systems and Other Settlements https://climatekg.tibwiki.io/entity/Q172 https://climatekg.tibwiki.io/entity/Q153 Q172 https://climatekg.tibwiki.io/wiki/Item:Q172 Chapters (Text Division) None Chapters (Text Division) -> Urban Systems and ...
104 Chapter Water https://climatekg.tibwiki.io/entity/Q116 https://climatekg.tibwiki.io/entity/Q110 Q116 https://climatekg.tibwiki.io/wiki/Item:Q116 Chapters (Text Division) None Chapters (Text Division) -> Water
105 Chapter Water Cycle Changes https://climatekg.tibwiki.io/entity/Q96 https://climatekg.tibwiki.io/entity/Q81 Q96 https://climatekg.tibwiki.io/wiki/Item:Q96 Chapters None Chapters -> Water Cycle Changes
106 Chapter Weather and Climate Extreme Events in a Changi... https://climatekg.tibwiki.io/entity/Q101 https://climatekg.tibwiki.io/entity/Q81 Q101 https://climatekg.tibwiki.io/wiki/Item:Q101 Chapters None Chapters -> Weather and Climate Extreme Events...

107 rows × 9 columns

This table shows the live population of the report hierarchy and how each item is nested within it.

Questions: Report structure

From the GitHub issue: https://github.com/TIBHannover/ClimateKG-Data-Bench/issues/1

  1. Show me the full hierarchy of AR6, from the work down to its chapters.
  2. Which chapters of the WGII report are licensed under CC-BY, and what are their DOIs?
  3. For AR6 report items, retrieve licence (P11) and DOI (P10) details, including the licence link where present, and identify which reports are not Creative Commons.

Question 1

Show me the full hierarchy of AR6, from the work down to its chapters.

Show code
from IPython.display import HTML, display

hierarchy_q_map = {
    "Work": "Q2",
    "Report Series": "Q3",
    "Report": "Q4",
    "Text Division": "Q5",
    "Chapter": "Q6",
}

report_tree = full_hierarchy_df.copy()

if report_tree.empty:
    print("No report hierarchy rows available to list.")
else:
    report_tree["class_q"] = report_tree["class"].map(hierarchy_q_map).fillna("Q?")
    report_tree["item"] = report_tree.get("item", pd.Series("", index=report_tree.index)).fillna("")
    report_tree["parent"] = report_tree.get("parent", pd.Series("", index=report_tree.index)).fillna("")
    report_tree["name"] = report_tree["name"].fillna("")

    report_lookup = (
        report_tree[report_tree["class"] == "Report"]
        [["item", "name"]]
        .drop_duplicates(subset=["item"], keep="first")
        .set_index("item")["name"]
        .to_dict()
    )

    work_rows = report_tree[report_tree["class"] == "Work"].drop_duplicates(subset=["item"], keep="first").sort_values("name")
    work_rows = work_rows[~work_rows["item"].fillna("").str.endswith("/Q3", na=False)].copy()
    work_rows = work_rows[~work_rows["name"].fillna("").str.strip().eq("Report Series")].copy()
    series_rows = report_tree[report_tree["class"] == "Report Series"].drop_duplicates(subset=["item"], keep="first").sort_values("name")
    series_rows = series_rows[~series_rows["item"].fillna("").str.endswith("/Q3", na=False)].copy()
    series_rows = series_rows[~series_rows["name"].fillna("").str.strip().eq("Report Series")].copy()
    helper_q7_rows = report_tree[report_tree["item"].str.endswith("/Q7", na=False)].copy()
    helper_q7_rows = helper_q7_rows[~helper_q7_rows["name"].fillna("").str.strip().eq("Report Series")].copy()
    if not helper_q7_rows.empty:
        helper_q7_rows = helper_q7_rows[(helper_q7_rows["class"] == "Work") | (helper_q7_rows["class"] == "Report Series")].copy()
        helper_q7_rows["display_name"] = helper_q7_rows["name"].fillna("Q7")
        if series_rows.empty:
            series_rows = helper_q7_rows[["item", "display_name", "wiki_link"]].rename(columns={"display_name": "name"})
        if work_rows.empty:
            work_rows = helper_q7_rows[["item", "display_name", "wiki_link"]].rename(columns={"display_name": "name"})
    text_divisions = report_tree[report_tree["class"] == "Text Division"].copy()
    text_divisions["report_name"] = text_divisions["parent"].map(report_lookup).fillna("Unassigned")

    chapters = report_tree[report_tree["class"] == "Chapter"].copy()
    td_lookup = (
        text_divisions[["item", "name"]]
        .drop_duplicates(subset=["item"], keep="first")
        .set_index("item")["name"]
        .to_dict()
    )
    chapters["text_division_name"] = chapters["parent"].map(td_lookup).fillna("Unassigned")

    reports = (
        report_tree[report_tree["class"] == "Report"]
        .drop_duplicates(subset=["item"], keep="first")
        .sort_values("name")
    )

    def render_title(row, label_field="name"):
        label = row.get(label_field) or row.get("name") or "Unnamed item"
        item_ref = row.get("item")
        item_id = item_ref.rsplit("/", 1)[-1] if isinstance(item_ref, str) and "/" in item_ref else None
        if item_id:
            label = f"{label} ({item_id})"
        if pd.notna(row.get("wiki_link")) and str(row["wiki_link"]).strip():
            return f'<a href="{row["wiki_link"]}" target="_blank">{label}</a>'
        return str(label)

    html_parts = ['<div><strong>Work, report series, and nested report structure</strong><ul>']

    if work_rows.empty:
        html_parts.append('<li><em>No work items found.</em></li>')
    else:
        for _, work_row in work_rows.iterrows():
            work_title = render_title(work_row)
            html_parts.append(f'<li><strong>Work:</strong> {work_title}<ul>')

            display_series = series_rows[series_rows["item"] == work_row["item"]].copy()
            if display_series.empty and not helper_q7_rows.empty:
                display_series = helper_q7_rows[helper_q7_rows["item"] == work_row["item"]].copy()
            if display_series.empty:
                display_series = pd.DataFrame(columns=["item", "name", "wiki_link"])

            for _, series_row in display_series.iterrows():
                series_title = render_title(series_row)
                html_parts.append(f'<li><strong>Report Series:</strong> {series_title}<ul>')

                series_reports = reports[reports["parent"] == series_row["item"]].drop_duplicates(subset=["item"], keep="first").sort_values("name")
                if series_reports.empty:
                    series_reports = reports[reports["parent"] == work_row["item"]].drop_duplicates(subset=["item"], keep="first").sort_values("name")

                if series_reports.empty:
                    html_parts.append('</ul></li>')
                    continue

                for _, report_row in series_reports.iterrows():
                    report_uri = report_row["item"]
                    report_title = render_title(report_row)
                    html_parts.append(f'<li><strong>{report_title}</strong> -- {report_row["class"]} ({report_row["class_q"]})<ul>')

                    report_text_divisions = text_divisions[
                        text_divisions["parent"] == report_uri
                    ].drop_duplicates(subset=["item"], keep="first").sort_values("name")

                    if report_text_divisions.empty:
                        html_parts.append('<li><em>No text divisions nested under this report.</em></li>')
                    else:
                        for _, td in report_text_divisions.iterrows():
                            td_title = render_title(td)
                            html_parts.append(f'<li><strong>{td_title}</strong> -- {td["class"]} ({td["class_q"]})<ul>')

                            report_chapters = chapters[
                                (chapters["parent"] == td["item"]) | (chapters["item"] == td["item"])
                            ].drop_duplicates(subset=["item"], keep="first").sort_values("name")

                            if report_chapters.empty:
                                html_parts.append('<li><em>No chapters nested under this text division.</em></li>')
                            else:
                                for _, ch in report_chapters.iterrows():
                                    ch_title = render_title(ch)
                                    html_parts.append(f'<li>{ch_title} -- {ch["class"]} ({ch["class_q"]})</li>')

                            html_parts.append('</ul></li>')

                    html_parts.append('</ul></li>')

                html_parts.append('</ul></li>')

            if series_rows.empty and not reports[reports["parent"] == work_row["item"]].empty:
                direct_reports = reports[reports["parent"] == work_row["item"]].drop_duplicates(subset=["item"], keep="first").sort_values("name")
                if not direct_reports.empty:
                    html_parts.append('<li><strong>Reports directly under this work:</strong><ul>')
                    for _, report_row in direct_reports.iterrows():
                        report_title = render_title(report_row)
                        html_parts.append(f'<li>{report_title} -- {report_row["class"]} ({report_row["class_q"]})</li>')
                    html_parts.append('</ul></li>')

    html_parts.append('</ul></div>')
    display(HTML(''.join(html_parts)))

print("This view shows the full Report hierarchy, including Work and Report Series above the report entries.")
Work, report series, and nested report structure
This view shows the full Report hierarchy, including Work and Report Series above the report entries.

Searchable nested hierarchy

This view turns the table into a readable hierarchy so a reader can quickly find a report part and trace its position in the corpus from the top-level work down to the chapter or section it belongs to.

Show code
import html
import json
import uuid
from IPython.display import HTML, display

hierarchy_lookup = full_hierarchy_df.copy()
hierarchy_lookup["class_rank"] = hierarchy_lookup["class"].map({"Work": 1, "Report Series": 2, "Report": 3, "Text Division": 4, "Chapter": 5}).fillna(99)
hierarchy_lookup["path"] = hierarchy_lookup["nested_structure"].fillna(hierarchy_lookup["name"])
hierarchy_lookup = hierarchy_lookup.sort_values(["class_rank", "name"]).drop_duplicates(subset=["name"], keep="first")

search_rows = []
for _, row in hierarchy_lookup.iterrows():
    wiki_link = row.get("wiki_link")
    search_rows.append(
        {
            "name": "" if pd.isna(row.get("name")) else str(row.get("name")),
            "path": "" if pd.isna(row.get("path")) else str(row.get("path")),
            "class": "" if pd.isna(row.get("class")) else str(row.get("class")),
            "parent": "top level" if pd.isna(row.get("parent")) else str(row.get("parent")),
            "wiki_link": "" if pd.isna(wiki_link) else str(wiki_link).strip(),
        }
    )

widget_id = f"hierarchy-search-{uuid.uuid4().hex[:8]}"
data_json = json.dumps(search_rows).replace("</", "<\\/")

html_block = f"""
<div style='max-width:900px'>
  <label for='{widget_id}-input'><strong>Find report part:</strong></label><br>
  <input id='{widget_id}-input' type='text' placeholder='Search names or path...' style='width:100%;max-width:500px;padding:6px 8px;margin:6px 0 12px 0;border:1px solid #bbb;border-radius:4px;'>
  <div id='{widget_id}-results'></div>
</div>
<script>
(function () {{
  const data = {data_json};
  const input = document.getElementById('{widget_id}-input');
  const results = document.getElementById('{widget_id}-results');

  function escapeHtml(value) {{
    return String(value)
      .replaceAll('&', '&amp;')
      .replaceAll('<', '&lt;')
      .replaceAll('>', '&gt;')
      .replaceAll('"', '&quot;')
      .replaceAll("'", '&#039;');
  }}

  function safeLink(url) {{
    if (!url) return '';
    const lower = String(url).toLowerCase();
    if (lower.startsWith('http://') || lower.startsWith('https://')) return url;
    return '';
  }}

  function render() {{
    const query = (input.value || '').trim().toLowerCase();
    const matches = !query
      ? data
      : data.filter((row) =>
          row.name.toLowerCase().includes(query) ||
          row.path.toLowerCase().includes(query) ||
          row.class.toLowerCase().includes(query)
        );

    const rows = matches.slice(0, 50).map((row) => {{
      const url = safeLink(row.wiki_link);
      const label = url
        ? `<a href="${{encodeURI(url)}}" target="_blank" rel="noopener noreferrer">${{escapeHtml(row.name)}}</a>`
        : escapeHtml(row.name);
      return `<li><strong>${{label}}</strong> (${{escapeHtml(row.class)}})<br>Parent: ${{escapeHtml(row.parent)}}<br>Path: ${{escapeHtml(row.path)}}</li>`;
    }}).join('');

    results.innerHTML = `<div><strong>Nested report hierarchy</strong><ul>${{rows || '<li>No matching report part found.</li>'}}</ul></div>`;
  }}

  input.addEventListener('input', render);
  render();
}})();
</script>
"""

display(HTML(html_block))

Question 2

Which chapters of the WGII report are licensed under CC-BY, and what are their DOIs?

Show code
wgii_chapters_query = f'''
PREFIX wd: <{ENTITY_NS}>
PREFIX wdt: <{PROPERTY_NS}>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT DISTINCT ?report ?reportLabel ?chapter ?chapterLabel ?doi
WHERE {{
  ?chapter wdt:P1 wd:Q6 .
  ?chapter wdt:P3 ?textDivision .
  ?textDivision wdt:P1 wd:Q5 .
  ?textDivision wdt:P3 ?report .
  ?report wdt:P1 wd:Q4 ; rdfs:label ?reportLabel .
  FILTER(LANG(?reportLabel) = "en")
  FILTER(CONTAINS(LCASE(?reportLabel), "working group ii:"))

  OPTIONAL {{
    ?chapter wdt:P10 ?doiValue .
    BIND(STR(?doiValue) AS ?doi)
  }}

  SERVICE wikibase:label {{ bd:serviceParam wikibase:language "en". }}
}}
ORDER BY ?chapterLabel
'''

print("SPARQL query for WGII chapter list:")
print(wgii_chapters_query)

wgii_client = build_sparql_client(SPARQL_ENDPOINT)
wgii_client.setQuery(wgii_chapters_query)
wgii_client.setReturnFormat(JSON)
wgii_results = wgii_client.query().convert()
wgii_rows = wgii_results.get("results", {}).get("bindings", [])

wgii_chapters_df = pd.DataFrame([{k: v["value"] for k, v in row.items()} for row in wgii_rows])

if wgii_chapters_df.empty:
    print("No WGII chapters found.")
else:
    for col in ["report", "reportLabel", "chapter", "chapterLabel", "doi"]:
        if col not in wgii_chapters_df.columns:
            wgii_chapters_df[col] = ""

    wgii_chapters_df["report_qid"] = wgii_chapters_df["report"].astype(str).str.rsplit("/", n=1).str[-1]
    wgii_chapters_df["chapter_qid"] = wgii_chapters_df["chapter"].astype(str).str.rsplit("/", n=1).str[-1]
    wgii_chapters_df["doi"] = wgii_chapters_df["doi"].fillna("").astype(str)

    # De-duplicate by chapter item identity, not label text.
    wgii_chapters_df = wgii_chapters_df.sort_values(["chapterLabel", "doi"]).drop_duplicates(subset=["chapter_qid"], keep="first")

    print(f"WGII chapters returned (deduplicated by chapter QID): {len(wgii_chapters_df)}")
    display(
        wgii_chapters_df[["reportLabel", "report_qid", "chapterLabel", "chapter_qid", "doi"]]
        .rename(columns={
            "reportLabel": "report",
            "chapterLabel": "chapter",
            "doi": "doi (P10)",
        })
    )
SPARQL query for WGII chapter list:

PREFIX wd: <https://climatekg.tibwiki.io/entity/>
PREFIX wdt: <https://climatekg.tibwiki.io/prop/direct/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT DISTINCT ?report ?reportLabel ?chapter ?chapterLabel ?doi
WHERE {
  ?chapter wdt:P1 wd:Q6 .
  ?chapter wdt:P3 ?textDivision .
  ?textDivision wdt:P1 wd:Q5 .
  ?textDivision wdt:P3 ?report .
  ?report wdt:P1 wd:Q4 ; rdfs:label ?reportLabel .
  FILTER(LANG(?reportLabel) = "en")
  FILTER(CONTAINS(LCASE(?reportLabel), "working group ii:"))

  OPTIONAL {
    ?chapter wdt:P10 ?doiValue .
    BIND(STR(?doiValue) AS ?doi)
  }

  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY ?chapterLabel

WGII chapters returned (deduplicated by chapter QID): 24
report report_qid chapter chapter_qid doi (P10)
0 Working Group II: Climate Change 2022 – Impact... Q106 Africa Q126 10.1017/9781009325844.011
1 Working Group II: Climate Change 2022 – Impact... Q106 Asia Q127 10.1017/9781009325844.012
2 Working Group II: Climate Change 2022 – Impact... Q106 Biodiversity Hotspots Q140 10.1017/9781009325844.018
3 Working Group II: Climate Change 2022 – Impact... Q106 Central and South America Q129 10.1017/9781009325844.014
4 Working Group II: Climate Change 2022 – Impact... Q106 Cities and Settlements by the Sea Q141 10.1017/9781009325844.019
5 Working Group II: Climate Change 2022 – Impact... Q106 Cities, Settlements and Key Infrastructure Q120 10.1017/9781009325844.008
6 Working Group II: Climate Change 2022 – Impact... Q106 Climate Resilient Development Pathways Q136 10.1017/9781009325844.027
7 Working Group II: Climate Change 2022 – Impact... Q106 Decision-Making Options for Managing Risk Q135 10.1017/9781009325844.026
8 Working Group II: Climate Change 2022 – Impact... Q106 Deserts, Semiarid Areas and Desertification Q142 10.1017/9781009325844.020
9 Working Group II: Climate Change 2022 – Impact... Q106 Europe Q130 10.1017/9781009325844.015
10 Working Group II: Climate Change 2022 – Impact... Q106 Food, Fibre and Other Ecosystem Products Q117 10.1017/9781009325844.007
11 Working Group II: Climate Change 2022 – Impact... Q106 Health, Wellbeing and the Changing Structure o... Q123 10.1017/9781009325844.009
12 Working Group II: Climate Change 2022 – Impact... Q106 Key Risks across Sectors and Regions Q133 10.1017/9781009325844.025
13 Working Group II: Climate Change 2022 – Impact... Q106 Mediterranean Region Q143 10.1017/9781009325844.021
14 Working Group II: Climate Change 2022 – Impact... Q106 Mountains Q145 10.1017/9781009325844.022
15 Working Group II: Climate Change 2022 – Impact... Q106 North America Q131 10.1017/9781009325844.016
16 Working Group II: Climate Change 2022 – Impact... Q106 Oceans and Coastal Ecosystems and Their Services Q114 10.1017/9781009325844.005
17 Working Group II: Climate Change 2022 – Impact... Q106 Point of Departure and Key Concepts Q111 10.1017/9781009325844.003
18 Working Group II: Climate Change 2022 – Impact... Q106 Polar Regions Q146 10.1017/9781009325844.023
19 Working Group II: Climate Change 2022 – Impact... Q106 Poverty, Livelihoods and Sustainable Development Q125 10.1017/9781009325844.010
20 Working Group II: Climate Change 2022 – Impact... Q106 Small Islands Q132 10.1017/9781009325844.017
21 Working Group II: Climate Change 2022 – Impact... Q106 Terrestrial and Freshwater Ecosystems and Thei... Q112 10.1017/9781009325844.004
22 Working Group II: Climate Change 2022 – Impact... Q106 Tropical Forests Q149 10.1017/9781009325844.024
23 Working Group II: Climate Change 2022 – Impact... Q106 Water Q116 10.1017/9781009325844.006
Show code
q2_query = f'''
PREFIX wd: <{ENTITY_NS}>
PREFIX wdt: <{PROPERTY_NS}>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT DISTINCT ?report ?reportLabel ?chapter ?chapterLabel ?doi ?license ?licenseLabel ?licenseLink
WHERE {{
  ?chapter wdt:P1 wd:Q6 .
  ?chapter wdt:P3 ?textDivision .
  ?textDivision wdt:P1 wd:Q5 .
  ?textDivision wdt:P3 ?report .
  ?report wdt:P1 wd:Q4 ; rdfs:label ?reportLabel .
  FILTER(LANG(?reportLabel) = "en")
  FILTER(CONTAINS(LCASE(?reportLabel), "working group ii:"))

  OPTIONAL {{
    ?chapter wdt:P10 ?doiValue .
    BIND(STR(?doiValue) AS ?doi)
  }}

  OPTIONAL {{
    ?report wdt:P11 ?license .
    OPTIONAL {{ ?license rdfs:label ?licenseLabel . FILTER(LANG(?licenseLabel) = "en") }}
    BIND(
      IF(
        isIRI(?license),
        STR(?license),
        IF(REGEX(STR(?license), "^https?://"), STR(?license), "")
      ) AS ?licenseLink
    )
  }}

  SERVICE wikibase:label {{ bd:serviceParam wikibase:language "en". }}

  BIND(COALESCE(?licenseLabel, STR(?license), "") AS ?licenseText)
  FILTER(
    REGEX(LCASE(?licenseText), "creative commons|cc[- ]?by|cc by")
    || REGEX(LCASE(COALESCE(?licenseLink, "")), "creativecommons.org")
  )
}}
ORDER BY ?chapterLabel
'''

print("SPARQL query for Question 2:")
print(q2_query)

q2_client = build_sparql_client(SPARQL_ENDPOINT)
q2_client.setQuery(q2_query)
q2_client.setReturnFormat(JSON)
q2_results = q2_client.query().convert()
q2_rows = q2_results.get("results", {}).get("bindings", [])

q2_df = pd.DataFrame([{k: v["value"] for k, v in row.items()} for row in q2_rows])

if q2_df.empty:
    print("No WGII chapters with inherited report-level Creative Commons licensing were returned.")
else:
    expected_cols = ["report", "reportLabel", "chapter", "chapterLabel", "doi", "licenseLabel", "license", "licenseLink"]
    for col in expected_cols:
        if col not in q2_df.columns:
            q2_df[col] = ""

    q2_df["report_qid"] = q2_df["report"].astype(str).str.rsplit("/", n=1).str[-1]
    q2_df["chapter_qid"] = q2_df["chapter"].astype(str).str.rsplit("/", n=1).str[-1]
    q2_df["license_qid"] = q2_df["license"].astype(str).where(
        q2_df["license"].astype(str).str.contains("/entity/", regex=False),
        ""
    ).str.rsplit("/", n=1).str[-1]
    q2_df["doi"] = q2_df["doi"].fillna("").astype(str)

    # De-duplicate by report+chapter item identity.
    q2_df = q2_df.sort_values(["chapterLabel", "doi"]).drop_duplicates(subset=["report_qid", "chapter_qid"], keep="first")

    q2_display = pd.DataFrame({
        "report": q2_df["reportLabel"].fillna("").astype(str),
        "report_qid": q2_df["report_qid"],
        "chapter": q2_df["chapterLabel"].fillna("").astype(str),
        "chapter_qid": q2_df["chapter_qid"],
        "doi (chapter P10)": q2_df["doi"],
        "inherited_license_label (report P11)": q2_df["licenseLabel"].fillna("").astype(str),
        "inherited_license_qid": q2_df["license_qid"],
        "inherited_license_value (report P11)": q2_df["license"].fillna("").astype(str),
        "inherited_license_link": q2_df["licenseLink"].fillna("").astype(str),
    })

    print(f"WGII chapters with inherited Creative Commons report licensing (deduplicated): {len(q2_display)}")
    display(q2_display)
SPARQL query for Question 2:

PREFIX wd: <https://climatekg.tibwiki.io/entity/>
PREFIX wdt: <https://climatekg.tibwiki.io/prop/direct/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT DISTINCT ?report ?reportLabel ?chapter ?chapterLabel ?doi ?license ?licenseLabel ?licenseLink
WHERE {
  ?chapter wdt:P1 wd:Q6 .
  ?chapter wdt:P3 ?textDivision .
  ?textDivision wdt:P1 wd:Q5 .
  ?textDivision wdt:P3 ?report .
  ?report wdt:P1 wd:Q4 ; rdfs:label ?reportLabel .
  FILTER(LANG(?reportLabel) = "en")
  FILTER(CONTAINS(LCASE(?reportLabel), "working group ii:"))

  OPTIONAL {
    ?chapter wdt:P10 ?doiValue .
    BIND(STR(?doiValue) AS ?doi)
  }

  OPTIONAL {
    ?report wdt:P11 ?license .
    OPTIONAL { ?license rdfs:label ?licenseLabel . FILTER(LANG(?licenseLabel) = "en") }
    BIND(
      IF(
        isIRI(?license),
        STR(?license),
        IF(REGEX(STR(?license), "^https?://"), STR(?license), "")
      ) AS ?licenseLink
    )
  }

  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }

  BIND(COALESCE(?licenseLabel, STR(?license), "") AS ?licenseText)
  FILTER(
    REGEX(LCASE(?licenseText), "creative commons|cc[- ]?by|cc by")
    || REGEX(LCASE(COALESCE(?licenseLink, "")), "creativecommons.org")
  )
}
ORDER BY ?chapterLabel

WGII chapters with inherited Creative Commons report licensing (deduplicated): 24
report report_qid chapter chapter_qid doi (chapter P10) inherited_license_label (report P11) inherited_license_qid inherited_license_value (report P11) inherited_license_link
0 Working Group II: Climate Change 2022 – Impact... Q106 Africa Q126 10.1017/9781009325844.011 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
1 Working Group II: Climate Change 2022 – Impact... Q106 Asia Q127 10.1017/9781009325844.012 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
2 Working Group II: Climate Change 2022 – Impact... Q106 Biodiversity Hotspots Q140 10.1017/9781009325844.018 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
3 Working Group II: Climate Change 2022 – Impact... Q106 Central and South America Q129 10.1017/9781009325844.014 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
4 Working Group II: Climate Change 2022 – Impact... Q106 Cities and Settlements by the Sea Q141 10.1017/9781009325844.019 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
5 Working Group II: Climate Change 2022 – Impact... Q106 Cities, Settlements and Key Infrastructure Q120 10.1017/9781009325844.008 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
6 Working Group II: Climate Change 2022 – Impact... Q106 Climate Resilient Development Pathways Q136 10.1017/9781009325844.027 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
7 Working Group II: Climate Change 2022 – Impact... Q106 Decision-Making Options for Managing Risk Q135 10.1017/9781009325844.026 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
8 Working Group II: Climate Change 2022 – Impact... Q106 Deserts, Semiarid Areas and Desertification Q142 10.1017/9781009325844.020 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
9 Working Group II: Climate Change 2022 – Impact... Q106 Europe Q130 10.1017/9781009325844.015 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
10 Working Group II: Climate Change 2022 – Impact... Q106 Food, Fibre and Other Ecosystem Products Q117 10.1017/9781009325844.007 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
11 Working Group II: Climate Change 2022 – Impact... Q106 Health, Wellbeing and the Changing Structure o... Q123 10.1017/9781009325844.009 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
12 Working Group II: Climate Change 2022 – Impact... Q106 Key Risks across Sectors and Regions Q133 10.1017/9781009325844.025 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
13 Working Group II: Climate Change 2022 – Impact... Q106 Mediterranean Region Q143 10.1017/9781009325844.021 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
14 Working Group II: Climate Change 2022 – Impact... Q106 Mountains Q145 10.1017/9781009325844.022 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
15 Working Group II: Climate Change 2022 – Impact... Q106 North America Q131 10.1017/9781009325844.016 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
16 Working Group II: Climate Change 2022 – Impact... Q106 Oceans and Coastal Ecosystems and Their Services Q114 10.1017/9781009325844.005 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
17 Working Group II: Climate Change 2022 – Impact... Q106 Point of Departure and Key Concepts Q111 10.1017/9781009325844.003 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
18 Working Group II: Climate Change 2022 – Impact... Q106 Polar Regions Q146 10.1017/9781009325844.023 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
19 Working Group II: Climate Change 2022 – Impact... Q106 Poverty, Livelihoods and Sustainable Development Q125 10.1017/9781009325844.010 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
20 Working Group II: Climate Change 2022 – Impact... Q106 Small Islands Q132 10.1017/9781009325844.017 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
21 Working Group II: Climate Change 2022 – Impact... Q106 Terrestrial and Freshwater Ecosystems and Thei... Q112 10.1017/9781009325844.004 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
22 Working Group II: Climate Change 2022 – Impact... Q106 Tropical Forests Q149 10.1017/9781009325844.024 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0
23 Working Group II: Climate Change 2022 – Impact... Q106 Water Q116 10.1017/9781009325844.006 CC-BY-NC-ND 4.0 CC-BY-NC-ND 4.0

Question 3

For AR6 report items, retrieve licence (P11) and DOI (P10) details, including the licence link where present, and identify which reports are not Creative Commons.

Show code
q3_query = f'''
PREFIX wd: <{ENTITY_NS}>
PREFIX wdt: <{PROPERTY_NS}>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT ?report ?reportLabel ?doi ?license ?licenseLabel ?licenseLink ?ccStatus
WHERE {{
  ?report wdt:P1 wd:Q4 ; rdfs:label ?reportLabel .
  FILTER(LANG(?reportLabel) = "en")

  OPTIONAL {{
    ?report wdt:P10 ?doiValue .
    BIND(STR(?doiValue) AS ?doi)
  }}

  OPTIONAL {{
    ?report wdt:P11 ?license .
    OPTIONAL {{ ?license rdfs:label ?licenseLabel . FILTER(LANG(?licenseLabel) = "en") }}
    BIND(
      IF(
        isIRI(?license),
        STR(?license),
        IF(REGEX(STR(?license), "^https?://"), STR(?license), "")
      ) AS ?licenseLink
    )
  }}

  BIND(COALESCE(?licenseLabel, STR(?license), "No license value") AS ?licenseText)
  BIND(
    IF(
      REGEX(LCASE(?licenseText), "creative commons|cc[- ]?by|cc[- ]?0|cc by"),
      "Creative Commons",
      "Not clearly Creative Commons"
    ) AS ?ccStatus
  )
}}
ORDER BY ?reportLabel
'''

print("SPARQL query for Question 3:")
print(q3_query)

q3_client = build_sparql_client(SPARQL_ENDPOINT)
q3_client.setQuery(q3_query)
q3_client.setReturnFormat(JSON)
q3_results = q3_client.query().convert()
q3_rows = q3_results.get("results", {}).get("bindings", [])

q3_df = pd.DataFrame([{k: v["value"] for k, v in row.items()} for row in q3_rows])

if q3_df.empty:
    print("No report-level P11/P10 rows were returned.")
else:
    expected_cols = ["reportLabel", "doi", "licenseLabel", "license", "licenseLink", "ccStatus"]
    for col in expected_cols:
        if col not in q3_df.columns:
            q3_df[col] = ""

    display_df = pd.DataFrame({
        "report": q3_df["reportLabel"].fillna("").astype(str),
        "doi (P10)": q3_df["doi"].fillna("").astype(str),
        "license_label": q3_df["licenseLabel"].fillna("").astype(str),
        "license_value (P11)": q3_df["license"].fillna("").astype(str),
        "license_link": q3_df["licenseLink"].fillna("").astype(str),
        "creative_commons_status": q3_df["ccStatus"].fillna("Not clearly Creative Commons").astype(str),
    })

    print(f"Report metadata rows returned: {len(display_df)}")
    display(display_df)

    status_summary = (
        display_df[["report", "creative_commons_status"]]
        .drop_duplicates()
        .groupby("creative_commons_status", as_index=False)["report"]
        .count()
        .rename(columns={"report": "report_count"})
    )
    print("Creative Commons status summary (distinct reports):")
    display(status_summary)
SPARQL query for Question 3:

PREFIX wd: <https://climatekg.tibwiki.io/entity/>
PREFIX wdt: <https://climatekg.tibwiki.io/prop/direct/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT ?report ?reportLabel ?doi ?license ?licenseLabel ?licenseLink ?ccStatus
WHERE {
  ?report wdt:P1 wd:Q4 ; rdfs:label ?reportLabel .
  FILTER(LANG(?reportLabel) = "en")

  OPTIONAL {
    ?report wdt:P10 ?doiValue .
    BIND(STR(?doiValue) AS ?doi)
  }

  OPTIONAL {
    ?report wdt:P11 ?license .
    OPTIONAL { ?license rdfs:label ?licenseLabel . FILTER(LANG(?licenseLabel) = "en") }
    BIND(
      IF(
        isIRI(?license),
        STR(?license),
        IF(REGEX(STR(?license), "^https?://"), STR(?license), "")
      ) AS ?licenseLink
    )
  }

  BIND(COALESCE(?licenseLabel, STR(?license), "No license value") AS ?licenseText)
  BIND(
    IF(
      REGEX(LCASE(?licenseText), "creative commons|cc[- ]?by|cc[- ]?0|cc by"),
      "Creative Commons",
      "Not clearly Creative Commons"
    ) AS ?ccStatus
  )
}
ORDER BY ?reportLabel

Report metadata rows returned: 7
report doi (P10) license_label license_value (P11) license_link creative_commons_status
0 Climate Change 2023: Synthesis Report. Contrib... 10.59327/IPCC/AR6-9789291691647 No license specified Not clearly Creative Commons
1 Special Report: Climate Change and Land 10.1017/9781009157988 CC-BY-NC-ND 4.0 Creative Commons
2 Special Report: Global Warming of 1.5°C 10.1017/9781009157940 CC-BY-NC-ND 4.0 Creative Commons
3 Special Report: The Ocean and Cryosphere in a ... 10.1017/9781009157964 CC-BY-NC-ND 4.0 Creative Commons
4 Working Group I: Climate Change 2021 – The Phy... 10.1017/9781009157896 CC-BY-NC-ND 4.0 Creative Commons
5 Working Group II: Climate Change 2022 – Impact... 10.1017/9781009325844 CC-BY-NC-ND 4.0 Creative Commons
6 Working Group III: Climate Change 2022 – Mitig... 10.1017/9781009157926 CC-BY-NC-ND 4.0 Creative Commons
Creative Commons status summary (distinct reports):
creative_commons_status report_count
0 Creative Commons 6
1 Not clearly Creative Commons 1

Interpretation

The ER model recorded in The Rock defines the intended corpus structure: Q2 Work, Q3 Report Series, Q4 Report, Q5 Text Division, and Q6 Chapter. In that schema, P3 is the parent / part-of relationship and P4 is the child / parts relationship.

The live graph is now populated across the expected report hierarchy: class counts show items at all five levels, from Q2 through Q6. This means the graph is no longer a chapter-only snapshot and now contains a working structure that can be compared directly against the ER model.

The current data therefore supports a more nuanced conclusion: the graph reflects a genuinely populated report hierarchy, while the notebook continues to use the canonical ER model as the baseline for checking which entities and relationship patterns are present and how they fit together.