Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ package.json
node_modules

**/*.quarto_ipynb

docs/doc-repo.json
5 changes: 5 additions & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ project:
type: website
preview:
port: 4200
resources:
- docs/**
pre-render:
- scripts/build_doc_repo.py

website:
favicon: assets/img/logos/apex_icon_blue.svg
Expand Down Expand Up @@ -114,6 +118,7 @@ website:
- href: interoperability/algohostingenv.md
- href: interoperability/geospatial_explorer.qmd
- href: interoperability/businessmodel.md
- href: docs/overview.md

format:
html:
Expand Down
11 changes: 11 additions & 0 deletions css/components/_datatable.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#docTable {
margin-top: 1rem;
}

#docTable_length {
margin-bottom: 1rem;
}

#docTable_info {
margin-top: 1rem;
}
3 changes: 2 additions & 1 deletion css/custom.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
@import "./components/headings";
@import "./components/references";
@import "./components/listings";
@import "./components/datatable";

/*-- scss:rules --*/

Expand Down Expand Up @@ -57,4 +58,4 @@
url("./assets/fonts/NotesEsa-BoldItalic.woff2") format("woff2"),
url("./assets/fonts/NotesEsa-BoldItalic.woff") format("woff");
font-display: swap;
}
}
Binary file not shown.
4 changes: 4 additions & 0 deletions docs/apex/APEx_D01_PMP_Project Management Plan_3.1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: Application Propagation Environments
title: Project Management Plan
tags:
- Project Management Plan
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
project: Application Propagation Environments
title: Interoperability and Compliance Guidelines
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
project: Application Propagation Environments
title: Instantiation Services & Specifications
182 changes: 182 additions & 0 deletions docs/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
---
title: "Document Repository"
format: html
---
```{=html}

<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>

<style>
#docContainer {
display: flex;
gap: 1rem;
align-items: flex-start;
margin-top: 2.5rem;

}

#error {
padding: 20px;
background-color: #310413;
color: #f54280;
border-radius: 10px;
}

/* project list column */
#projectsContainer {
flex: 0 0 220px;
height: 70vh;
overflow: auto;
background: #00868138;
padding: 1rem;
border-radius: 10px;
}
#projectsContainer h4, #docListContainer h4 {
margin-top: 0;
margin-bottom: 1rem;
}

.project-item {
padding: 8px 10px;
margin: 4px 0;
border-radius: 4px;
cursor: pointer;
user-select: none;
}
.project-item:hover {
background: #f0f4ff;
color: black
}
.project-item.selected {
background: #00af9d;
color: white;
font-weight: 600;
}

#docListContainer{
padding: 1rem;
flex: 3;
}

/* table column */
#docTable_wrapper {
border-collapse: collapse;
}

.dataTables_wrapper input {
border-radius: 10px !important;
margin-left: 1rem !important;
}
</style>

<div id="error"></div>
<div id="docContainer">
<div id="projectsContainer">
<h4>Projects</h4>
<div id="projectList" aria-label="Project list">
</div>
</div>

<div id="docListContainer">
<h4 id="selectedProject"></h4>
<table id="docTable">
<thead>
<tr><th>Title</th><th>File</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>


<script>
let docs = [];
let table = null;

// helper to build DataTable rows for a project
function rowsForProject(projectName) {
const filtered = docs.filter(d => d.project === projectName);
const results = filtered.map(it => ([
it.title || it.filename || "",
`<a href="${it.path}" target="_blank" rel="noopener">${it.filename || (it.path || "").split("/").pop()}</a>`
]));
console.log(results);
return results;
}

// initialize empty DataTable (columns set)
function initTable() {
table = $('#docTable').DataTable({
data: [], // start empty
columns: [
{ title: "Title" },
{ title: "File", orderable: false, searchable: false }
],
pageLength: 10,
order: [[1, 'asc']],
});
}

function populateProjectList(projects) {
// create items with data-project attribute
const html = projects.map(p => `<div class="project-item" data-project="${p}">${p}</div>`).join('');
$('#projectList').html(html);
// click handler (delegated)
$('#projectList').off('click').on('click', '.project-item', function() {
const project = $(this).attr('data-project');
// highlight selected project and unhighlight others
$('#projectList .project-item').removeClass('selected');
$(this).addClass('selected');

// set title of selected project
$("#selectedProject").html(project);

// populate DataTable for this project
const newRows = rowsForProject(project);
table.clear();
if (newRows.length) table.rows.add(newRows);
table.draw();
});

// optionally auto-click first project if exists
const first = $('#projectList .project-item').first();
if (first.length) {
first.trigger('click');
}
}

function showError(error) {
$('#error').html(error);
$('#docContainer').hide();
}

fetch("doc-repo.json")
.then(r => {
if (!r.ok) {
console.error("Could not load listing", r);
throw new Error("Could not load document listing");
} else {
$('#error').hide();
return r.json();
}
})
.then(items => {
docs = items || [];

// collect unique projects in original order (or sorted)
const projects = [...new Set(items.map(it => it.project))];

console.log(projects);

initTable();
populateProjectList(projects);
})
.catch(e => {
console.error(e);
showError(e)
});
</script>

```
Binary file not shown.
4 changes: 4 additions & 0 deletions docs/test/APEx_D01_PMP_Project Management Plan_3.1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: Test
title: TEST Project Management Plan
tags:
- Project Management Plan
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
project: Test
title: TEST Interoperability and Compliance Guidelines
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
project: Test
title: TEST Instantiation Services & Specifications
90 changes: 90 additions & 0 deletions scripts/build_doc_repo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""
Build an index of PDFs under ROOT , merging metadata from
sidecar YAML files with the same basename (e.g. Report.pdf -> Report.yml).
Writes doc-index.json
"""

from pathlib import Path
import json
import sys
import argparse
import logging
import yaml

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")


def load_yaml(yaml_path: Path):
try:
with yaml_path.open("r", encoding="utf-8") as f:
data = yaml.safe_load(f) or {}
if not isinstance(data, dict):
logging.warning(
"YAML %s did not contain a mapping (object). Ignoring.", yaml_path
)
return {}
return data
except Exception as e:
logging.warning("Failed to read YAML %s: %s", yaml_path, e)
return {}


def build_index(root: Path):
items = []
if not root.exists():
logging.error("Root folder %s does not exist.", root)
return items

for project_dir in sorted(p for p in root.iterdir() if p.is_dir()):
project_name = project_dir.name
for pdf_path in sorted(project_dir.glob("*.pdf")):
basename = pdf_path.stem # filename without suffix
yaml_path = pdf_path.with_suffix(".yml")

if not yaml_path.exists():
logging.warning(
f"YAML configuration does not exist for {basename}, skipping"
)

else:
metadata = load_yaml(yaml_path)

item = {}
item["project"] = metadata.get("project", project_name)
item["title"] = metadata.get("title", basename)
item["filename"] = pdf_path.name
item["path"] = "/" + str(pdf_path.as_posix())
tags = metadata.get("tags")
if isinstance(tags, str):
item["tags"] = [t.strip() for t in tags.split(",") if t.strip()]
elif isinstance(tags, (list, tuple)):
item["tags"] = list(tags)
else:
item["tags"] = None
items.append(item)
logging.info(f"Indexed: {item['path']}")

return items


def write_json(items, out_path: Path):
with out_path.open("w", encoding="utf-8") as f:
json.dump(items, f, indent=2, ensure_ascii=False)
logging.info("Wrote %d items to %s", len(items), out_path)


def main(argv):
p = argparse.ArgumentParser()
p.add_argument(
"--root", "-r", default="docs", help="Root folder containing project subfolders"
)
p.add_argument("--out", "-o", default="docs/doc-repo.json", help="Output JSON file")
args = p.parse_args(argv)

root = Path(args.root)
items = build_index(root)
write_json(items, Path(args.out))


if __name__ == "__main__":
main(sys.argv[1:])