#!/usr/bin/env python3 """ Demonstration of the Recursive Position System This example shows how to use the hierarchical position tracking system that can reference any type of content (words, images, table cells, etc.) in a nested document structure. Key Features Demonstrated: - Hierarchical position tracking - Dynamic content type support - JSON and shelf serialization - Position relationships (ancestor/descendant) - Bookmark management - Real-world ereader scenarios """ import sys import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from pyWebLayout.layout.recursive_position import ( ContentType, LocationNode, RecursivePosition, PositionBuilder, PositionStorage, create_word_position, create_image_position, create_table_cell_position, create_list_item_position ) def demonstrate_basic_position_creation(): """Show basic position creation and manipulation""" print("=== Basic Position Creation ===") # Create a position using the builder pattern position = (PositionBuilder() .chapter(2) .block(5) .paragraph() .word(12, offset=3) .with_rendering_metadata(font_scale=1.5, page_size=[800, 600]) .build()) print(f"Position path: {position}") print(f"Depth: {position.get_depth()}") print(f"Leaf node: {position.get_leaf_node()}") # Query specific nodes chapter_node = position.get_node(ContentType.CHAPTER) word_node = position.get_node(ContentType.WORD) print(f"Chapter: {chapter_node.index}") print(f"Word: {word_node.index}, offset: {word_node.offset}") print(f"Font scale: {position.rendering_metadata.get('font_scale')}") print() def demonstrate_different_content_types(): """Show positions for different content types""" print("=== Different Content Types ===") # Word position word_pos = create_word_position(1, 3, 15, 2) print(f"Word position: {word_pos}") # Image position image_pos = create_image_position(2, 1, 0) print(f"Image position: {image_pos}") # Table cell position table_pos = create_table_cell_position(0, 4, 2, 1, 5) print(f"Table cell position: {table_pos}") # List item position list_pos = create_list_item_position(1, 2, 3, 0) print(f"List item position: {list_pos}") # Complex nested structure complex_pos = (PositionBuilder() .chapter(3) .block(7) .table(0, table_type="data", columns=4) .table_row(2, row_type="header") .table_cell(1, cell_type="data", colspan=2) .link(0, url="https://example.com", text="Click here") .build()) print(f"Complex nested position: {complex_pos}") print() def demonstrate_position_relationships(): """Show ancestor/descendant relationships""" print("=== Position Relationships ===") # Create related positions chapter_pos = (PositionBuilder() .chapter(1) .block(2) .build()) paragraph_pos = (PositionBuilder() .chapter(1) .block(2) .paragraph() .build()) word_pos = (PositionBuilder() .chapter(1) .block(2) .paragraph() .word(5) .build()) # Test relationships print(f"Chapter position: {chapter_pos}") print(f"Paragraph position: {paragraph_pos}") print(f"Word position: {word_pos}") print(f"Chapter is ancestor of paragraph: {chapter_pos.is_ancestor_of(paragraph_pos)}") print(f"Chapter is ancestor of word: {chapter_pos.is_ancestor_of(word_pos)}") print(f"Word is descendant of chapter: {word_pos.is_descendant_of(chapter_pos)}") # Find common ancestors unrelated_pos = create_word_position(2, 1, 0) # Different chapter common = word_pos.get_common_ancestor(unrelated_pos) print(f"Common ancestor of word and unrelated: {common}") print() def demonstrate_serialization(): """Show JSON and shelf serialization""" print("=== Serialization ===") # Create a complex position position = (PositionBuilder() .chapter(4) .block(8) .table(0, table_type="financial", columns=5, rows=20) .table_row(3, row_type="data", category="Q2") .table_cell(2, cell_type="currency", format="USD") .word(0, text="$1,234.56") .with_rendering_metadata( font_scale=1.2, page_size=[600, 800], theme="light", currency_format="USD" ) .build()) # JSON serialization json_str = position.to_json() print("JSON serialization:") print(json_str[:200] + "..." if len(json_str) > 200 else json_str) # Deserialize and verify restored = RecursivePosition.from_json(json_str) print(f"Restored position equals original: {position == restored}") print() def demonstrate_storage_systems(): """Show both JSON and shelf storage""" print("=== Storage Systems ===") # Create test positions positions = { "bookmark1": create_word_position(1, 5, 20, 3), "bookmark2": create_image_position(2, 3, 1), "bookmark3": create_table_cell_position(3, 1, 2, 1, 0) } # Test JSON storage print("JSON Storage:") json_storage = PositionStorage("demo_positions_json", use_shelf=False) for name, pos in positions.items(): json_storage.save_position("demo_doc", name, pos) print(f" Saved {name}: {pos}") # List and load positions saved_positions = json_storage.list_positions("demo_doc") print(f" Saved positions: {saved_positions}") loaded = json_storage.load_position("demo_doc", "bookmark1") print(f" Loaded bookmark1: {loaded}") print(f" Matches original: {loaded == positions['bookmark1']}") # Test shelf storage print("\nShelf Storage:") shelf_storage = PositionStorage("demo_positions_shelf", use_shelf=True) for name, pos in positions.items(): shelf_storage.save_position("demo_doc", name, pos) shelf_positions = shelf_storage.list_positions("demo_doc") print(f" Shelf positions: {shelf_positions}") # Clean up demo files import shutil try: shutil.rmtree("demo_positions_json") shutil.rmtree("demo_positions_shelf") except: pass print() def demonstrate_ereader_scenario(): """Show realistic ereader bookmark scenario""" print("=== Ereader Bookmark Scenario ===") # Simulate user reading progress reading_positions = [ # User starts reading chapter 1 (PositionBuilder() .chapter(1) .block(0) .paragraph() .word(0) .with_rendering_metadata(font_scale=1.0, page_size=[600, 800], theme="light") .build(), "Chapter 1 Start"), # User bookmarks an interesting quote in chapter 2 (PositionBuilder() .chapter(2) .block(15) .paragraph() .word(8, offset=0) .with_rendering_metadata(font_scale=1.2, page_size=[600, 800], theme="sepia") .build(), "Interesting Quote"), # User bookmarks a table in chapter 3 (PositionBuilder() .chapter(3) .block(22) .table(0, table_type="data", title="Sales Figures") .table_row(1, row_type="header") .table_cell(0, cell_type="header", text="Quarter") .with_rendering_metadata(font_scale=1.1, page_size=[600, 800], theme="dark") .build(), "Sales Table"), # User bookmarks an image caption (PositionBuilder() .chapter(4) .block(8) .image(0, alt_text="Company Logo", caption="Figure 4.1: Corporate Identity") .with_rendering_metadata(font_scale=1.0, page_size=[600, 800], theme="light") .build(), "Logo Image"), # User's current reading position (with character-level precision) (PositionBuilder() .chapter(5) .block(12) .paragraph() .word(23, offset=7) # 7 characters into word 23 .with_rendering_metadata(font_scale=1.3, page_size=[600, 800], theme="dark") .build(), "Current Position") ] # Save all bookmarks storage = PositionStorage("ereader_bookmarks", use_shelf=False) for position, description in reading_positions: bookmark_name = description.lower().replace(" ", "_") storage.save_position("my_novel", bookmark_name, position) print(f"Saved bookmark '{description}': {position}") print(f"\nTotal bookmarks: {len(storage.list_positions('my_novel'))}") # Demonstrate bookmark navigation print("\n--- Bookmark Navigation ---") current_pos = reading_positions[-1][0] # Current reading position for position, description in reading_positions[:-1]: # All except current # Calculate relationship to current position if position.is_ancestor_of(current_pos): relationship = "ancestor of current" elif current_pos.is_ancestor_of(position): relationship = "descendant of current" else: common = position.get_common_ancestor(current_pos) if len(common.path) > 1: relationship = f"shares {common.get_leaf_node().content_type.value} with current" else: relationship = "unrelated to current" print(f"'{description}' is {relationship}") # Clean up try: shutil.rmtree("ereader_bookmarks") except: pass print() def demonstrate_advanced_navigation(): """Show advanced navigation scenarios""" print("=== Advanced Navigation Scenarios ===") # Multi-level list navigation print("Multi-level List Navigation:") nested_list_pos = (PositionBuilder() .chapter(2) .block(5) .list(0, list_type="ordered", title="Main Topics") .list_item(2, text="Data Structures") .list(1, list_type="unordered", title="Subtopics") .list_item(1, text="Hash Tables") .word(3, text="implementation") .build()) print(f" Nested list position: {nested_list_pos}") # Navigate to parent list item parent_item_pos = nested_list_pos.copy().truncate_to_type(ContentType.LIST_ITEM) print(f" Parent list item: {parent_item_pos}") # Navigate to main list main_list_pos = nested_list_pos.copy().truncate_to_type(ContentType.LIST) print(f" Main list: {main_list_pos}") # Table navigation print("\nTable Navigation:") table_pos = (PositionBuilder() .chapter(3) .block(10) .table(0, table_type="comparison", rows=5, columns=3) .table_row(2, row_type="data") .table_cell(1, cell_type="data", header="Price") .word(0, text="$99.99") .build()) print(f" Table cell position: {table_pos}") # Navigate to different cells in same row next_cell_pos = table_pos.copy() cell_node = next_cell_pos.get_node(ContentType.TABLE_CELL) cell_node.index = 2 # Move to next column cell_node.metadata["header"] = "Quantity" word_node = next_cell_pos.get_node(ContentType.WORD) word_node.text = "5" print(f" Next cell position: {next_cell_pos}") # Verify they share the same row common = table_pos.get_common_ancestor(next_cell_pos) row_node = common.get_node(ContentType.TABLE_ROW) print(f" Shared row index: {row_node.index if row_node else 'None'}") print() def main(): """Run all demonstrations""" print("Recursive Position System Demonstration") print("=" * 50) print() demonstrate_basic_position_creation() demonstrate_different_content_types() demonstrate_position_relationships() demonstrate_serialization() demonstrate_storage_systems() demonstrate_ereader_scenario() demonstrate_advanced_navigation() print("=== Summary ===") print("The Recursive Position System provides:") print("✓ Hierarchical position tracking for any content type") print("✓ Dynamic content type support (words, images, tables, lists, etc.)") print("✓ Flexible serialization (JSON and Python shelf)") print("✓ Position relationships (ancestor/descendant queries)") print("✓ Fluent builder pattern for easy position creation") print("✓ Metadata support for rendering context") print("✓ Real-world ereader bookmark management") print("✓ Advanced navigation capabilities") print() print("This system is ideal for:") print("• Ereader applications with precise bookmarking") print("• Document editors with complex navigation") print("• Content management systems") print("• Any application requiring hierarchical position tracking") if __name__ == "__main__": main()