387 lines
13 KiB
Python
387 lines
13 KiB
Python
#!/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()
|