Coverage for sources/librovore/results.py: 28%
270 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-02 00:02 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-02 00:02 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
4#============================================================================#
5# #
6# Licensed under the Apache License, Version 2.0 (the "License"); #
7# you may not use this file except in compliance with the License. #
8# You may obtain a copy of the License at #
9# #
10# http://www.apache.org/licenses/LICENSE-2.0 #
11# #
12# Unless required by applicable law or agreed to in writing, software #
13# distributed under the License is distributed on an "AS IS" BASIS, #
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15# See the License for the specific language governing permissions and #
16# limitations under the License. #
17# #
18#============================================================================#
21''' Results structures.
23 Search results, inventory objects, content documents, etc....
24'''
27from . import __
30_CONTENT_PREVIEW_LIMIT = 100
33class InventoryObject( __.immut.DataclassObject ):
34 ''' Universal inventory object with complete source attribution.
36 Represents a single documentation object from any inventory source
37 with standardized fields and format-specific metadata container.
38 '''
40 name: __.typx.Annotated[
41 str,
42 __.ddoc.Doc( "Primary object identifier from inventory source." ),
43 ]
44 uri: __.typx.Annotated[
45 str,
46 __.ddoc.Doc( "Relative URI to object documentation content." ),
47 ]
48 inventory_type: __.typx.Annotated[
49 str,
50 __.ddoc.Doc(
51 "Inventory format identifier (e.g., sphinx_objects_inv)." ),
52 ]
53 location_url: __.typx.Annotated[
54 str, __.ddoc.Doc(
55 "Complete URL to inventory location for attribution." )
56 ]
57 display_name: __.typx.Annotated[
58 __.typx.Optional[ str ],
59 __.ddoc.Doc( "Human-readable name if different from name." ),
60 ] = None
61 specifics: __.typx.Annotated[
62 __.immut.Dictionary[ str, __.typx.Any ],
63 __.ddoc.Doc(
64 "Format-specific metadata (domain, role, priority, etc.)." ),
65 ] = __.dcls.field( default_factory = lambda: __.immut.Dictionary( ) )
68 @property
69 def effective_display_name( self ) -> str:
70 ''' Effective display name. Might be same as name. '''
71 if self.display_name is not None:
72 return self.display_name
73 return self.name
75 @__.abc.abstractmethod
76 def render_specifics_json(
77 self
78 ) -> __.immut.Dictionary[ str, __.typx.Any ]:
79 ''' Renders specifics for JSON output. '''
80 raise NotImplementedError
82 @__.abc.abstractmethod
83 def render_specifics_markdown(
84 self, /, *,
85 reveal_internals: __.typx.Annotated[
86 bool,
87 __.ddoc.Doc( '''
88 Controls whether implementation-specific details (internal
89 field names, version numbers, priority scores) are included.
90 When False, only user-facing information is shown.
91 ''' ),
92 ] = True,
93 ) -> tuple[ str, ... ]:
94 ''' Renders specifics as Markdown lines for CLI display. '''
95 raise NotImplementedError
97 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
98 ''' Renders complete object as JSON-compatible dictionary. '''
99 base = __.immut.Dictionary[
100 str, __.typx.Any
101 ](
102 name = self.name,
103 uri = self.uri,
104 inventory_type = self.inventory_type,
105 location_url = self.location_url,
106 display_name = self.display_name,
107 effective_display_name = self.effective_display_name,
108 )
109 formatted_specifics = self.render_specifics_json( )
110 result_dict = dict( base )
111 result_dict.update( dict( formatted_specifics ) )
112 return __.immut.Dictionary[ str, __.typx.Any ]( result_dict )
114 def render_as_markdown(
115 self, /, *,
116 reveal_internals: __.typx.Annotated[
117 bool,
118 __.ddoc.Doc( "Controls whether internal details are shown." ),
119 ] = True,
120 ) -> tuple[ str, ... ]:
121 ''' Renders complete object as Markdown lines for display. '''
122 lines = [ f"### `{self.effective_display_name}`" ]
123 if reveal_internals:
124 lines.append( f"**URI:** {self.uri}" )
125 lines.append( f"**Type:** {self.inventory_type}" )
126 lines.append( f"**Location:** {self.location_url}" )
127 specifics_lines = self.render_specifics_markdown(
128 reveal_internals = reveal_internals )
129 lines.extend( specifics_lines )
130 return tuple( lines )
133class ContentDocument( __.immut.DataclassObject ):
134 ''' Documentation content with extracted metadata and snippets. '''
136 inventory_object: __.typx.Annotated[
137 InventoryObject,
138 __.ddoc.Doc( "Location inventory object for this content." ),
139 ]
140 description: __.typx.Annotated[
141 str,
142 __.ddoc.Doc( "Extracted object description or summary." ),
143 ] = ''
144 documentation_url: __.typx.Annotated[
145 str,
146 __.ddoc.Doc( "Complete URL to full documentation page." ),
147 ] = ''
148 extraction_metadata: __.typx.Annotated[
149 __.immut.Dictionary[ str, __.typx.Any ],
150 __.ddoc.Doc( "Metadata from structure processor extraction." ),
151 ] = __.dcls.field( default_factory = lambda: __.immut.Dictionary( ) )
153 @property
154 def has_meaningful_content( self ) -> bool:
155 ''' Returns True if document contains useful extracted content. '''
156 return bool( self.description )
158 def render_as_json(
159 self, /, *,
160 lines_max: __.typx.Optional[ int ] = None,
161 ) -> __.immut.Dictionary[ str, __.typx.Any ]:
162 ''' Renders complete document as JSON-compatible dictionary. '''
163 description = self.description
164 if lines_max is not None:
165 desc_lines = description.split( '\n' )
166 if len( desc_lines ) > lines_max:
167 desc_lines = desc_lines[ :lines_max ]
168 desc_lines.append( "..." )
169 description = '\n'.join( desc_lines )
170 return __.immut.Dictionary[
171 str, __.typx.Any
172 ](
173 inventory_object = dict( self.inventory_object.render_as_json( ) ),
174 description = description,
175 documentation_url = self.documentation_url,
176 extraction_metadata = dict( self.extraction_metadata ),
177 has_meaningful_content = self.has_meaningful_content,
178 )
180 def render_as_markdown(
181 self, /, *,
182 reveal_internals: __.typx.Annotated[
183 bool,
184 __.ddoc.Doc( "Controls whether internal details are shown." ),
185 ] = True,
186 ) -> tuple[ str, ... ]:
187 ''' Renders complete document as Markdown lines for display. '''
188 lines = [ f"### `{self.inventory_object.effective_display_name}`" ]
189 if self.description:
190 lines.append( f"**Description:** {self.description}" )
191 if self.documentation_url:
192 lines.append( f"**URL:** {self.documentation_url}" )
193 if reveal_internals:
194 inventory_lines = self.inventory_object.render_specifics_markdown(
195 reveal_internals = True )
196 lines.extend( inventory_lines )
197 return tuple( lines )
200class InventoryLocationInfo( __.immut.DataclassObject ):
201 ''' Information about detected inventory location and processor. '''
203 inventory_type: __.typx.Annotated[
204 str,
205 __.ddoc.Doc( "Inventory format type identifier." ),
206 ]
207 location_url: __.typx.Annotated[
208 str,
209 __.ddoc.Doc( "Complete URL to inventory location." ),
210 ]
211 processor_name: __.typx.Annotated[
212 str,
213 __.ddoc.Doc( "Name of processor handling this location." ),
214 ]
215 confidence: __.typx.Annotated[
216 float,
217 __.ddoc.Doc( "Detection confidence score (0.0-1.0)." ),
218 ]
219 object_count: __.typx.Annotated[
220 int,
221 __.ddoc.Doc( "Total objects available in this inventory." ),
222 ]
224 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
225 ''' Renders location info as JSON-compatible dictionary. '''
226 return __.immut.Dictionary(
227 inventory_type = self.inventory_type,
228 location_url = self.location_url,
229 processor_name = self.processor_name,
230 confidence = self.confidence,
231 object_count = self.object_count,
232 )
235class SearchMetadata( __.immut.DataclassObject ):
236 ''' Search operation metadata and performance statistics. '''
238 results_count: __.typx.Annotated[
239 int,
240 __.ddoc.Doc( "Number of results returned to user." ),
241 ]
242 results_max: __.typx.Annotated[
243 int,
244 __.ddoc.Doc( "Maximum results requested by user." ),
245 ]
246 matches_total: __.typx.Annotated[
247 __.typx.Optional[ int ],
248 __.ddoc.Doc( "Total matching objects before limit applied." ),
249 ] = None
250 search_time_ms: __.typx.Annotated[
251 __.typx.Optional[ int ],
252 __.ddoc.Doc( "Search execution time in milliseconds." ),
253 ] = None
255 @property
256 def results_truncated( self ) -> bool:
257 ''' Returns True if results were limited by results_max. '''
258 if self.matches_total is None:
259 return False
260 return self.results_count < self.matches_total
262 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
263 ''' Renders search metadata as JSON-compatible dictionary. '''
264 return __.immut.Dictionary(
265 results_count = self.results_count,
266 results_max = self.results_max,
267 matches_total = self.matches_total,
268 search_time_ms = self.search_time_ms,
269 results_truncated = self.results_truncated,
270 )
273class SearchResult( __.immut.DataclassObject ):
274 ''' Search result with inventory object and match metadata. '''
276 inventory_object: __.typx.Annotated[
277 InventoryObject,
278 __.ddoc.Doc( "Matched inventory object with metadata." ),
279 ]
280 score: __.typx.Annotated[
281 float,
282 __.ddoc.Doc( "Search relevance score (0.0-1.0)." ),
283 ]
284 match_reasons: __.typx.Annotated[
285 tuple[ str, ... ],
286 __.ddoc.Doc( "Detailed reasons for search match." ),
287 ]
289 @classmethod
290 def from_inventory_object(
291 cls,
292 inventory_object: InventoryObject, *,
293 score: float,
294 match_reasons: __.cabc.Sequence[ str ],
295 ) -> __.typx.Self:
296 ''' Produces search result from inventory object with scoring. '''
297 return cls(
298 inventory_object = inventory_object,
299 score = score,
300 match_reasons = tuple( match_reasons ) )
302 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
303 ''' Renders search result as JSON-compatible dictionary. '''
304 return __.immut.Dictionary[
305 str, __.typx.Any
306 ](
307 inventory_object = dict( self.inventory_object.render_as_json( ) ),
308 score = self.score,
309 match_reasons = list( self.match_reasons ),
310 )
312 def render_as_markdown(
313 self, /, *,
314 reveal_internals: __.typx.Annotated[
315 bool,
316 __.ddoc.Doc( "Controls whether internal details are shown." ),
317 ] = True,
318 ) -> tuple[ str, ... ]:
319 ''' Renders search result as Markdown lines for display. '''
320 title = "### `{name}` (Score: {score:.2f})".format(
321 name = self.inventory_object.effective_display_name,
322 score = self.score )
323 lines = [ title ]
324 if reveal_internals and self.match_reasons:
325 reasons = ', '.join( self.match_reasons )
326 lines.append( "**Match reasons:** {reasons}".format(
327 reasons = reasons ) )
328 inventory_lines = self.inventory_object.render_as_markdown(
329 reveal_internals = reveal_internals )
330 lines.extend( inventory_lines[ 1: ] ) # Skip duplicate title line
331 return tuple( lines )
334class ContentQueryResult( __.immut.DataclassObject ):
335 ''' Complete result structure for content queries. '''
337 location: __.typx.Annotated[
338 str,
339 __.ddoc.Doc( "Primary location URL for this query." ),
340 ]
341 query: __.typx.Annotated[
342 str,
343 __.ddoc.Doc( "Search term or query string used." ),
344 ]
345 documents: __.typx.Annotated[
346 tuple[ ContentDocument, ... ],
347 __.ddoc.Doc( "Documentation content for matching objects." ) ]
348 search_metadata: __.typx.Annotated[
349 SearchMetadata,
350 __.ddoc.Doc( "Search execution and result metadata." ),
351 ]
352 inventory_locations: __.typx.Annotated[
353 tuple[ InventoryLocationInfo, ... ],
354 __.ddoc.Doc( "Information about inventory locations used." ),
355 ]
357 def render_as_json(
358 self, /, *,
359 lines_max: __.typx.Optional[ int ] = None,
360 ) -> __.immut.Dictionary[ str, __.typx.Any ]:
361 ''' Renders content query result as JSON-compatible dictionary. '''
362 documents_json = [
363 dict( doc.render_as_json( lines_max = lines_max ) )
364 for doc in self.documents ]
365 locations_json = [
366 dict( loc.render_as_json( ) ) for loc in self.inventory_locations ]
367 return __.immut.Dictionary[
368 str, __.typx.Any
369 ](
370 location = self.location,
371 query = self.query,
372 documents = documents_json,
373 search_metadata = dict( self.search_metadata.render_as_json( ) ),
374 inventory_locations = locations_json,
375 )
377 def render_as_markdown(
378 self, /, *,
379 reveal_internals: __.typx.Annotated[
380 bool,
381 __.ddoc.Doc( "Controls whether internal details are shown." ),
382 ] = True,
383 lines_max: __.typx.Annotated[
384 __.typx.Optional[ int ],
385 __.ddoc.Doc( "Maximum lines to display per content result." ),
386 ] = None,
387 ) -> tuple[ str, ... ]:
388 ''' Renders content query result as Markdown lines for display. '''
389 title = "# Content Query Results"
390 if lines_max is not None:
391 title += " (truncated)"
392 lines = [ title ]
393 lines.append( "**Query:** {query}".format( query = self.query ) )
394 if reveal_internals:
395 lines.append( "**Location:** {location}".format(
396 location = self.location ) )
397 lines.append( "**Results:** {count} of {max}".format(
398 count = self.search_metadata.results_count,
399 max = self.search_metadata.results_max ) )
400 if self.documents:
401 lines.append( "" )
402 lines.append( "## Documents" )
403 for index, doc in enumerate( self.documents, 1 ):
404 separator = "\n📄 ── Document {} ──────────────────── 📄\n"
405 lines.append( separator.format( index ) )
406 lines.append( "**URL:** {url}".format(
407 url = doc.documentation_url ) )
408 if doc.description:
409 description = doc.description
410 if lines_max is not None:
411 desc_lines = description.split( '\n' )
412 if len( desc_lines ) > lines_max:
413 desc_lines = desc_lines[ :lines_max ]
414 desc_lines.append( "..." )
415 description = '\n'.join( desc_lines )
416 lines.append( "**Description:** {description}".format(
417 description = description ) )
418 return tuple( lines )
421class InventoryQueryResult( __.immut.DataclassObject ):
422 ''' Complete result structure for inventory queries. '''
424 location: __.typx.Annotated[
425 str,
426 __.ddoc.Doc( "Primary location URL for this query." ),
427 ]
428 query: __.typx.Annotated[
429 str,
430 __.ddoc.Doc( "Search term or query string used." ),
431 ]
432 objects: __.typx.Annotated[
433 tuple[ InventoryObject, ... ],
434 __.ddoc.Doc( "Inventory objects matching search criteria." ),
435 ]
436 search_metadata: __.typx.Annotated[
437 SearchMetadata,
438 __.ddoc.Doc( "Search execution and result metadata." ),
439 ]
440 inventory_locations: __.typx.Annotated[
441 tuple[ InventoryLocationInfo, ... ],
442 __.ddoc.Doc( "Information about inventory locations used." ),
443 ]
445 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
446 ''' Renders inventory query result as JSON-compatible dictionary. '''
447 objects_json = [
448 dict( obj.render_as_json( ) ) for obj in self.objects ]
449 locations_json = [
450 dict( loc.render_as_json( ) ) for loc in self.inventory_locations ]
451 return __.immut.Dictionary[
452 str, __.typx.Any
453 ](
454 location = self.location,
455 query = self.query,
456 objects = objects_json,
457 search_metadata = dict( self.search_metadata.render_as_json( ) ),
458 inventory_locations = locations_json,
459 )
461 def render_as_markdown(
462 self, /, *,
463 reveal_internals: __.typx.Annotated[
464 bool,
465 __.ddoc.Doc( "Controls whether internal details are shown." ),
466 ] = True,
467 ) -> tuple[ str, ... ]:
468 ''' Renders inventory query result as Markdown lines for display. '''
469 lines = [ "# Inventory Query Results" ]
470 lines.append( "**Query:** {query}".format( query = self.query ) )
471 if reveal_internals:
472 lines.append( "**Location:** {location}".format(
473 location = self.location ) )
474 lines.append( "**Results:** {count} of {max}".format(
475 count = self.search_metadata.results_count,
476 max = self.search_metadata.results_max ) )
477 if self.objects:
478 lines.append( "" )
479 lines.append( "## Objects" )
480 for index, obj in enumerate( self.objects, 1 ):
481 separator = "\n📦 ── Object {} ─────────────────────── 📦\n"
482 lines.append( separator.format( index ) )
483 obj_lines = obj.render_as_markdown(
484 reveal_internals = reveal_internals )
485 lines.extend( obj_lines )
486 return tuple( lines )
489class Detection( __.immut.DataclassObject ):
490 ''' Processor detection information with confidence scoring. '''
492 processor_name: __.typx.Annotated[
493 str,
494 __.ddoc.Doc( "Name of the processor that can handle this location." ),
495 ]
496 confidence: __.typx.Annotated[
497 float,
498 __.ddoc.Doc( "Detection confidence score (0.0-1.0)." ),
499 ]
500 processor_type: __.typx.Annotated[
501 str,
502 __.ddoc.Doc( "Type of processor (inventory, structure)." ),
503 ]
504 detection_metadata: __.typx.Annotated[
505 __.immut.Dictionary[ str, __.typx.Any ],
506 __.ddoc.Doc( "Processor-specific detection metadata." ),
507 ] = __.dcls.field( default_factory = lambda: __.immut.Dictionary( ) )
509 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
510 ''' Renders detection as JSON-compatible dictionary. '''
511 return __.immut.Dictionary[
512 str, __.typx.Any
513 ](
514 processor_name = self.processor_name,
515 confidence = self.confidence,
516 processor_type = self.processor_type,
517 detection_metadata = dict( self.detection_metadata ),
518 )
521class DetectionsResult( __.immut.DataclassObject ):
522 ''' Detection results with processor selection and timing metadata. '''
524 source: __.typx.Annotated[
525 str,
526 __.ddoc.Doc( "Primary location URL for detection operation." ),
527 ]
528 detections: __.typx.Annotated[
529 tuple[ Detection, ... ],
530 __.ddoc.Doc( "All processor detections found for location." ),
531 ]
532 detection_optimal: __.typx.Annotated[
533 __.typx.Optional[ Detection ],
534 __.ddoc.Doc( "Best detection result based on confidence scoring." ),
535 ]
536 time_detection_ms: __.typx.Annotated[
537 int,
538 __.ddoc.Doc( "Detection operation time in milliseconds." ),
539 ]
542 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
543 ''' Renders detection results as JSON-compatible dictionary. '''
544 detections_json = [
545 dict( detection.render_as_json( ) )
546 for detection in self.detections ]
547 return __.immut.Dictionary[
548 str, __.typx.Any
549 ](
550 source = self.source,
551 detections = detections_json,
552 detection_optimal = (
553 dict( self.detection_optimal.render_as_json( ) )
554 if self.detection_optimal else None ),
555 time_detection_ms = self.time_detection_ms,
556 )
558 def render_as_markdown(
559 self, /, *,
560 reveal_internals: __.typx.Annotated[
561 bool,
562 __.ddoc.Doc( "Controls whether internal details are shown." ),
563 ] = True,
564 ) -> tuple[ str, ... ]:
565 ''' Renders detection results as Markdown lines for display. '''
566 lines = [ "# Detection Results" ]
567 if reveal_internals:
568 lines.append( "**Source:** {source}".format(
569 source = self.source ) )
570 lines.append( "**Detection time:** {time}ms".format(
571 time = self.time_detection_ms ) )
572 if self.detection_optimal:
573 lines.append( "**Optimal processor:** {name} ({type})".format(
574 name = self.detection_optimal.processor_name,
575 type = self.detection_optimal.processor_type ) )
576 lines.append( "**Confidence:** {conf:.2f}".format(
577 conf = self.detection_optimal.confidence ) )
578 else:
579 lines.append( "**No optimal processor found**" )
580 if reveal_internals and self.detections:
581 lines.append( "" )
582 lines.append( "## All Detections" )
583 detection_lines = [
584 "- **{name}** ({type}): {conf:.2f}".format(
585 name = detection.processor_name,
586 type = detection.processor_type,
587 conf = detection.confidence )
588 for detection in self.detections ]
589 lines.extend( detection_lines )
590 return tuple( lines )
593class ProcessorInfo( __.immut.DataclassObject ):
594 ''' Information about a processor and its capabilities. '''
596 processor_name: __.typx.Annotated[
597 str,
598 __.ddoc.Doc( "Name of the processor for identification." ),
599 ]
600 processor_type: __.typx.Annotated[
601 str,
602 __.ddoc.Doc( "Type of processor (inventory, structure)." ),
603 ]
604 capabilities: __.typx.Annotated[
605 __.typx.Any, # Will be _interfaces.ProcessorCapabilities after import
606 __.ddoc.Doc( "Complete capability description for processor." ),
607 ]
609 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
610 ''' Renders processor info as JSON-compatible dictionary. '''
611 return __.immut.Dictionary[
612 str, __.typx.Any
613 ](
614 processor_name = self.processor_name,
615 processor_type = self.processor_type,
616 capabilities = serialize_for_json( self.capabilities ),
617 )
619 def render_as_markdown(
620 self, /, *,
621 reveal_internals: __.typx.Annotated[
622 bool,
623 __.ddoc.Doc( "Controls whether internal details are shown." ),
624 ] = True,
625 ) -> tuple[ str, ... ]:
626 ''' Renders processor info as Markdown lines for display. '''
627 lines = [ f"### `{self.processor_name}` ({self.processor_type})" ]
628 if reveal_internals:
629 lines.append( f"**Version:** {self.capabilities.version}" )
630 if self.capabilities.results_limit_max:
631 lines.append(
632 f"**Max results:** {self.capabilities.results_limit_max}" )
633 if self.capabilities.response_time_typical:
634 lines.append(
635 f"**Response time:** "
636 f"{self.capabilities.response_time_typical}" )
637 if self.capabilities.notes:
638 lines.append( f"**Notes:** {self.capabilities.notes}" )
639 if self.capabilities.supported_filters:
640 lines.append( "" )
641 lines.append( "**Supported filters:**" )
642 for filter_cap in self.capabilities.supported_filters:
643 filter_line = f"- `{filter_cap.name}` ({filter_cap.type})"
644 if filter_cap.required:
645 filter_line += " *required*"
646 lines.append( filter_line )
647 if filter_cap.description:
648 lines.append( f" {filter_cap.description}" )
649 if filter_cap.values:
650 values_str = ', '.join( filter_cap.values )
651 lines.append( f" Values: {values_str}" )
652 return tuple( lines )
655class ProcessorsSurveyResult( __.immut.DataclassObject ):
656 ''' Survey results listing available processors and capabilities. '''
658 genus: __.typx.Annotated[
659 __.typx.Any, # Will be _interfaces.ProcessorGenera after import
660 __.ddoc.Doc(
661 "Processor genus that was surveyed (inventory or structure)." ),
662 ]
663 filter_name: __.typx.Annotated[
664 __.typx.Optional[ str ],
665 __.ddoc.Doc( "Optional processor name filter applied to survey." ),
666 ] = None
667 processors: __.typx.Annotated[
668 tuple[ ProcessorInfo, ... ],
669 __.ddoc.Doc( "Available processors matching survey criteria." ),
670 ]
671 survey_time_ms: __.typx.Annotated[
672 int,
673 __.ddoc.Doc( "Survey operation time in milliseconds." ),
674 ]
676 def render_as_json( self ) -> __.immut.Dictionary[ str, __.typx.Any ]:
677 ''' Renders survey results as JSON-compatible dictionary. '''
678 processors_json = [
679 dict( processor.render_as_json( ) )
680 for processor in self.processors ]
681 return __.immut.Dictionary[
682 str, __.typx.Any
683 ](
684 genus = (
685 self.genus.value if hasattr( self.genus, 'value' )
686 else str( self.genus ) ),
687 filter_name = self.filter_name,
688 processors = processors_json,
689 survey_time_ms = self.survey_time_ms,
690 )
692 def render_as_markdown(
693 self, /, *,
694 reveal_internals: __.typx.Annotated[
695 bool,
696 __.ddoc.Doc( "Controls whether internal details are shown." ),
697 ] = True,
698 ) -> tuple[ str, ... ]:
699 ''' Renders survey results as Markdown lines for display. '''
700 genus_name = (
701 self.genus.value if hasattr( self.genus, 'value' )
702 else str( self.genus ) )
703 title = f"# Processor Survey Results ({genus_name})"
704 lines = [ title ]
705 if reveal_internals:
706 lines.append( f"**Survey time:** {self.survey_time_ms}ms" )
707 if self.filter_name:
708 lines.append( f"**Filter:** {self.filter_name}" )
709 lines.append( f"**Processors found:** {len( self.processors )}" )
710 if self.processors:
711 lines.append( "" )
712 for i, processor in enumerate( self.processors, 1 ):
713 lines.append( f"📦 ── Processor {i} ──────────" )
714 processor_lines = processor.render_as_markdown(
715 reveal_internals = reveal_internals )
716 lines.extend( processor_lines )
717 if i < len( self.processors ):
718 lines.append( "" )
719 return tuple( lines )
722def serialize_for_json( objct: __.typx.Any ) -> __.typx.Any:
723 ''' Legacy serialization for non-structured objects (dicts). '''
724 if __.dcls.is_dataclass( objct ):
725 return _serialize_dataclass_for_json( objct )
726 if isinstance( objct, ( list, tuple, set, frozenset ) ):
727 return _serialize_sequence_for_json( objct )
728 if isinstance( objct, ( dict, __.immut.Dictionary ) ):
729 return _serialize_mapping_for_json( objct )
730 return objct
734def _serialize_dataclass_for_json(
735 obj: __.typx.Any,
736) -> dict[ str, __.typx.Any ]:
737 ''' Serializes dataclass objects to JSON-compatible dictionaries. '''
738 result: dict[ str, __.typx.Any ] = { }
739 for field in __.dcls.fields( obj ):
740 if field.name.startswith( '_' ):
741 continue
742 value = getattr( obj, field.name )
743 result[ field.name ] = serialize_for_json( value )
744 return result
747def _serialize_mapping_for_json(
748 obj: __.typx.Any
749) -> dict[ __.typx.Any, __.typx.Any ]:
750 ''' Serializes mapping-like objects to JSON-compatible dictionaries. '''
751 return {
752 key: serialize_for_json( value )
753 for key, value in obj.items( )
754 }
757def _serialize_sequence_for_json( obj: __.typx.Any ) -> list[ __.typx.Any ]:
758 ''' Serializes sequence-like objects to JSON-compatible lists. '''
759 return [ serialize_for_json( item ) for item in obj ]
762ContentDocuments: __.typx.TypeAlias = __.cabc.Sequence[ ContentDocument ]
763InventoryObjects: __.typx.TypeAlias = __.cabc.Sequence[ InventoryObject ]
764SearchResults: __.typx.TypeAlias = __.cabc.Sequence[ SearchResult ]