{"id":24752454,"url":"https://github.com/willwade/aacprocessors","last_synced_at":"2026-02-15T21:33:06.702Z","repository":{"id":272512593,"uuid":"916842329","full_name":"willwade/AACProcessors","owner":"willwade","description":"A python library to read, import, export and modify pagesets from different AAC providers","archived":false,"fork":false,"pushed_at":"2025-12-17T15:35:39.000Z","size":22752,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-21T22:37:54.282Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willwade.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-14T21:27:00.000Z","updated_at":"2025-12-17T15:35:43.000Z","dependencies_parsed_at":"2025-03-26T12:21:42.344Z","dependency_job_id":"cd6f74f6-2024-49db-bf98-e04353724e1c","html_url":"https://github.com/willwade/AACProcessors","commit_stats":null,"previous_names":["willwade/aacprocessors"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/willwade/AACProcessors","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2FAACProcessors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2FAACProcessors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2FAACProcessors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2FAACProcessors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willwade","download_url":"https://codeload.github.com/willwade/AACProcessors/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2FAACProcessors/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29490311,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T19:29:10.908Z","status":"ssl_error","status_checked_at":"2026-02-15T19:29:10.419Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-28T10:49:57.002Z","updated_at":"2026-02-15T21:33:06.697Z","avatar_url":"https://github.com/willwade.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AACProcessors\n\n[![PyPI version](https://badge.fury.io/py/aac-processors.svg)](https://badge.fury.io/py/aac-processors)\n[![Python Versions](https://img.shields.io/pypi/pyversions/aac-processors.svg)](https://pypi.org/project/aac-processors/)\n[![Tests](https://github.com/willwade/AACProcessors/actions/workflows/test.yml/badge.svg)](https://github.com/willwade/AACProcessors/actions/workflows/test.yml)\n[![Coverage](https://codecov.io/gh/willwade/AACProcessors/branch/main/graph/badge.svg)](https://codecov.io/gh/willwade/AACProcessors)\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)\n\nA Python library to read, import, export and modify pagesets from different AAC (Augmentative and Alternative Communication) providers. NB: Node/JS version at https://github.com/willwade/AACProcessors-nodejs and probably more coverage now.  Currently supports:\n\n- Grid 3 (`.gridset`)\n- CoughDrop (OpenBoard) (`.obf`, `.obz`)\n- TouchChat (`.touchChat`)\n- Snap Core First (`.spb`)\n\nAnd unusual formats:\n\n- Dot Processor (.dot)\n- OPML (.opml)\n- Apple Panels\n- Screenshot Processor (optional)\n\n## Features\n\n- Extract text content from AAC pagesets\n- Load AAC pagesets into a common tree structure\n- Support for multiple AAC software formats\n- Translation support for extracted text\n- Consistent API across different AAC formats\n- Convert between different AAC formats\n- Analyze vocabulary usage and structure\n\n\n\n## Installation\n\n```bash\npip install aac-processors\n```\n\n## Quick Start\n\n```python\nfrom aac_processors import GridsetProcessor, CoughDropProcessor, TouchChatProcessor, SnapProcessor\n\n# Example with Grid 3\nprocessor = GridsetProcessor()\ntexts = processor.extract_texts(\"path/to/your.gridset\")\nprint(f\"Found {len(texts)} text items\")\n\n# Load into tree structure\ntree = processor.load_into_tree(\"path/to/your.gridset\")\nprint(f\"Found {len(tree.pages)} pages\")\n\n# Translate texts\ntranslations = {\n    \"Hello\": \"Hola\",\n    \"Goodbye\": \"Adiós\"\n}\ntranslated_file = processor.process_texts(\"path/to/your.gridset\", translations, \"path/to/output.gridset\")\n```\n\n## Try it now!\n\nWant to try AACProcessors without installing? Click below to run our interactive demo:\n\n[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/willwade/AACProcessors/blob/main/examples/demo_aacprocessors.ipynb)\n\nThis interactive notebook will:\n- Install AACProcessors\n- Download example AAC files\n- Demonstrate key features:\n  - Viewing file structures\n  - Extracting texts\n  - Analyzing vocabulary\n  - Converting between formats\n\n## Usage Examples\n\n### Converting Between Formats (OBZ Import/Export)\n\n```python\nfrom aac_processors import GridsetProcessor, CoughDropProcessor\n\n# Convert Grid3 to CoughDrop OBZ\ngrid_processor = GridsetProcessor()\ncough_processor = CoughDropProcessor()\n\n# Load Grid3 file into common tree structure\ntree = grid_processor.load_into_tree(\"path/to/your.gridset\")\n\n# Export tree to CoughDrop OBZ format\ncough_processor.export_tree(tree, \"output.obz\")\n\n# Import from OBZ\nimported_tree = cough_processor.load_into_tree(\"path/to/board.obz\")\n```\n\n### Viewing File Structure\n\nThe library includes two ways to view AAC board structures:\n\n1. Using the command-line viewer:\n\n```bash\n# Using the standalone viewer script\npython demo_viewer.py path/to/your/board.gridset\n\n# Or using the package module directly\npython -m aac_processors.viewer path/to/your/board.gridset\n\n# Programmatically:\nfrom aac_processors import viewer, GridsetProcessor\n\n# Load and print a board structure\nprocessor = GridsetProcessor()\ntree = processor.load_into_tree(\"path/to/your.gridset\")\nviewer.print_tree(tree)\n\n# Or use the auto-detection feature\nviewer.main()  # Will prompt for file path\n```\n\n2. Programmatically:\n```python\nfrom aac_processors import viewer, GridsetProcessor\nfrom aac_processors.tree_structure import ButtonType\n\n# Load and print a board structure\nprocessor = GridsetProcessor()\ntree = processor.load_into_tree(\"path/to/your.gridset\")\nviewer.print_tree(tree)\n\n# Or use the auto-detection feature\nviewer.main()  # Will prompt for file path\n```\n\nThe viewer will show:\n- Complete board structure with pages and buttons\n- Button types (🗣️ Speech, 🔀 Navigation, ⚡ Action)\n- Grid layout and button positions\n- Navigation analysis (dead ends, orphaned pages)\n- Circular references in navigation\n\n### Vocabulary Analysis\n\n```python\nfrom aac_processors import GridsetProcessor\nfrom collections import Counter\nimport re\n\ndef analyze_vocabulary(file_path):\n    processor = GridsetProcessor()\n    tree = processor.load_into_tree(file_path)\n    \n    # Collect all text from buttons\n    words = []\n    for page in tree.pages.values():\n        for button in page.buttons:\n            if button.label:\n                # Split into words and clean\n                button_words = re.findall(r'\\w+', button.label.lower())\n                words.extend(button_words)\n            if button.message:\n                message_words = re.findall(r'\\w+', button.message.lower())\n                words.extend(message_words)\n    \n    # Count word frequencies\n    word_counts = Counter(words)\n    \n    # Print statistics\n    print(f\"Total unique words: {len(word_counts)}\")\n    print(\"\\nMost common words:\")\n    for word, count in word_counts.most_common(10):\n        print(f\"  {word}: {count}\")\n    \n    return word_counts\n\n# Example usage\nvocabulary = analyze_vocabulary(\"path/to/your.gridset\")\n```\n\n## Demo\n\nSee `demo.py` for a complete example of how to use the library.\n\n```bash\npython demo.py\n```\n\nOutput:\n\n```bash\n=== Viewing File Structure ===\n\nViewing structure of SimpleTest.gridset:\n\n=== AAC Board Structure ===\n\nRoot Page:\n  📄 Start (4x4 grid)\n    Row 0:\n      [Empty] (0, 0)\n      🔀 quick chat (0, 1)\n        └─ Says: quick chat\n        └─ Goes to: quick chat\n      🔀 don't like (0, 2)\n        └─ Says: don't like\n        └─ Goes to: Don't like\n      🔀 something different (0, 3)\n        └─ Says: something different\n        └─ Goes to: something different\n    Row 1:\n      [Empty] (1, 0)\n      🔀 something's wrong (1, 1)\n        └─ Says: something's wrong\n        └─ Goes to: something's wrong\n      🔀 I want (1, 2)\n        └─ Says: I want\n        └─ Goes to: I want\n      🔀 Comment (1, 3)\n        └─ Says: Comment\n        └─ Goes to: Comment\n    Row 2:\n      [Empty] (2, 0)\n      🔀 About me (2, 1)\n        └─ Says: About me\n        └─ Goes to: About me\n      └─ Target Page:\n          📄 About me (5x4 grid)\n            Row 0:\n              🗣️ [No Label] (0, 0)\n              🗣️ Back (0, 1)\n                └─ Says: Back\n              🔀 Family (0, 2)\n                └─ Says: Family\n                └─ Goes to: Family\n              └─ Target Page:\n                  📄 Family (5x7 grid)\n                    Row 0:\n                      [Empty] (0, 0)\n                      🗣️ Back (0, 1)\n                        └─ Says: Back\n                      [Empty] (0, 2)\n                      🗣️ Younger Sister (0, 3)\n                        └─ Says: Younger Sister\n                      [Empty] (0, 4)\n                      [Empty] (0, 5)\n                      [Empty] (0, 6)\n                    Row 1:\n                      [Empty] (1, 0)\n                      🗣️ Dad (1, 1)\n                        └─ Says: Dad\n                      [Empty] (1, 2)\n                      [Empty] (1, 3)\n                      [Empty] (1, 4)\n                      [Empty] (1, 5)\n                      [Empty] (1, 6)\n                    Row 2:\n                      [Empty] (2, 0)\n                      [Empty] (2, 1)\n                      [Empty] (2, 2)\n                      🔀 Cousins (2, 3)\n                        └─ Says: Cousins\n                        └─ Goes to: Cousins\n                      [Empty] (2, 4)\n                      [Empty] (2, 5)\n                      [Empty] (2, 6)\n                    Row 3:\n                      [Empty] (3, 0)\n                      🗣️ Mum (3, 1)\n                        └─ Says: Mum\n                      [Empty] (3, 2)\n                      [Empty] (3, 3)\n                      [Empty] (3, 4)\n                      [Empty] (3, 5)\n                      [Empty] (3, 6)\n                    Row 4:\n                      [Empty] (4, 0)\n                      [Empty] (4, 1)\n                      [Empty] (4, 2)\n                      🔀 Aunts and Uncles (4, 3)\n                        └─ Says: Aunts and Uncles\n                        └─ Goes to: Aunts and Uncles\n                      [Empty] (4, 4)\n                      [Empty] (4, 5)\n                      [Empty] (4, 6)\n              🗣️ Vegetarian (0, 3)\n                └─ Says: Vegetarian\n            Row 1:\n              [Empty] (1, 0)\n              🗣️ Name (1, 1)\n                └─ Says: Name\n              🔀 Pets (1, 2)\n                └─ Says: Pets\n                └─ Goes to: Pets\n              [Empty] (1, 3)\n            Row 2:\n              [Empty] (2, 0)\n              🗣️ Age (2, 1)\n                └─ Says: Age\n              🔀 Places I have been (2, 2)\n                └─ Says: Places I have been\n                └─ Goes to: Places I have been\n              [Empty] (2, 3)\n            Row 3:\n              [Empty] (3, 0)\n              🗣️ My birthday (3, 1)\n                └─ Says: My birthday\n              🗣️ like to look (3, 2)\n                └─ Says: like to look\n              [Empty] (3, 3)\n            Row 4:\n              [Empty] (4, 0)\n              [Empty] (4, 1)\n              [Empty] (4, 2)\n              [Empty] (4, 3)\n      🔀 I'm asking a question (2, 2)\n        └─ Says: I'm asking a question\n        └─ Goes to: questions\n      🔀 I feel (2, 3)\n        └─ Says: I feel\n        └─ Goes to: Feelings\n      └─ Target Page:\n          📄 Feelings (6x4 grid)\n            Row 0:\n              🗣️ [No Label] (0, 0)\n              🗣️ Back (0, 1)\n                └─ Says: Back\n              🗣️ angry. (0, 2)\n                └─ Says: angry.\n              [Empty] (0, 3)\n            Row 1:\n              [Empty] (1, 0)\n              🗣️ happy. (1, 1)\n                └─ Says: happy.\n              🗣️ excited. (1, 2)\n                └─ Says: excited.\n              [Empty] (1, 3)\n            Row 2:\n              [Empty] (2, 0)\n              🗣️ sad. (2, 1)\n                └─ Says: sad.\n              🗣️ fantastic. (2, 2)\n                └─ Says: fantastic.\n              [Empty] (2, 3)\n            Row 3:\n              [Empty] (3, 0)\n              🗣️ tired. (3, 1)\n                └─ Says: tired.\n              [Empty] (3, 2)\n              [Empty] (3, 3)\n            Row 4:\n              [Empty] (4, 0)\n              [Empty] (4, 1)\n              [Empty] (4, 2)\n              [Empty] (4, 3)\n            Row 5:\n              [Empty] (5, 0)\n              [Empty] (5, 1)\n              [Empty] (5, 2)\n              [Empty] (5, 3)\n    Row 3:\n      [Empty] (3, 0)\n      🔀 like (3, 1)\n        └─ Says: like\n        └─ Goes to: Like\n      🔀 Places to go (3, 2)\n        └─ Says: Places to go\n        └─ Goes to: let's go\n      🔀 Alphabet (3, 3)\n        └─ Says: Alphabet\n        └─ Goes to: Alphabet\n\n=== Navigation Analysis ===\n\nTotal Pages: 6\n\nDead End Pages (no way back):\n  - Family\n  - Feelings\n\nOrphaned Pages (no way to reach):\n  - Drinks\n  - About me 2\n\n=== Extracting Texts ===\n\nExtracted 43 texts from SimpleTest.gridset\nSample texts: [\"something's wrong\", 'Age', 'Back to home page', 'I feel', 'Pets']\n\n=== Loading Tree Structure ===\n\nLoaded 6 pages from SimpleTest.gridset\n\nPage Drinks: Drinks\nGrid size: (5, 4)\nButtons: 7\n\nPage Family: Family\nGrid size: (5, 7)\nButtons: 8\n```\n\n## Supported Formats\n\n### Grid 3 (`.gridset`)\n- Full support for reading grid layouts\n- Text extraction from buttons and pages\n- Translation support\n\n### CoughDrop (`.obf`, `.obz`)\n- Support for both single board (`.obf`) and board set (`.obz`) formats\n- Extraction of button labels and messages\n- Translation capabilities\n\n### TouchChat (`.touchChat`)\n- Support for TouchChat page sets\n- Button text extraction\n- Page structure loading\n\n### Snap Core First (`.spb`)\n- Support for Snap Core First board sets\n- Text extraction from buttons and pages\n- Basic translation support\n\n### Screenshot Processor (optional)\n\nThe screenshot processor is an optional dependency that requires additional dependencies. Install it with:\n\n```bash\npip install aac-processors[screenshot]\n```\n\n#### screenshot Usage\n\n```python\nfrom aac_processors import ScreenshotProcessor\n\n# Create processor\nprocessor = ScreenshotProcessor()\n\n# Load and print a board structure from screenshot\ntree = processor.load_into_tree(\n    \"path/to/screenshot.png\",\n    grid_rows=6,     # Optional: specify grid dimensions if known\n    grid_cols=4      # e.g. 6x4 for TouchChat24, 6x10 for TouchChat60\n)\nviewer.print_tree(tree)\n\n# Extract text from screenshot\ntexts = processor.extract_texts(\"path/to/screenshot.png\")\nprint(texts)\n\n# Initialize processor with debug images enabled (optional)\nprocessor = ScreenshotProcessor(save_debug_images=True)\n\n# Get detailed page info including colors and grid layout\npage = processor.create_page_from_screenshot(\n    \"path/to/screenshot.png\",\n    grid_rows=6,        # Number of rows if known\n    grid_cols=4,        # Number of columns if known\n    ignore_rows=1,      # Skip top N rows (e.g. for menu bars)\n)\nprint(f\"Grid size: {page.grid_size}\")\nfor btn in page.buttons:\n    print(f\"Button at {btn.position}: {btn.label} (color: {btn.style.body_color})\")\n```\n\nDebug Images: When `save_debug_images=True` is set, the processor creates two visualization files alongside the input image:\n- `input.png.debug.png`: Shows the detected grid cells in green\n- `input.png.text_debug.png`: Shows detected text regions in blue with text overlay\n\n## Command Line Interface\n\nThe package provides a command-line interface (CLI) for viewing and converting AAC files. After installation, you can use it in two ways:\n\n### Interactive Mode\n\nSimply run without arguments to enter interactive mode:\n\n```bash\naac-processors\n```\n\nThis will guide you through:\n1. Selecting an AAC file (with tab completion)\n2. Choosing to view its structure or convert it\n3. If converting, selecting the target format and output path\n\n### Command Line Mode\n\nFor direct command-line usage:\n\n1. View an AAC file structure:\n```bash\naac-processors view input.gridset\n```\n\n2. Convert between formats:\n```bash\n# Basic conversion (auto-generates output filename)\naac-processors convert input.gridset --to coughdrop\n\n# Specify custom output path\naac-processors convert input.obf --to grid --output custom_name.gridset\n```\n\n## API Documentation\n\n### Base Classes\n\n#### FileProcessor\nThe base class that all format-specific processors inherit from.\n\n```python\nclass FileProcessor:\n    def extract_texts(self, file_path: str) -\u003e List[str]:\n        \"\"\"Extract all text content from the file\"\"\"\n        \n    def load_into_tree(self, file_path: str) -\u003e AACTree:\n        \"\"\"Load the file into a common tree structure\"\"\"\n        \n    def process_texts(\n        self,\n        file_path: str,\n        translations: Optional[Dict[str, str]] = None,\n        output_path: Optional[str] = None\n    ) -\u003e Union[List[str], str, None]:\n        \"\"\"Process and translate texts in the file.\n        \n        Returns:\n            - List[str] if extracting texts (translations=None)\n            - str if translating (path to translated file)\n            - None if error occurs\n        \"\"\"\n        \n    def set_source_file(self, file_path: str) -\u003e None:\n        \"\"\"Set the source file path for processing\"\"\"\n        \n    def cleanup_temp_files(self) -\u003e None:\n        \"\"\"Clean up any temporary files created during processing\"\"\"\n```\n\n### Common Data Structures\n\n#### AACTree\nRepresents the common structure for all AAC formats.\n\n```python\nclass AACTree:\n    pages: Dict[str, AACPage]  # Dictionary of pages by ID\n    root_id: Optional[str]  # ID of the root page\n    \n    def add_page(self, page: AACPage) -\u003e None:\n        \"\"\"Add a page to the tree\"\"\"\n        \n    def get_page(self, page_id: str) -\u003e Optional[AACPage]:\n        \"\"\"Get a page by ID\"\"\"\n```\n\n#### AACPage\nRepresents a single page in an AAC system.\n\n```python\nclass AACPage:\n    id: str  # Unique identifier for the page\n    name: str  # Display name of the page\n    grid_size: Tuple[int, int]  # (rows, columns)\n    buttons: List[AACButton]  # List of buttons on the page\n```\n\n#### AACButton\nRepresents a button in an AAC system.\n\n```python\nclass AACButton:\n    id: str  # Unique identifier for the button\n    label: str  # Display text\n    type: ButtonType  # SPEAK, NAVIGATE, ACTION, etc.\n    position: Tuple[int, int]  # Grid position (row, col)\n    vocalization: Optional[str]  # Text to speak\n    target_page_id: Optional[str]  # For navigation buttons\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n## Testing\n\nTo run the tests:\n\n```bash\npython -m pytest\n```\n\nCurrent test coverage: 52%\n\n## Dev\n\n```bash\nuv pip install -e \".[dev,screenshot]\"\n```\n\n## License\n\nThis project is licensed under the AGPLv3 License - see the [LICENSE](LICENSE) file for details.\n\n## Acknowledgments\n\n- OpenAAC \n- The AAC community for their feedback and support\n\n## Contributors\n\n- [Will Wade](https://github.com/willwade)\n- [OpenAAC](https://github.com/OpenAAC)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillwade%2Faacprocessors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillwade%2Faacprocessors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillwade%2Faacprocessors/lists"}