Skip to content

Commit f15f766

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent 692d350 commit f15f766

2 files changed

Lines changed: 152 additions & 42 deletions

File tree

43.6 KB
Binary file not shown.

vfbterms.py

Lines changed: 152 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,19 @@ def get_term_url(label, short_form):
130130
url = label.replace('\\', '').replace(' ', '-').lower() + "-" + short_form.lower()
131131
return re.sub("[^0-9a-zA-Z-_]+", "", url)
132132

133+
def get_report_url(identifier):
134+
"""Create the VFB report URL path for a term identifier."""
135+
return f'/reports/{identifier}'
136+
133137
def is_known_id(identifier):
134138
"""Check if an identifier matches a known VFB prefix."""
135139
return any(identifier.startswith(p) for p in KNOWN_PREFIXES)
136140

137141
def convert_internal_links(text):
138-
"""Convert API markdown links [label](ID) to site inter-page links.
142+
"""Convert API markdown links [label](ID) to VFB report links.
139143
140144
Handles:
141-
- Simple links: [medulla](FBbt_00003748) → [medulla](/term/slug/)
145+
- Simple links: [medulla](FBbt_00003748) → [medulla](/reports/FBbt_00003748)
142146
- IDs that aren't known prefixes are left as-is
143147
"""
144148
if not text:
@@ -148,8 +152,7 @@ def replace_link(match):
148152
label = match.group(1)
149153
identifier = match.group(2)
150154
if is_known_id(identifier):
151-
slug = get_term_url(label, identifier)
152-
return f'[{label}](/term/{slug}/)'
155+
return f'[{label}]({get_report_url(identifier)})'
153156
return match.group(0)
154157

155158
# Use a pattern that handles nested brackets in labels (e.g. gene names with [allele])
@@ -160,7 +163,7 @@ def format_relationships_section(relationships_text):
160163
"""Format Meta.Relationships into markdown bullets.
161164
162165
Input format: [rel_name](rel_id): [target1](id1), [target2](id2); [rel2](id): [target](id)
163-
Output: - **rel_name**: [target1](/term/...), [target2](/term/...)
166+
Output: - **rel_name**: [target1](/reports/...), [target2](/reports/...)
164167
"""
165168
if not relationships_text:
166169
return ""
@@ -192,8 +195,8 @@ def format_types_section(types_text):
192195
"""Format Meta.Types into markdown bullets.
193196
194197
Input format: [type1](id1); [type2](id2)
195-
Output: - [type1](/term/slug1/)
196-
- [type2](/term/slug2/)
198+
Output: - [type1](/reports/id1)
199+
- [type2](/reports/id2)
197200
"""
198201
if not types_text:
199202
return ""
@@ -291,6 +294,9 @@ def format_query_preview(query, term_id):
291294
# Extract name — may be markdown link like [name](id)
292295
name_raw = row.get("label", row.get("name", ""))
293296
name_converted = convert_internal_links(name_raw)
297+
row_id = row.get("id", "")
298+
if row_id and is_known_id(row_id) and name_converted == name_raw and name_raw:
299+
name_converted = f'[{name_raw}]({get_report_url(row_id)})'
294300

295301
# Extract tags
296302
tags_raw = row.get("tags", "")
@@ -304,7 +310,6 @@ def format_query_preview(query, term_id):
304310
img_match = re.search(r'!\[[^\]]*\]\(([^\s)]+)', thumb_raw)
305311
if img_match:
306312
thumb_url = img_match.group(1)
307-
row_id = row.get("id", "")
308313
thumb_html = f'<a href="{VFB_BROWSER_BASE}?id={row_id}"><img src="{thumb_url}" width="80" style="background:#000; border-radius:2px;"/></a>'
309314

310315
lines.append(f'| {thumb_html} | {name_converted} | {tags_display} |')
@@ -389,6 +394,45 @@ def format_publications(publications):
389394
lines.append("".join(parts))
390395
return "\n".join(lines)
391396

397+
def build_hero_card(name, term_id, tags_badges, description_html, comment_html, thumbnails):
398+
"""Build the hero card without blank lines that break Markdown HTML blocks."""
399+
lines = [
400+
'<div class="card mb-4 border-primary">',
401+
'<div class="card-body">',
402+
'<div class="row">',
403+
'<div class="col-md-4 text-center">',
404+
]
405+
406+
for thumb in thumbnails:
407+
lines.append(
408+
f' <a href="{VFB_BROWSER_BASE}?id={term_id}">'
409+
f'<img src="{thumb["url"]}" alt="{name}" class="img-fluid rounded" '
410+
f'style="max-width:200px; background:#000; margin:4px;"/></a>'
411+
)
412+
413+
lines.extend([
414+
'</div>',
415+
'<div class="col-md-8">',
416+
f' <h4>{name}</h4>',
417+
f' <p class="text-muted"><strong>ID:</strong> {term_id}</p>',
418+
f' <div class="mb-2">{tags_badges}</div>',
419+
])
420+
421+
if description_html:
422+
lines.append(f' {description_html}')
423+
if comment_html:
424+
lines.append(f' {comment_html}')
425+
426+
lines.extend([
427+
f' <a href="{VFB_BROWSER_BASE}?id={term_id}" class="btn btn-primary btn-lg mt-2">Open in VFB 3D Browser &rarr;</a>',
428+
'</div>',
429+
'</div>',
430+
'</div>',
431+
'</div>',
432+
])
433+
434+
return "\n".join(lines)
435+
392436
# ─── Page Generation ─────────────────────────────────────────────────────────
393437

394438
def generate_page(term_data):
@@ -448,17 +492,6 @@ def generate_page(term_data):
448492
''')
449493

450494
# ── Hero section ──
451-
thumb_html = ""
452-
if thumbnails:
453-
thumb_imgs = []
454-
for t in thumbnails:
455-
thumb_imgs.append(
456-
f'<a href="{VFB_BROWSER_BASE}?id={term_id}">'
457-
f'<img src="{t["url"]}" alt="{name}" class="img-fluid rounded" '
458-
f'style="max-width:200px; background:#000; margin:4px;"/></a>'
459-
)
460-
thumb_html = "\n ".join(thumb_imgs)
461-
462495
tags_badges = format_tags_badges(tags)
463496

464497
desc_html = ""
@@ -469,24 +502,7 @@ def generate_page(term_data):
469502
if comment:
470503
comment_html = f'<p class="text-muted"><em>{convert_internal_links(comment)}</em></p>'
471504

472-
sections.append(f'''<div class="card mb-4 border-primary">
473-
<div class="card-body">
474-
<div class="row">
475-
<div class="col-md-4 text-center">
476-
{thumb_html}
477-
</div>
478-
<div class="col-md-8">
479-
<h4>{name}</h4>
480-
<p class="text-muted"><strong>ID:</strong> {term_id}</p>
481-
<div class="mb-2">{tags_badges}</div>
482-
{desc_html}
483-
{comment_html}
484-
<a href="{VFB_BROWSER_BASE}?id={term_id}" class="btn btn-primary btn-lg mt-2">Open in VFB 3D Browser &rarr;</a>
485-
</div>
486-
</div>
487-
</div>
488-
</div>
489-
''')
505+
sections.append(build_hero_card(name, term_id, tags_badges, desc_html, comment_html, thumbnails))
490506

491507
# ── Classification ──
492508
types_text = meta.get("Types", "")
@@ -647,21 +663,115 @@ def test_term_page(term_id, term_type="class"):
647663

648664
return all_pass
649665

666+
def test_hero_card_regression():
667+
"""Ensure optional empty fields do not turn the hero CTA into a code block."""
668+
print("=" * 60)
669+
print("Test 0: Hero card CTA markdown regression")
670+
print("=" * 60)
671+
672+
term_data = {
673+
"Name": "adult intercalary segment",
674+
"Id": "FBbt_00003013",
675+
"Meta": {
676+
"Description": "Any intercalary segment of the adult.",
677+
"Comment": "",
678+
"Types": "[adult procephalic segment](FBbt_00003010); [intercalary segment](FBbt_00000010)",
679+
},
680+
"Tags": ["Adult", "Anatomy"],
681+
"Synonyms": [],
682+
"Queries": [],
683+
"Licenses": {},
684+
"Publications": [],
685+
"Technique": [],
686+
"Images": {},
687+
"Examples": {},
688+
}
689+
690+
page_content = generate_page(term_data)
691+
hero_cta = f'<a href="{VFB_BROWSER_BASE}?id=FBbt_00003013" class="btn btn-primary btn-lg mt-2">Open in VFB 3D Browser &rarr;</a>'
692+
693+
checks = {
694+
"Hero CTA present": hero_cta in page_content,
695+
"No blank line before hero CTA": "\n \n <a href=" not in page_content,
696+
"Description directly precedes hero CTA": (
697+
'<p>Any intercalary segment of the adult.</p>\n'
698+
f' {hero_cta}'
699+
) in page_content,
700+
}
701+
702+
all_pass = True
703+
for check_name, result in checks.items():
704+
status = "PASS" if result else "FAIL"
705+
if not result:
706+
all_pass = False
707+
print(f" [{status}] {check_name}")
708+
709+
return all_pass
710+
711+
def test_report_link_regression():
712+
"""Ensure generated term links point at VFB report URLs."""
713+
print("=" * 60)
714+
print("Test 1: Report link regression")
715+
print("=" * 60)
716+
717+
converted = convert_internal_links("[DNb08](FBbt_20011340)")
718+
query_preview = format_query_preview(
719+
{
720+
"label": "Neurons with some part in adult intercalary segment",
721+
"count": 2,
722+
"preview_results": {
723+
"rows": [
724+
{
725+
"label": "[DNb08](FBbt_20011340)",
726+
"id": "FBbt_20011340",
727+
"tags": "Adult|Cholinergic|Nervous_system|primary_neuron",
728+
"thumbnail": '[![DNb08](https://www.virtualflybrain.org/data/VFB/i/jrmc/37nx/VFB_00101567/thumbnail.png)](FBbt_20011340)',
729+
},
730+
{
731+
"label": "DNp45",
732+
"id": "FBbt_20011346",
733+
"tags": "Adult|Cholinergic|Nervous_system|primary_neuron",
734+
},
735+
]
736+
},
737+
},
738+
"FBbt_00003013",
739+
)
740+
741+
checks = {
742+
"Markdown links use report path": converted == "[DNb08](/reports/FBbt_20011340)",
743+
"Query preview preserves report link": "[DNb08](/reports/FBbt_20011340)" in query_preview,
744+
"Plain row labels get report link": "[DNp45](/reports/FBbt_20011346)" in query_preview,
745+
}
746+
747+
all_pass = True
748+
for check_name, result in checks.items():
749+
status = "PASS" if result else "FAIL"
750+
if not result:
751+
all_pass = False
752+
print(f" [{status}] {check_name}")
753+
754+
return all_pass
755+
650756
def test_medulla_page():
651757
"""Test page generation for medulla (class) and fru-M-200266 (individual)."""
758+
result0 = test_hero_card_regression()
759+
result1 = test_report_link_regression()
760+
761+
print()
652762
print("=" * 60)
653-
print("Test 1: Class term — medulla (FBbt_00003748)")
763+
print("Test 2: Class term — medulla (FBbt_00003748)")
654764
print("=" * 60)
655-
result1 = test_term_page("FBbt_00003748", "class")
765+
result2 = test_term_page("FBbt_00003748", "class")
656766

657767
print()
658768
print("=" * 60)
659-
print("Test 2: Individual term — fru-M-200266 (VFB_00000001)")
769+
print("Test 3: Individual term — fru-M-200266 (VFB_00000001)")
660770
print("=" * 60)
661-
result2 = test_term_page("VFB_00000001", "individual")
771+
result3 = test_term_page("VFB_00000001", "individual")
662772

663773
print()
664-
if result1 and result2:
774+
if result0 and result1 and result2 and result3:
665775
print("All tests PASSED")
666776
return True
667777
else:

0 commit comments

Comments
 (0)