Skip to content
Merged
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
45 changes: 45 additions & 0 deletions .github/method_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# {{method}}

<!--- This file is autogenerated. Edit {{id}}.json to make changes in this page. --->

{{#method_description_content}}{{{method_description_content}}}{{/method_description_content}}

## Method Details

* **Vendor:** {{#vendor_content}}{{{vendor_content}}}{{/vendor_content}}{{^vendor_content}}Not available{{/vendor_content}}

* **Catalog Number:** {{#catalog_number_content}}{{{catalog_number_content}}}{{/catalog_number_content}}{{^catalog_number_content}}Not available{{/catalog_number_content}}

* **Catalog Webpage:** {{#catalog_webpage_url}}[{{{catalog_webpage_url}}}]({{{catalog_webpage_url}}}){{/catalog_webpage_url}}{{^catalog_webpage_url}}Not available{{/catalog_webpage_url}}

* **VHP4Safety Workflow Stage:** {{#vhp4safety_workflow_stage_content}}{{{vhp4safety_workflow_stage_content}}}{{/vhp4safety_workflow_stage_content}}{{^vhp4safety_workflow_stage_content}}Not available{{/vhp4safety_workflow_stage_content}}

* **Workflow Substage:** {{#workflow_substage_content}}{{{workflow_substage_content}}}{{/workflow_substage_content}}{{^workflow_substage_content}}Not available{{/workflow_substage_content}}

* **Case Study:** {{#case_study_content}}{{{case_study_content}}}{{/case_study_content}}{{^case_study_content}}Not available{{/case_study_content}}

* **Regulatory Question:** {{#regulatory_question_content}}{{{regulatory_question_content}}}{{/regulatory_question_content}}{{^regulatory_question_content}}Not available{{/regulatory_question_content}}

## Additional Information

* **Assay Name:** {{#assay_name_content}}{{{assay_name_content}}}{{/assay_name_content}}{{^assay_name_content}}Not available{{/assay_name_content}}

* **Data Producer:** {{#data_producer_content}}{{{data_producer_content}}}{{/data_producer_content}}{{^data_producer_content}}Not available{{/data_producer_content}}

* **Relevant AOP Wiki Key Event(s):** {{#relevant_aop_wiki_key_event(s)_to_the_assay_content}}{{{relevant_aop_wiki_key_event(s)_to_the_assay_content}}}{{/relevant_aop_wiki_key_event(s)_to_the_assay_content}}{{^relevant_aop_wiki_key_event(s)_to_the_assay_content}}Not available{{/relevant_aop_wiki_key_event(s)_to_the_assay_content}}

* **Relevant AOP Wiki Adverse Outcome Pathway(s):** {{#relevant_aop_wiki_adverse_outcome_pathway(s)_to_the_assay_content}}{{{relevant_aop_wiki_adverse_outcome_pathway(s)_to_the_assay_content}}}{{/relevant_aop_wiki_adverse_outcome_pathway(s)_to_the_assay_content}}{{^relevant_aop_wiki_adverse_outcome_pathway(s)_to_the_assay_content}}Not available{{/relevant_aop_wiki_adverse_outcome_pathway(s)_to_the_assay_content}}

* **Available SOP or Protocol:** {{#available_sop_or_protocol_content}}{{{available_sop_or_protocol_content}}}{{/available_sop_or_protocol_content}}{{^available_sop_or_protocol_content}}Not available{{/available_sop_or_protocol_content}}

* **Ontology Term:** {{#ontology_term_content}}[{{{ontology_term_content}}}]({{{ontology_term_content}}}){{/ontology_term_content}}{{^ontology_term_content}}Not available{{/ontology_term_content}}

* **Citation:** {{#citation_content}}{{{citation_content}}}{{/citation_content}}{{^citation_content}}Not available{{/citation_content}}

---

*Generated from issue #{{issue_number}}*

<script>
(async()=>{document.querySelectorAll('li').forEach(li=>{if(li.textContent.includes('Ontology Term:')&&li.textContent.includes('No response')){const a=li.querySelector('a');if(a){const span=document.createElement('span');span.textContent='No ontology annotation';a.replaceWith(span)}}});const e=encodeURIComponent,t=document.querySelectorAll('a[href^="http://purl"]');for(const r of t){const t=r.closest('li');if(t?.textContent?.includes("Ontology Term:")){const o=r.href;try{const t=await fetch(`https://www.ebi.ac.uk/ols4/api/terms/${e(e(o))}?lang=en`),n=await t.json(),a=n._embedded?.terms?.[0];a&&(r.textContent=a.short_form?`${a.label} (${a.short_form})`:a.label,r.href=a._links.self.href)}catch(e){}await new Promise((e=>setTimeout(e,100)))}}})();
</script>
178 changes: 44 additions & 134 deletions .github/workflows/generate_method_markdown.yml
Original file line number Diff line number Diff line change
@@ -1,149 +1,59 @@
name: Generate Method Markdown from JSON
name: Generate Method Documentation with Mustache

on:
workflow_dispatch:
push:
paths:
- 'docs/methods/*.json'
- '.github/method_template.md'
- '.github/workflows/generate_method_markdown.yml'
pull_request:
paths:
- 'docs/methods/*.json'
- '.github/method_template.md'
- '.github/workflows/generate_method_markdown.yml'

jobs:
generate-markdown:
generate-docs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install dependencies
run: |
pip install requests

- name: Generate Markdown from JSON files
run: |
python - <<'EOF'
import os, json, glob, datetime

def clean_field_name(field):
"""Convert field names to human readable format"""
return field.replace('_content', '').replace('_', ' ').title()

def format_value(value):
"""Format field values for display"""
if isinstance(value, str):
# Handle URLs
if value.startswith('http'):
return f"[{value}]({value})"
if value.startswith('//'): # temp fix
return f"[https{value}](https{value})"
# Skip empty responses
if value in ['_No response_', 'N/A', 'n/a', 'TBD', 'tbd']:
return None
return value

# Process all JSON files in docs/methods/
json_files = glob.glob('docs/methods/*.json')

for json_file in json_files:
try:
with open(json_file, 'r') as f:
data = json.load(f)

method_id = data.get('id', 'unknown')
md_file = f'docs/methods/{method_id}.md'

# Generate markdown content
with open(md_file, 'w') as f:
# Header
method_name = data.get('name_of_the_method_content', data.get('service', 'Unknown Method'))
method_name = method_name.replace('[METHOD]: ', '')
f.write(f"# {method_name}\n\n")

# Description
description = data.get('method_description_content')
if description and description != '_No response_':
f.write(f"{description}\n\n")

# Method Details
f.write("## Method Details\n\n")

# Key fields to display in order
key_fields = [
('type_content', 'Type'),
('vendor_content', 'Vendor'),
('catalog_number_content', 'Catalog Number'),
('catalog_webpage_url', 'Catalog Webpage'),
('vhp4safety_workflow_stage_content', 'VHP4Safety Workflow Stage'),
('workflow_substage_content', 'Workflow Substage'),
('relevant_vhp4safety_regulatory_question(s)_content', 'Relevant VHP4Safety Regulatory Questions'),
('relevant_AOP_Wiki_Key_Event(s)_content', 'Relevant AOP Wiki Key Event(s) to the assay'),
('relevant_AOP_Wiki_AOP(s)_content', 'Relevant AOP Wiki Adverse Outcome Pathway(s) to the assay'),
('case_study_content', 'Case Study'),
('regulatory_question_content', 'Regulatory Question'),
('available_sop_content', 'Available SOP or protocol'),

]

for field_key, field_label in key_fields:
value = data.get(field_key)
formatted_value = format_value(value)
if formatted_value:
f.write(f"**{field_label}:** {formatted_value}\n\n")

# Citation section
citation = data.get('citation_content')
if citation and citation != '_No response_':
f.write("## Citation\n\n")
f.write(f"{citation}\n\n")

# Additional Information
f.write("## Additional Information\n\n")

# Display other fields not already shown
skip_fields = {
'id', 'service', 'issue_number', 'timestamp',
'name_of_the_method_content', 'method_description_content',
'citation_content'
}
skip_fields.update([field[0] for field in key_fields])

for key, value in data.items():
if key not in skip_fields:
formatted_value = format_value(value)
if formatted_value:
clean_key = clean_field_name(key)
f.write(f"**{clean_key}:** {formatted_value}\n\n")

# Footer
f.write("---\n\n")
f.write(f"*Generated from issue #{data.get('issue_number', 'unknown')} on {datetime.datetime.now().strftime('%Y-%m-%d')}*\n")

print(f"Generated markdown: {md_file}")

except Exception as e:
print(f"Error processing {json_file}: {e}")

print("Markdown generation complete")
EOF

- name: Commit generated markdown files
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

if [ -n "$(git status --porcelain docs/methods/*.md 2>/dev/null)" ]; then
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'

- name: Install mustache CLI
run: npm install -g mustache

- name: Generate method documentation
run: |
for json_file in docs/methods/*.json; do
if [ -f "$json_file" ]; then
basename=$(basename "$json_file" .json)

# Validate JSON
jq empty "$json_file" || { echo "Invalid JSON: $json_file"; exit 1; }

# Generate markdown using mustache template
mustache "$json_file" .github/method_template.md > "docs/methods/${basename}.md"

echo "Generated docs/methods/${basename}.md"
fi
done

- name: Commit and push generated documentation
run: |
git config --global user.name 'GitHub Action'
git config --global user.email 'action@github.com'
git pull
git add docs/methods/*.md
git commit -m "Auto-generate method markdown files"
git push
else
echo "No markdown files to commit"
fi
if git diff --exit-code --staged; then
echo "No changes to commit"
else
git commit -m 'Generate method documentation from JSON files'
git push
fi
40 changes: 22 additions & 18 deletions docs/methods/5_cfda_assay_to_determine_cytotoxicity.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
# Unknown Method
# 5-CFDA assay to determine cytotoxicity

<!--- This file is autogenerated. Edit 5_cfda_assay_to_determine_cytotoxicity.json to make changes in this page. --->

Fluorescence-based determination of cell membrane damage

## Method Details

**Vendor:** Invitrogen
* **Vendor:** Invitrogen

**Catalog Number:** C1354
* **Catalog Number:** C1354

**Catalog Webpage:** [https://www.thermofisher.com/order/catalog/product/C1354](https://www.thermofisher.com/order/catalog/product/C1354)
* **Catalog Webpage:** [https://www.thermofisher.com/order/catalog/product/C1354](https://www.thermofisher.com/order/catalog/product/C1354)

**VHP4Safety Workflow Stage:** Adverse Outcome
* **VHP4Safety Workflow Stage:** Adverse Outcome

**Workflow Substage:** Cell death, Adverse outcome
* **Workflow Substage:** Cell death, Adverse outcome

**Case Study:** parkinson
* **Case Study:** parkinson

**Regulatory Question:** Q1
* **Regulatory Question:** Q1

## Additional Information

**Method:** 5-CFDA assay to determine cytotoxicity

**Assay Name:** 5-CFDA, AM (5-Carboxyfluorescein Diacetate, Acetoxymethyl Ester)
* **Assay Name:** 5-CFDA, AM (5-Carboxyfluorescein Diacetate, Acetoxymethyl Ester)

**Data Producer:** Julia Meerman
* **Data Producer:** Julia Meerman

**Relevant Aop Wiki Key Event(S) To The Assay:** KE1825
* **Relevant AOP Wiki Key Event(s):** KE1825

**Relevant Aop Wiki Adverse Outcome Pathway(S) To The Assay:** AOP464
* **Relevant AOP Wiki Adverse Outcome Pathway(s):** AOP464

**Https:** [https//www.thermofisher.com/order/catalog/product/C1354](https//www.thermofisher.com/order/catalog/product/C1354)
* **Available SOP or Protocol:** No response

**Available Sop Or Protocol:** No response
* **Ontology Term:** [No response](No response)

**Ontology Term:** No response
* **Citation:** _No response_

---

*Generated from issue #170 on 2025-12-03*
*Generated from issue #170*

<script>
(async()=>{document.querySelectorAll('li').forEach(li=>{if(li.textContent.includes('Ontology Term:')&&li.textContent.includes('No response')){const a=li.querySelector('a');if(a){const span=document.createElement('span');span.textContent='No ontology annotation';a.replaceWith(span)}}});const e=encodeURIComponent,t=document.querySelectorAll('a[href^="http://purl"]');for(const r of t){const t=r.closest('li');if(t?.textContent?.includes("Ontology Term:")){const o=r.href;try{const t=await fetch(`https://www.ebi.ac.uk/ols4/api/terms/${e(e(o))}?lang=en`),n=await t.json(),a=n._embedded?.terms?.[0];a&&(r.textContent=a.short_form?`${a.label} (${a.short_form})`:a.label,r.href=a._links.self.href)}catch(e){}await new Promise((e=>setTimeout(e,100)))}}})();
</script>
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
# Unknown Method
# Alamar Blue assay for detection of mitochondrial activity

<!--- This file is autogenerated. Edit alamar_blue_assay_for_detection_of_mitochondrial_activity.json to make changes in this page. --->

Fluorescence-based detection of mitochondrial metabolic activity

## Method Details

**Vendor:** Invitrogen
* **Vendor:** Invitrogen

**Catalog Number:** R12204
* **Catalog Number:** R12204

**Catalog Webpage:** [https://www.thermofisher.com/order/catalog/product/R12204?SID=srch-hj-R12204](https://www.thermofisher.com/order/catalog/product/R12204?SID=srch-hj-R12204)
* **Catalog Webpage:** [https://www.thermofisher.com/order/catalog/product/R12204?SID=srch-hj-R12204](https://www.thermofisher.com/order/catalog/product/R12204?SID=srch-hj-R12204)

**VHP4Safety Workflow Stage:** AOP
* **VHP4Safety Workflow Stage:** AOP

**Workflow Substage:** Mitochondrial dysfunction, mitochondrial damage
* **Workflow Substage:** Mitochondrial dysfunction, mitochondrial damage

**Case Study:** parkinson
* **Case Study:** parkinson

**Regulatory Question:** Q1
* **Regulatory Question:** Q1

## Additional Information

**Method:** Alamar Blue assay for detection of mitochondrial activity

**Assay Name:** Resazurin, Sodium Salt

**Data Producer:** Julia Meerman
* **Assay Name:** Resazurin, Sodium Salt

**Http:** [https//id.nlm.nih.gov/mesh/C005843](https//id.nlm.nih.gov/mesh/C005843)
* **Data Producer:** Julia Meerman

**Type Url:** [http://id.nlm.nih.gov/mesh/C005843](http://id.nlm.nih.gov/mesh/C005843)
* **Relevant AOP Wiki Key Event(s):** KE177, KE1547

**Relevant Aop Wiki Key Event(S) To The Assay:** KE177, KE1547
* **Relevant AOP Wiki Adverse Outcome Pathway(s):** AOP464

**Relevant Aop Wiki Adverse Outcome Pathway(S) To The Assay:** AOP464
* **Available SOP or Protocol:** No response

**Https:** [https//www.thermofisher.com/order/catalog/product/R12204?SID=srch-hj-R12204](https//www.thermofisher.com/order/catalog/product/R12204?SID=srch-hj-R12204)
* **Ontology Term:** [http://purl.enanomapper.org/onto/ENM_8000224](http://purl.enanomapper.org/onto/ENM_8000224)

**Available Sop Or Protocol:** No response

**Ontology Term:** [http://purl.enanomapper.org/onto/ENM_8000224](http://purl.enanomapper.org/onto/ENM_8000224)
* **Citation:** _No response_

---

*Generated from issue #166 on 2025-12-03*
*Generated from issue #166*

<script>
(async()=>{document.querySelectorAll('li').forEach(li=>{if(li.textContent.includes('Ontology Term:')&&li.textContent.includes('No response')){const a=li.querySelector('a');if(a){const span=document.createElement('span');span.textContent='No ontology annotation';a.replaceWith(span)}}});const e=encodeURIComponent,t=document.querySelectorAll('a[href^="http://purl"]');for(const r of t){const t=r.closest('li');if(t?.textContent?.includes("Ontology Term:")){const o=r.href;try{const t=await fetch(`https://www.ebi.ac.uk/ols4/api/terms/${e(e(o))}?lang=en`),n=await t.json(),a=n._embedded?.terms?.[0];a&&(r.textContent=a.short_form?`${a.label} (${a.short_form})`:a.label,r.href=a._links.self.href)}catch(e){}await new Promise((e=>setTimeout(e,100)))}}})();
</script>
Loading
Loading