cjm-transcript-verify 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cjm_transcript_verify/__init__.py +1 -0
- cjm_transcript_verify/_modidx.py +127 -0
- cjm_transcript_verify/components/__init__.py +0 -0
- cjm_transcript_verify/components/helpers.py +53 -0
- cjm_transcript_verify/components/integrity_checks.py +117 -0
- cjm_transcript_verify/components/sample_segments.py +207 -0
- cjm_transcript_verify/components/step_renderer.py +162 -0
- cjm_transcript_verify/components/verification_summary.py +124 -0
- cjm_transcript_verify/html_ids.py +49 -0
- cjm_transcript_verify/models.py +98 -0
- cjm_transcript_verify/routes/__init__.py +0 -0
- cjm_transcript_verify/routes/core.py +96 -0
- cjm_transcript_verify/routes/init.py +47 -0
- cjm_transcript_verify/routes/sample.py +81 -0
- cjm_transcript_verify/routes/verify.py +73 -0
- cjm_transcript_verify/services/__init__.py +0 -0
- cjm_transcript_verify/services/verify.py +351 -0
- cjm_transcript_verify/utils.py +51 -0
- cjm_transcript_verify-0.0.1.dist-info/LICENSE +201 -0
- cjm_transcript_verify-0.0.1.dist-info/METADATA +773 -0
- cjm_transcript_verify-0.0.1.dist-info/RECORD +24 -0
- cjm_transcript_verify-0.0.1.dist-info/WHEEL +5 -0
- cjm_transcript_verify-0.0.1.dist-info/entry_points.txt +2 -0
- cjm_transcript_verify-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.1"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Autogenerated by nbdev
|
|
2
|
+
|
|
3
|
+
d = { 'settings': { 'branch': 'main',
|
|
4
|
+
'doc_baseurl': '/cjm-transcript-verify',
|
|
5
|
+
'doc_host': 'https://cj-mills.github.io',
|
|
6
|
+
'git_url': 'https://github.com/cj-mills/cjm-transcript-verify',
|
|
7
|
+
'lib_path': 'cjm_transcript_verify'},
|
|
8
|
+
'syms': { 'cjm_transcript_verify.components.helpers': { 'cjm_transcript_verify.components.helpers._get_document_id': ( 'components/helpers.html#_get_document_id',
|
|
9
|
+
'cjm_transcript_verify/components/helpers.py'),
|
|
10
|
+
'cjm_transcript_verify.components.helpers._get_document_id_any': ( 'components/helpers.html#_get_document_id_any',
|
|
11
|
+
'cjm_transcript_verify/components/helpers.py'),
|
|
12
|
+
'cjm_transcript_verify.components.helpers._get_document_id_from_review': ( 'components/helpers.html#_get_document_id_from_review',
|
|
13
|
+
'cjm_transcript_verify/components/helpers.py'),
|
|
14
|
+
'cjm_transcript_verify.components.helpers._get_media_path': ( 'components/helpers.html#_get_media_path',
|
|
15
|
+
'cjm_transcript_verify/components/helpers.py'),
|
|
16
|
+
'cjm_transcript_verify.components.helpers._get_verify_state': ( 'components/helpers.html#_get_verify_state',
|
|
17
|
+
'cjm_transcript_verify/components/helpers.py')},
|
|
18
|
+
'cjm_transcript_verify.components.integrity_checks': { 'cjm_transcript_verify.components.integrity_checks._render_check_row': ( 'components/integrity_checks.html#_render_check_row',
|
|
19
|
+
'cjm_transcript_verify/components/integrity_checks.py'),
|
|
20
|
+
'cjm_transcript_verify.components.integrity_checks.render_integrity_checks': ( 'components/integrity_checks.html#render_integrity_checks',
|
|
21
|
+
'cjm_transcript_verify/components/integrity_checks.py')},
|
|
22
|
+
'cjm_transcript_verify.components.sample_segments': { 'cjm_transcript_verify.components.sample_segments.render_jump_result': ( 'components/sample_segments.html#render_jump_result',
|
|
23
|
+
'cjm_transcript_verify/components/sample_segments.py'),
|
|
24
|
+
'cjm_transcript_verify.components.sample_segments.render_jump_to_index': ( 'components/sample_segments.html#render_jump_to_index',
|
|
25
|
+
'cjm_transcript_verify/components/sample_segments.py'),
|
|
26
|
+
'cjm_transcript_verify.components.sample_segments.render_sample_list': ( 'components/sample_segments.html#render_sample_list',
|
|
27
|
+
'cjm_transcript_verify/components/sample_segments.py'),
|
|
28
|
+
'cjm_transcript_verify.components.sample_segments.render_sample_row': ( 'components/sample_segments.html#render_sample_row',
|
|
29
|
+
'cjm_transcript_verify/components/sample_segments.py'),
|
|
30
|
+
'cjm_transcript_verify.components.sample_segments.render_sample_segments': ( 'components/sample_segments.html#render_sample_segments',
|
|
31
|
+
'cjm_transcript_verify/components/sample_segments.py')},
|
|
32
|
+
'cjm_transcript_verify.components.step_renderer': { 'cjm_transcript_verify.components.step_renderer.render_verify_error': ( 'components/step_renderer.html#render_verify_error',
|
|
33
|
+
'cjm_transcript_verify/components/step_renderer.py'),
|
|
34
|
+
'cjm_transcript_verify.components.step_renderer.render_verify_header': ( 'components/step_renderer.html#render_verify_header',
|
|
35
|
+
'cjm_transcript_verify/components/step_renderer.py'),
|
|
36
|
+
'cjm_transcript_verify.components.step_renderer.render_verify_loading': ( 'components/step_renderer.html#render_verify_loading',
|
|
37
|
+
'cjm_transcript_verify/components/step_renderer.py'),
|
|
38
|
+
'cjm_transcript_verify.components.step_renderer.render_verify_step': ( 'components/step_renderer.html#render_verify_step',
|
|
39
|
+
'cjm_transcript_verify/components/step_renderer.py')},
|
|
40
|
+
'cjm_transcript_verify.components.verification_summary': { 'cjm_transcript_verify.components.verification_summary._render_section_header': ( 'components/verification_summary.html#_render_section_header',
|
|
41
|
+
'cjm_transcript_verify/components/verification_summary.py'),
|
|
42
|
+
'cjm_transcript_verify.components.verification_summary._render_stat_row': ( 'components/verification_summary.html#_render_stat_row',
|
|
43
|
+
'cjm_transcript_verify/components/verification_summary.py'),
|
|
44
|
+
'cjm_transcript_verify.components.verification_summary.render_document_section': ( 'components/verification_summary.html#render_document_section',
|
|
45
|
+
'cjm_transcript_verify/components/verification_summary.py'),
|
|
46
|
+
'cjm_transcript_verify.components.verification_summary.render_segments_section': ( 'components/verification_summary.html#render_segments_section',
|
|
47
|
+
'cjm_transcript_verify/components/verification_summary.py'),
|
|
48
|
+
'cjm_transcript_verify.components.verification_summary.render_sources_section': ( 'components/verification_summary.html#render_sources_section',
|
|
49
|
+
'cjm_transcript_verify/components/verification_summary.py'),
|
|
50
|
+
'cjm_transcript_verify.components.verification_summary.render_verification_summary': ( 'components/verification_summary.html#render_verification_summary',
|
|
51
|
+
'cjm_transcript_verify/components/verification_summary.py')},
|
|
52
|
+
'cjm_transcript_verify.html_ids': { 'cjm_transcript_verify.html_ids.VerifyHtmlIds': ( 'html_ids.html#verifyhtmlids',
|
|
53
|
+
'cjm_transcript_verify/html_ids.py'),
|
|
54
|
+
'cjm_transcript_verify.html_ids.VerifyHtmlIds.as_selector': ( 'html_ids.html#verifyhtmlids.as_selector',
|
|
55
|
+
'cjm_transcript_verify/html_ids.py')},
|
|
56
|
+
'cjm_transcript_verify.models': { 'cjm_transcript_verify.models.SegmentSample': ( 'models.html#segmentsample',
|
|
57
|
+
'cjm_transcript_verify/models.py'),
|
|
58
|
+
'cjm_transcript_verify.models.SegmentSample.duration': ( 'models.html#segmentsample.duration',
|
|
59
|
+
'cjm_transcript_verify/models.py'),
|
|
60
|
+
'cjm_transcript_verify.models.VerificationResult': ( 'models.html#verificationresult',
|
|
61
|
+
'cjm_transcript_verify/models.py'),
|
|
62
|
+
'cjm_transcript_verify.models.VerificationResult.all_checks_passed': ( 'models.html#verificationresult.all_checks_passed',
|
|
63
|
+
'cjm_transcript_verify/models.py'),
|
|
64
|
+
'cjm_transcript_verify.models.VerifyStepState': ( 'models.html#verifystepstate',
|
|
65
|
+
'cjm_transcript_verify/models.py'),
|
|
66
|
+
'cjm_transcript_verify.models.VerifyUrls': ( 'models.html#verifyurls',
|
|
67
|
+
'cjm_transcript_verify/models.py')},
|
|
68
|
+
'cjm_transcript_verify.routes.core': { 'cjm_transcript_verify.routes.core.VerifyContext': ( 'routes/core.html#verifycontext',
|
|
69
|
+
'cjm_transcript_verify/routes/core.py'),
|
|
70
|
+
'cjm_transcript_verify.routes.core._get_review_state': ( 'routes/core.html#_get_review_state',
|
|
71
|
+
'cjm_transcript_verify/routes/core.py'),
|
|
72
|
+
'cjm_transcript_verify.routes.core._get_verify_state': ( 'routes/core.html#_get_verify_state',
|
|
73
|
+
'cjm_transcript_verify/routes/core.py'),
|
|
74
|
+
'cjm_transcript_verify.routes.core._load_verify_context': ( 'routes/core.html#_load_verify_context',
|
|
75
|
+
'cjm_transcript_verify/routes/core.py'),
|
|
76
|
+
'cjm_transcript_verify.routes.core._update_verify_state': ( 'routes/core.html#_update_verify_state',
|
|
77
|
+
'cjm_transcript_verify/routes/core.py')},
|
|
78
|
+
'cjm_transcript_verify.routes.init': { 'cjm_transcript_verify.routes.init.init_verify_routers': ( 'routes/init.html#init_verify_routers',
|
|
79
|
+
'cjm_transcript_verify/routes/init.py')},
|
|
80
|
+
'cjm_transcript_verify.routes.sample': { 'cjm_transcript_verify.routes.sample.init_sample_router': ( 'routes/sample.html#init_sample_router',
|
|
81
|
+
'cjm_transcript_verify/routes/sample.py')},
|
|
82
|
+
'cjm_transcript_verify.routes.verify': { 'cjm_transcript_verify.routes.verify.init_verify_router': ( 'routes/verify.html#init_verify_router',
|
|
83
|
+
'cjm_transcript_verify/routes/verify.py')},
|
|
84
|
+
'cjm_transcript_verify.services.verify': { 'cjm_transcript_verify.services.verify.VerifyService': ( 'services/verify.html#verifyservice',
|
|
85
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
86
|
+
'cjm_transcript_verify.services.verify.VerifyService.__init__': ( 'services/verify.html#verifyservice.__init__',
|
|
87
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
88
|
+
'cjm_transcript_verify.services.verify.VerifyService._compute_source_stats': ( 'services/verify.html#verifyservice._compute_source_stats',
|
|
89
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
90
|
+
'cjm_transcript_verify.services.verify.VerifyService._compute_timing_stats': ( 'services/verify.html#verifyservice._compute_timing_stats',
|
|
91
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
92
|
+
'cjm_transcript_verify.services.verify.VerifyService._count_edges_by_type': ( 'services/verify.html#verifyservice._count_edges_by_type',
|
|
93
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
94
|
+
'cjm_transcript_verify.services.verify.VerifyService._extract_samples': ( 'services/verify.html#verifyservice._extract_samples',
|
|
95
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
96
|
+
'cjm_transcript_verify.services.verify.VerifyService._find_document_node': ( 'services/verify.html#verifyservice._find_document_node',
|
|
97
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
98
|
+
'cjm_transcript_verify.services.verify.VerifyService._get_context': ( 'services/verify.html#verifyservice._get_context',
|
|
99
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
100
|
+
'cjm_transcript_verify.services.verify.VerifyService._get_context_async': ( 'services/verify.html#verifyservice._get_context_async',
|
|
101
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
102
|
+
'cjm_transcript_verify.services.verify.VerifyService._get_segment_nodes': ( 'services/verify.html#verifyservice._get_segment_nodes',
|
|
103
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
104
|
+
'cjm_transcript_verify.services.verify.VerifyService._node_to_sample': ( 'services/verify.html#verifyservice._node_to_sample',
|
|
105
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
106
|
+
'cjm_transcript_verify.services.verify.VerifyService.get_segment_by_index': ( 'services/verify.html#verifyservice.get_segment_by_index',
|
|
107
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
108
|
+
'cjm_transcript_verify.services.verify.VerifyService.get_segment_by_index_async': ( 'services/verify.html#verifyservice.get_segment_by_index_async',
|
|
109
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
110
|
+
'cjm_transcript_verify.services.verify.VerifyService.get_segment_count': ( 'services/verify.html#verifyservice.get_segment_count',
|
|
111
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
112
|
+
'cjm_transcript_verify.services.verify.VerifyService.get_segment_count_async': ( 'services/verify.html#verifyservice.get_segment_count_async',
|
|
113
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
114
|
+
'cjm_transcript_verify.services.verify.VerifyService.is_available': ( 'services/verify.html#verifyservice.is_available',
|
|
115
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
116
|
+
'cjm_transcript_verify.services.verify.VerifyService.verify_document': ( 'services/verify.html#verifyservice.verify_document',
|
|
117
|
+
'cjm_transcript_verify/services/verify.py'),
|
|
118
|
+
'cjm_transcript_verify.services.verify.VerifyService.verify_document_async': ( 'services/verify.html#verifyservice.verify_document_async',
|
|
119
|
+
'cjm_transcript_verify/services/verify.py')},
|
|
120
|
+
'cjm_transcript_verify.utils': { 'cjm_transcript_verify.utils.format_duration_mmss': ( 'utils.html#format_duration_mmss',
|
|
121
|
+
'cjm_transcript_verify/utils.py'),
|
|
122
|
+
'cjm_transcript_verify.utils.format_duration_seconds': ( 'utils.html#format_duration_seconds',
|
|
123
|
+
'cjm_transcript_verify/utils.py'),
|
|
124
|
+
'cjm_transcript_verify.utils.format_time_range': ( 'utils.html#format_time_range',
|
|
125
|
+
'cjm_transcript_verify/utils.py'),
|
|
126
|
+
'cjm_transcript_verify.utils.truncate_text': ( 'utils.html#truncate_text',
|
|
127
|
+
'cjm_transcript_verify/utils.py')}}}
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""State getters for the verify step from InteractionContext"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/helpers.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = []
|
|
7
|
+
|
|
8
|
+
# %% ../../nbs/components/helpers.ipynb #verify-helpers-exports
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from cjm_fasthtml_interactions.core.context import InteractionContext
|
|
12
|
+
|
|
13
|
+
from ..models import VerifyStepState
|
|
14
|
+
|
|
15
|
+
# %% ../../nbs/components/helpers.ipynb #verify-helpers-getters
|
|
16
|
+
def _get_verify_state(
|
|
17
|
+
ctx:InteractionContext # Interaction context with state
|
|
18
|
+
) -> VerifyStepState: # Typed verify step state
|
|
19
|
+
"""Get the full verify step state from context."""
|
|
20
|
+
step_states = ctx.state.get("step_states", {})
|
|
21
|
+
return step_states.get("verify", {})
|
|
22
|
+
|
|
23
|
+
def _get_document_id(
|
|
24
|
+
ctx:InteractionContext # Interaction context with state
|
|
25
|
+
) -> Optional[str]: # Document UUID or None if not set
|
|
26
|
+
"""Get the document ID to verify from context."""
|
|
27
|
+
state = _get_verify_state(ctx)
|
|
28
|
+
return state.get("document_id")
|
|
29
|
+
|
|
30
|
+
def _get_media_path(
|
|
31
|
+
ctx:InteractionContext # Interaction context with state
|
|
32
|
+
) -> Optional[str]: # Media path or None if not set
|
|
33
|
+
"""Get the media path for display context."""
|
|
34
|
+
state = _get_verify_state(ctx)
|
|
35
|
+
return state.get("media_path")
|
|
36
|
+
|
|
37
|
+
# %% ../../nbs/components/helpers.ipynb #verify-helpers-cross
|
|
38
|
+
def _get_document_id_from_review(
|
|
39
|
+
ctx:InteractionContext # Interaction context with state
|
|
40
|
+
) -> Optional[str]: # Document UUID or None if not found
|
|
41
|
+
"""Get the document ID from review step state (fallback)."""
|
|
42
|
+
step_states = ctx.state.get("step_states", {})
|
|
43
|
+
review_state = step_states.get("review", {})
|
|
44
|
+
return review_state.get("document_id")
|
|
45
|
+
|
|
46
|
+
def _get_document_id_any(
|
|
47
|
+
ctx:InteractionContext # Interaction context with state
|
|
48
|
+
) -> Optional[str]: # Document UUID or None if not found anywhere
|
|
49
|
+
"""Get document ID from verify state, falling back to review state."""
|
|
50
|
+
doc_id = _get_document_id(ctx)
|
|
51
|
+
if doc_id is None:
|
|
52
|
+
doc_id = _get_document_id_from_review(ctx)
|
|
53
|
+
return doc_id
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Integrity check display with pass/fail indicators"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/integrity_checks.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['render_integrity_checks']
|
|
7
|
+
|
|
8
|
+
# %% ../../nbs/components/integrity_checks.ipynb #verify-integrity-exports
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from fasthtml.common import Div, Span, H3
|
|
12
|
+
|
|
13
|
+
# DaisyUI components
|
|
14
|
+
from cjm_fasthtml_daisyui.components.data_display.card import card, card_body
|
|
15
|
+
from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui, text_dui
|
|
16
|
+
|
|
17
|
+
# Tailwind utilities
|
|
18
|
+
from cjm_fasthtml_tailwind.utilities.spacing import p, m
|
|
19
|
+
from cjm_fasthtml_tailwind.utilities.typography import font_size, font_weight
|
|
20
|
+
from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import (
|
|
21
|
+
flex_display, flex_direction, items, gap
|
|
22
|
+
)
|
|
23
|
+
from cjm_fasthtml_tailwind.core.base import combine_classes
|
|
24
|
+
|
|
25
|
+
# Lucide icons via factory
|
|
26
|
+
from cjm_fasthtml_lucide_icons.factory import lucide_icon
|
|
27
|
+
|
|
28
|
+
# Local imports
|
|
29
|
+
from ..html_ids import VerifyHtmlIds
|
|
30
|
+
from ..models import VerificationResult
|
|
31
|
+
|
|
32
|
+
# %% ../../nbs/components/integrity_checks.ipynb #verify-integrity-helpers
|
|
33
|
+
def _render_check_row(
|
|
34
|
+
passed:bool, # Whether the check passed
|
|
35
|
+
label:str, # Check description
|
|
36
|
+
detail:str="", # Optional detail (e.g., "246/246")
|
|
37
|
+
) -> Any: # Check row element
|
|
38
|
+
"""Render an integrity check row with pass/fail icon."""
|
|
39
|
+
if passed:
|
|
40
|
+
icon = lucide_icon("circle-check", size=5, cls=str(text_dui.success))
|
|
41
|
+
else:
|
|
42
|
+
icon = lucide_icon("triangle-alert", size=5, cls=str(text_dui.warning))
|
|
43
|
+
|
|
44
|
+
detail_span = Span(
|
|
45
|
+
detail,
|
|
46
|
+
cls=combine_classes(text_dui.base_content.opacity(60), font_size.sm)
|
|
47
|
+
) if detail else None
|
|
48
|
+
|
|
49
|
+
return Div(
|
|
50
|
+
icon,
|
|
51
|
+
Span(label, cls=str(font_size.sm)),
|
|
52
|
+
detail_span,
|
|
53
|
+
cls=combine_classes(flex_display, items.center, gap(2), p.y(1))
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# %% ../../nbs/components/integrity_checks.ipynb #verify-integrity-section
|
|
57
|
+
def render_integrity_checks(
|
|
58
|
+
result:VerificationResult, # Verification result with integrity data
|
|
59
|
+
) -> Any: # Integrity checks card
|
|
60
|
+
"""Render the structure integrity checks section."""
|
|
61
|
+
# Compute expected counts for display
|
|
62
|
+
expected_next = max(0, result.segment_count - 1)
|
|
63
|
+
|
|
64
|
+
header = Div(
|
|
65
|
+
lucide_icon("shield-check", size=5),
|
|
66
|
+
H3("Structure Integrity", cls=combine_classes(font_size.lg, font_weight.semibold)),
|
|
67
|
+
cls=combine_classes(flex_display, items.center, gap(2), m.b(3))
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
checks = Div(
|
|
71
|
+
# STARTS_WITH check
|
|
72
|
+
_render_check_row(
|
|
73
|
+
passed=result.has_starts_with and result.starts_with_count == 1,
|
|
74
|
+
label="STARTS_WITH edge",
|
|
75
|
+
detail=str(result.starts_with_count)
|
|
76
|
+
),
|
|
77
|
+
|
|
78
|
+
# NEXT chain check
|
|
79
|
+
_render_check_row(
|
|
80
|
+
passed=result.next_chain_complete,
|
|
81
|
+
label="NEXT chain",
|
|
82
|
+
detail=f"{result.next_count}/{expected_next}"
|
|
83
|
+
),
|
|
84
|
+
|
|
85
|
+
# PART_OF check
|
|
86
|
+
_render_check_row(
|
|
87
|
+
passed=result.part_of_complete,
|
|
88
|
+
label="PART_OF edges",
|
|
89
|
+
detail=f"{result.part_of_count}/{result.segment_count}"
|
|
90
|
+
),
|
|
91
|
+
|
|
92
|
+
# Timing check
|
|
93
|
+
_render_check_row(
|
|
94
|
+
passed=result.all_have_timing,
|
|
95
|
+
label="All segments have timing",
|
|
96
|
+
detail="" if result.all_have_timing else f"{result.segments_missing_timing} missing"
|
|
97
|
+
),
|
|
98
|
+
|
|
99
|
+
# Sources check
|
|
100
|
+
_render_check_row(
|
|
101
|
+
passed=result.all_have_sources,
|
|
102
|
+
label="All segments have sources",
|
|
103
|
+
detail="" if result.all_have_sources else f"{result.segments_missing_sources} missing"
|
|
104
|
+
),
|
|
105
|
+
|
|
106
|
+
cls=combine_classes(flex_display, flex_direction.col)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return Div(
|
|
110
|
+
Div(
|
|
111
|
+
header,
|
|
112
|
+
checks,
|
|
113
|
+
cls=str(card_body)
|
|
114
|
+
),
|
|
115
|
+
id=VerifyHtmlIds.INTEGRITY_SECTION,
|
|
116
|
+
cls=combine_classes(card, bg_dui.base_100, "shadow-sm")
|
|
117
|
+
)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Sample segment display with first/last segments and jump-to-index"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/sample_segments.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['render_sample_row', 'render_sample_list', 'render_jump_to_index', 'render_jump_result', 'render_sample_segments']
|
|
7
|
+
|
|
8
|
+
# %% ../../nbs/components/sample_segments.ipynb #verify-samples-exports
|
|
9
|
+
from typing import Any, List, Optional
|
|
10
|
+
|
|
11
|
+
from fasthtml.common import Div, Span, H3, Input, Button, Form
|
|
12
|
+
|
|
13
|
+
# DaisyUI components
|
|
14
|
+
from cjm_fasthtml_daisyui.components.data_display.card import card, card_body
|
|
15
|
+
from cjm_fasthtml_daisyui.components.data_input.text_input import text_input, text_input_sizes
|
|
16
|
+
from cjm_fasthtml_daisyui.components.actions.button import btn, btn_colors, btn_sizes
|
|
17
|
+
from cjm_fasthtml_daisyui.components.feedback.loading import loading, loading_styles, loading_sizes
|
|
18
|
+
from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui, text_dui
|
|
19
|
+
|
|
20
|
+
# Tailwind utilities
|
|
21
|
+
from cjm_fasthtml_tailwind.utilities.spacing import p, m
|
|
22
|
+
from cjm_fasthtml_tailwind.utilities.sizing import w
|
|
23
|
+
from cjm_fasthtml_tailwind.utilities.typography import font_size, font_weight, font_family
|
|
24
|
+
from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import (
|
|
25
|
+
flex_display, flex_direction, items, gap
|
|
26
|
+
)
|
|
27
|
+
from cjm_fasthtml_tailwind.core.base import combine_classes
|
|
28
|
+
|
|
29
|
+
# Lucide icons via factory
|
|
30
|
+
from cjm_fasthtml_lucide_icons.factory import lucide_icon
|
|
31
|
+
|
|
32
|
+
# Local imports
|
|
33
|
+
from ..html_ids import VerifyHtmlIds
|
|
34
|
+
from ..models import VerificationResult, SegmentSample, VerifyUrls
|
|
35
|
+
from ..utils import format_time_range
|
|
36
|
+
|
|
37
|
+
# %% ../../nbs/components/sample_segments.ipynb #verify-samples-row
|
|
38
|
+
def render_sample_row(
|
|
39
|
+
sample:SegmentSample, # Segment sample to display
|
|
40
|
+
) -> Any: # Sample row element
|
|
41
|
+
"""Render a single segment sample row."""
|
|
42
|
+
timing = format_time_range(sample.start_time, sample.end_time)
|
|
43
|
+
|
|
44
|
+
return Div(
|
|
45
|
+
# Index badge
|
|
46
|
+
Span(
|
|
47
|
+
f"[{sample.index}]",
|
|
48
|
+
cls=combine_classes(font_family.mono, font_size.sm, text_dui.primary)
|
|
49
|
+
),
|
|
50
|
+
# Text (truncated)
|
|
51
|
+
Span(
|
|
52
|
+
f'"{sample.text}"',
|
|
53
|
+
cls=combine_classes(font_size.sm, "truncate", "flex-1")
|
|
54
|
+
),
|
|
55
|
+
# Timing
|
|
56
|
+
Span(
|
|
57
|
+
f"({timing})",
|
|
58
|
+
cls=combine_classes(font_size.xs, text_dui.base_content.opacity(60), font_family.mono)
|
|
59
|
+
),
|
|
60
|
+
cls=combine_classes(flex_display, items.center, gap(2), p.y(1))
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# %% ../../nbs/components/sample_segments.ipynb #verify-samples-list
|
|
64
|
+
def render_sample_list(
|
|
65
|
+
samples:List[SegmentSample], # Samples to display
|
|
66
|
+
label:str, # Label for this list (e.g., "First", "Last")
|
|
67
|
+
container_id:str="", # Optional container ID
|
|
68
|
+
) -> Any: # Sample list element
|
|
69
|
+
"""Render a list of segment samples with label."""
|
|
70
|
+
if not samples:
|
|
71
|
+
return Div(
|
|
72
|
+
Span(f"{label}:", cls=combine_classes(font_weight.medium, font_size.sm)),
|
|
73
|
+
Span("No samples", cls=combine_classes(text_dui.base_content.opacity(60), font_size.sm)),
|
|
74
|
+
id=container_id if container_id else None,
|
|
75
|
+
cls=combine_classes(flex_display, flex_direction.col, gap(1))
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return Div(
|
|
79
|
+
Span(f"{label}:", cls=combine_classes(font_weight.medium, font_size.sm, m.b(1))),
|
|
80
|
+
*[render_sample_row(s) for s in samples],
|
|
81
|
+
id=container_id if container_id else None,
|
|
82
|
+
cls=combine_classes(flex_display, flex_direction.col)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# %% ../../nbs/components/sample_segments.ipynb #verify-samples-jump
|
|
86
|
+
def render_jump_to_index(
|
|
87
|
+
urls:VerifyUrls=None, # URL bundle for routes
|
|
88
|
+
max_index:int=0, # Maximum valid index for placeholder
|
|
89
|
+
) -> Any: # Jump-to-index form with loading indicator
|
|
90
|
+
"""Render the jump-to-index input form with loading state."""
|
|
91
|
+
urls = urls or VerifyUrls()
|
|
92
|
+
|
|
93
|
+
# Build form attributes
|
|
94
|
+
form_attrs = {}
|
|
95
|
+
if urls.sample:
|
|
96
|
+
form_attrs.update({
|
|
97
|
+
"hx_post": urls.sample,
|
|
98
|
+
"hx_target": f"#{VerifyHtmlIds.JUMP_RESULT}",
|
|
99
|
+
"hx_swap": "innerHTML",
|
|
100
|
+
"hx_indicator": f"#{VerifyHtmlIds.JUMP_LOADING}",
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
placeholder = f"0-{max_index}" if max_index > 0 else "index"
|
|
104
|
+
|
|
105
|
+
# Loading indicator (hidden by default, shown during HTMX request)
|
|
106
|
+
loading_indicator = Span(
|
|
107
|
+
cls=combine_classes(loading, loading_styles.spinner, loading_sizes.sm, "htmx-indicator"),
|
|
108
|
+
id=VerifyHtmlIds.JUMP_LOADING
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return Div(
|
|
112
|
+
Span("Jump to index:", cls=combine_classes(font_size.sm, font_weight.medium)),
|
|
113
|
+
Form(
|
|
114
|
+
Input(
|
|
115
|
+
type="number",
|
|
116
|
+
name="index",
|
|
117
|
+
min="0",
|
|
118
|
+
max=str(max_index) if max_index > 0 else None,
|
|
119
|
+
placeholder=placeholder,
|
|
120
|
+
id=VerifyHtmlIds.JUMP_INPUT,
|
|
121
|
+
cls=combine_classes(text_input, text_input_sizes.sm, w(20), font_family.mono)
|
|
122
|
+
),
|
|
123
|
+
Button(
|
|
124
|
+
lucide_icon("arrow-right", size=4),
|
|
125
|
+
type="submit",
|
|
126
|
+
id=VerifyHtmlIds.JUMP_BUTTON,
|
|
127
|
+
cls=combine_classes(btn, btn_colors.primary, btn_sizes.sm)
|
|
128
|
+
),
|
|
129
|
+
loading_indicator,
|
|
130
|
+
method="post",
|
|
131
|
+
cls=combine_classes(flex_display, items.center, gap(2)),
|
|
132
|
+
**form_attrs
|
|
133
|
+
),
|
|
134
|
+
cls=combine_classes(flex_display, items.center, gap(3), m.t(3), p.t(3), "border-t", "border-base-300")
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# %% ../../nbs/components/sample_segments.ipynb #verify-samples-result
|
|
138
|
+
def render_jump_result(
|
|
139
|
+
sample:Optional[SegmentSample]=None, # Fetched sample or None
|
|
140
|
+
error:str="", # Error message if any
|
|
141
|
+
) -> Any: # Jump result display
|
|
142
|
+
"""Render the jump-to-index result (single segment or error)."""
|
|
143
|
+
if error:
|
|
144
|
+
return Div(
|
|
145
|
+
lucide_icon("circle-alert", size=4, cls=str(text_dui.error)),
|
|
146
|
+
Span(error, cls=combine_classes(font_size.sm, text_dui.error)),
|
|
147
|
+
id=VerifyHtmlIds.JUMP_RESULT,
|
|
148
|
+
cls=combine_classes(flex_display, items.center, gap(2), m.t(2))
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if sample:
|
|
152
|
+
return Div(
|
|
153
|
+
render_sample_row(sample),
|
|
154
|
+
id=VerifyHtmlIds.JUMP_RESULT,
|
|
155
|
+
cls=str(m.t(2))
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Empty placeholder
|
|
159
|
+
return Div(id=VerifyHtmlIds.JUMP_RESULT)
|
|
160
|
+
|
|
161
|
+
# %% ../../nbs/components/sample_segments.ipynb #verify-samples-section
|
|
162
|
+
def render_sample_segments(
|
|
163
|
+
result:VerificationResult, # Verification result with samples
|
|
164
|
+
urls:VerifyUrls=None, # URL bundle for routes
|
|
165
|
+
) -> Any: # Sample segments card
|
|
166
|
+
"""Render the full sample segments section."""
|
|
167
|
+
urls = urls or VerifyUrls()
|
|
168
|
+
max_index = result.segment_count - 1 if result.segment_count > 0 else 0
|
|
169
|
+
|
|
170
|
+
header = Div(
|
|
171
|
+
lucide_icon("scan-search", size=5),
|
|
172
|
+
H3("Sample Segments", cls=combine_classes(font_size.lg, font_weight.semibold)),
|
|
173
|
+
cls=combine_classes(flex_display, items.center, gap(2), m.b(3))
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return Div(
|
|
177
|
+
Div(
|
|
178
|
+
header,
|
|
179
|
+
|
|
180
|
+
# First samples
|
|
181
|
+
render_sample_list(
|
|
182
|
+
result.first_segments,
|
|
183
|
+
"First",
|
|
184
|
+
VerifyHtmlIds.FIRST_SAMPLES
|
|
185
|
+
),
|
|
186
|
+
|
|
187
|
+
# Spacer
|
|
188
|
+
Div(cls=str(m.y(3))),
|
|
189
|
+
|
|
190
|
+
# Last samples
|
|
191
|
+
render_sample_list(
|
|
192
|
+
result.last_segments,
|
|
193
|
+
"Last",
|
|
194
|
+
VerifyHtmlIds.LAST_SAMPLES
|
|
195
|
+
),
|
|
196
|
+
|
|
197
|
+
# Jump-to-index
|
|
198
|
+
render_jump_to_index(urls, max_index),
|
|
199
|
+
|
|
200
|
+
# Result container (empty initially)
|
|
201
|
+
render_jump_result(),
|
|
202
|
+
|
|
203
|
+
cls=str(card_body)
|
|
204
|
+
),
|
|
205
|
+
id=VerifyHtmlIds.SAMPLES_SECTION,
|
|
206
|
+
cls=combine_classes(card, bg_dui.base_100, "shadow-sm")
|
|
207
|
+
)
|