# AIA Tools
AIA Tools is a Python library for interacting with App Inventor Application (AIA) files in Python. It is useful for opening, summarizing, and analyzing AIA files for research inquiries. The query API is inspired by SQLalchemy
## Installing
```shell
$ pip install aiatools
```
For development:
```shell
$ pyenv install 3.6.3
$ pyenv virtualenv 3.6.3 aiatools
$ pyenv activate aiatools
$ pip install -r requirements.txt
$ pip install .
```
## Usage Examples
```python
from aiatools import AIAFile
with AIAFile('MyProject.aia') as aia:
print('Number of screens: %d\n' % len(aia.screens))
print('Number of components: %d\n' % len(aia.screens['Screen1'].componentiter()))
print('Number of blocks: %d\n' % len(aia.screens['Screen1'].blockiter()))
print('Number of event blocks: %d\n' % len(aia.screens['Screen1'].blockiter(type='component_event')))
aia.screens['Screen1'].blocks(type=='component_event').count(by='event_name')
```
```python
from aiatools import AIAFile
from aiatools.attributes import event_name, type
from aiatools.block_types import *
from aiatools.component_types import *
aia = AIAFile('MyProject.aia')
# Count the number of screens
print len(aia.screens)
# Count the number of distinct component types used on Screen1
print aia.screens['Screen1'].components().count(group_by=type)
# Count the number of Button components on Screen1
print aia.screens['Screen1'].components(type==Button).count()
# Count the number of component_event blocks, grouped by event name
print aia.screens['Screen1'].blocks(type==component_event).count(group_by=event_name)
# Compute the average depth of the blocks tree in Button.Click handlers
print aia.screens['Screen1'].blocks(type==component_event && event_name == Button.Click).avg(depth)
# Count the number of blocks referencing a specific component
print aia.screens['Screen1'].components(name=='Button1').blocks().count()
# Count the number of event handlers where the event opens another screen
print aia.blocks(type==component_event).descendants(type==control_openAnotherScreen).count()
# Get the screens where the user has included more than one TinyDB
print aia.screens().components(type==TinyDB).count(group_by = Screen.name).filter(lambda k,v: v > 1)
```
## Selectors
```python
project = AIAFile('project.aia')
project.screens() # Select all screens
project.screens('Screen1') # Select Screen1
project.screens(Button.any) # Select any screen with at least 1 button
project.screens(Control.open_another_screen) # Select any screen containing an open_another_screen block
project.screens(Component.Name == 'TinyDb1') # Select any screen containing a component named TinyDb1
```
```python
class Block(object):
"""
:py:class:`Block` represents an individual block in the blocks workspace.
.. Arguments ::
id_ The block ID
type_ :py:class:`BlockType` The block type
"""
def __init__(self, id_, type_):
self.id = id_
self.type = type_
self.parent = None
self.children = []
class Component(object):
"""
:py:class:`Component` represents a component in the designer view.
.. Arguments ::
id_
type_ :py:class:`ComponentType`
"""
def __init__(self, id_, type_):
self.id = id_
self.type = type_
self.properties = {}
class ComponentContainer(Component):
def __init__(self, id_, type_):
super(self, ComponentContainer).__init__(id_, type_)
self.components = []
class BlockType(object):
def __init__(self, name):
self.name = name
self.mutators = []
class ComponentType(object):
def __init__(self, name, class_name):
self.name = name
self.class_name = class_name
class Screen(object):
def __init__(self, scm=None, bky=None):
self.name = ''
self.properties = {}
self.components = FilterableDict()
self.blocks = FilterableDict()
self.top_blocks = FilterableDict()
if scm is not None:
self._read_scheme(scm)
if bky is not None:
self._read_blocks(bky)
class Project(object):
def __init__(self, file=None):
self.name = ''
self.screens = FilterableDict()
self.components = FilterableDict()
self.components.parent = self.screens
self.blocks = FilterableDict()
self.blocks.parent = self.screens
if file is not None:
self.read(file)
class FilterableDict(dict):
def __call__(self, filter_):
return FilterableDict([k, v for k, v in self.iteritems() if filter_(v) else None, None])
class Filter(object):
def __call__(self, o):
throw NotImplementedError()
def __and__(self, right):
return and_(self, right)
def __or__(self, right):
return or_(self, right)
def __eq__(self, right):
return eq(self, right)
def __ne__(self, right):
return ne(self, right)
def __lt__(self, right):
return lt(self, right)
def __gt__(self, right):
return gt(self, right)
def __le__(self, right):
return le(self, right)
def __ge__(self, right):
return ge(self, right)
class AndFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) and self.r(o)
class OrFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) or self.r(o)
class NotFilter(Filter):
def __init__(self, expression):
self.expression = expression
def __call__(self, o):
return not self.expression(o)
class EqualFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) == self.r(o)
class NotEqualFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) != self.r(o)
class LessThanFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) < self.r(o)
class GreaterThanFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) > self.r(o)
class LessThanOrEqualFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) <= self.r(o)
class GreaterThanOrEqualFilter(Filter):
def __init__(self, l, r):
self.l = l
self.r = r
def __call__(self, o):
return self.l(o) <= self.r(o)
class ScreenFilter(Filter):
pass
class ComponentFilter(Filter):
pass
class BlockFilter(Filter):
pass
```
## Attributes
`depth` - For a component, this is the depth of the component hierarchy rooted at that component. For components that are not containers this value is always 1. For containers and blocks, this is the longest length of the paths from this root node to any of its leaf nodes.
`length` - The number of direct descendants of the target. If the target is a component container, it will be the number of direct chidlren. For a block, it will be the number of
`children` - The list of children for the item(s) in the set. If more than one item is in the set, the children will be provided in the order of their parents.
`mutators` - If the block has mutations, a list of strings indicating the types of the mutations, e.g. ['if', 'elseif', 'elseif', 'else'].
`callers` - For procedures, the number of caller blocks in the workspace. For variables and component methods and properties, the number of getter blocks.
## Aggregation
`max` - Maximum value of the filter
`min` - Minimum value of the filter
`avg` - Average value of the filter
`count` - Count of items matching the filter
`median` - Median value of the attribute
Raw data
{
"_id": null,
"home_page": "https://github.com/mit-cml/aiatools",
"name": "aiatools",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "App Inventor AIA extraction analysis toolkit",
"author": "Evan W. Patton",
"author_email": "ewpatton@mit.edu",
"download_url": "https://files.pythonhosted.org/packages/45/71/fd3c058759033766595546310c5479f09327b238039b00f30106184fe590/aiatools-0.5.1.tar.gz",
"platform": null,
"description": "# AIA Tools\nAIA Tools is a Python library for interacting with App Inventor Application (AIA) files in Python. It is useful for opening, summarizing, and analyzing AIA files for research inquiries. The query API is inspired by SQLalchemy\n\n## Installing\n\n```shell\n$ pip install aiatools\n```\n\nFor development:\n\n```shell\n$ pyenv install 3.6.3\n$ pyenv virtualenv 3.6.3 aiatools\n$ pyenv activate aiatools\n$ pip install -r requirements.txt\n$ pip install .\n```\n\n## Usage Examples\n\n```python\nfrom aiatools import AIAFile\n\nwith AIAFile('MyProject.aia') as aia:\n print('Number of screens: %d\\n' % len(aia.screens))\n print('Number of components: %d\\n' % len(aia.screens['Screen1'].componentiter()))\n print('Number of blocks: %d\\n' % len(aia.screens['Screen1'].blockiter()))\n print('Number of event blocks: %d\\n' % len(aia.screens['Screen1'].blockiter(type='component_event')))\n aia.screens['Screen1'].blocks(type=='component_event').count(by='event_name')\n```\n\n```python\nfrom aiatools import AIAFile\nfrom aiatools.attributes import event_name, type\nfrom aiatools.block_types import *\nfrom aiatools.component_types import *\n\naia = AIAFile('MyProject.aia')\n\n# Count the number of screens\nprint len(aia.screens)\n\n# Count the number of distinct component types used on Screen1\nprint aia.screens['Screen1'].components().count(group_by=type)\n\n# Count the number of Button components on Screen1\nprint aia.screens['Screen1'].components(type==Button).count()\n\n# Count the number of component_event blocks, grouped by event name\nprint aia.screens['Screen1'].blocks(type==component_event).count(group_by=event_name)\n\n# Compute the average depth of the blocks tree in Button.Click handlers\nprint aia.screens['Screen1'].blocks(type==component_event && event_name == Button.Click).avg(depth)\n\n# Count the number of blocks referencing a specific component\nprint aia.screens['Screen1'].components(name=='Button1').blocks().count()\n\n# Count the number of event handlers where the event opens another screen\nprint aia.blocks(type==component_event).descendants(type==control_openAnotherScreen).count()\n\n# Get the screens where the user has included more than one TinyDB\nprint aia.screens().components(type==TinyDB).count(group_by = Screen.name).filter(lambda k,v: v > 1)\n```\n\n## Selectors\n\n```python\nproject = AIAFile('project.aia')\n\nproject.screens() # Select all screens\nproject.screens('Screen1') # Select Screen1\nproject.screens(Button.any) # Select any screen with at least 1 button\nproject.screens(Control.open_another_screen) # Select any screen containing an open_another_screen block\nproject.screens(Component.Name == 'TinyDb1') # Select any screen containing a component named TinyDb1\n```\n\n```python\nclass Block(object):\n \"\"\"\n :py:class:`Block` represents an individual block in the blocks workspace.\n\n .. Arguments ::\n id_ The block ID\n type_ :py:class:`BlockType` The block type\n \"\"\"\n def __init__(self, id_, type_):\n self.id = id_\n self.type = type_\n self.parent = None\n self.children = []\n\n\nclass Component(object):\n \"\"\"\n :py:class:`Component` represents a component in the designer view.\n\n .. Arguments ::\n id_\n type_ :py:class:`ComponentType`\n \"\"\"\n def __init__(self, id_, type_):\n self.id = id_\n self.type = type_\n self.properties = {}\n\n\nclass ComponentContainer(Component):\n def __init__(self, id_, type_):\n super(self, ComponentContainer).__init__(id_, type_)\n self.components = []\n\n\nclass BlockType(object):\n def __init__(self, name):\n self.name = name\n self.mutators = []\n\n\nclass ComponentType(object):\n def __init__(self, name, class_name):\n self.name = name\n self.class_name = class_name\n\n\nclass Screen(object):\n def __init__(self, scm=None, bky=None):\n self.name = ''\n self.properties = {}\n self.components = FilterableDict()\n self.blocks = FilterableDict()\n self.top_blocks = FilterableDict()\n if scm is not None:\n self._read_scheme(scm)\n if bky is not None:\n self._read_blocks(bky)\n\n\nclass Project(object):\n def __init__(self, file=None):\n self.name = ''\n self.screens = FilterableDict()\n self.components = FilterableDict()\n self.components.parent = self.screens\n self.blocks = FilterableDict()\n self.blocks.parent = self.screens\n if file is not None:\n self.read(file)\n\n\nclass FilterableDict(dict):\n def __call__(self, filter_):\n return FilterableDict([k, v for k, v in self.iteritems() if filter_(v) else None, None])\n\n\nclass Filter(object):\n def __call__(self, o):\n throw NotImplementedError()\n\n def __and__(self, right):\n return and_(self, right)\n\n def __or__(self, right):\n return or_(self, right)\n\n def __eq__(self, right):\n return eq(self, right)\n\n def __ne__(self, right):\n return ne(self, right)\n\n def __lt__(self, right):\n return lt(self, right)\n\n def __gt__(self, right):\n return gt(self, right)\n\n def __le__(self, right):\n return le(self, right)\n\n def __ge__(self, right):\n return ge(self, right)\n\n\nclass AndFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) and self.r(o)\n\n\nclass OrFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) or self.r(o)\n\n\nclass NotFilter(Filter):\n def __init__(self, expression):\n self.expression = expression\n\n def __call__(self, o):\n return not self.expression(o)\n\n\nclass EqualFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) == self.r(o)\n\n\nclass NotEqualFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) != self.r(o)\n\n\nclass LessThanFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) < self.r(o)\n\n\nclass GreaterThanFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) > self.r(o)\n\n\nclass LessThanOrEqualFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) <= self.r(o)\n\n\nclass GreaterThanOrEqualFilter(Filter):\n def __init__(self, l, r):\n self.l = l\n self.r = r\n\n def __call__(self, o):\n return self.l(o) <= self.r(o)\n\n\nclass ScreenFilter(Filter):\n pass\n\n\nclass ComponentFilter(Filter):\n pass\n\n\nclass BlockFilter(Filter):\n pass\n```\n\n## Attributes\n\n`depth` - For a component, this is the depth of the component hierarchy rooted at that component. For components that are not containers this value is always 1. For containers and blocks, this is the longest length of the paths from this root node to any of its leaf nodes.\n\n`length` - The number of direct descendants of the target. If the target is a component container, it will be the number of direct chidlren. For a block, it will be the number of\n\n`children` - The list of children for the item(s) in the set. If more than one item is in the set, the children will be provided in the order of their parents.\n\n`mutators` - If the block has mutations, a list of strings indicating the types of the mutations, e.g. ['if', 'elseif', 'elseif', 'else'].\n\n`callers` - For procedures, the number of caller blocks in the workspace. For variables and component methods and properties, the number of getter blocks.\n\n## Aggregation\n\n`max` - Maximum value of the filter\n\n`min` - Minimum value of the filter\n\n`avg` - Average value of the filter\n\n`count` - Count of items matching the filter\n\n`median` - Median value of the attribute\n\n\n",
"bugtrack_url": null,
"license": "GPLv3+",
"summary": "Tools for extracting information from App Inventor AIA files",
"version": "0.5.1",
"project_urls": {
"Homepage": "https://github.com/mit-cml/aiatools"
},
"split_keywords": [
"app",
"inventor",
"aia",
"extraction",
"analysis",
"toolkit"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "7a232dd64b88a9985716dfc3d952fe4e59251208b79eb1c42e569b723daa1fd1",
"md5": "b89f0201598f5629a79184fa8cd6b3a3",
"sha256": "1643fbc0390f162cd42b802de389a08ef30e1a5c08b194458768cd21427ee390"
},
"downloads": -1,
"filename": "aiatools-0.5.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b89f0201598f5629a79184fa8cd6b3a3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 133421,
"upload_time": "2023-10-26T00:32:58",
"upload_time_iso_8601": "2023-10-26T00:32:58.639224Z",
"url": "https://files.pythonhosted.org/packages/7a/23/2dd64b88a9985716dfc3d952fe4e59251208b79eb1c42e569b723daa1fd1/aiatools-0.5.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "4571fd3c058759033766595546310c5479f09327b238039b00f30106184fe590",
"md5": "82dbe36aa3b8021c5e5e3cbcbbc0a6ed",
"sha256": "75342a4b51b22a92445b9af6df4c0272a4fb2d0ea6d39b8336e186d59d3cedfd"
},
"downloads": -1,
"filename": "aiatools-0.5.1.tar.gz",
"has_sig": false,
"md5_digest": "82dbe36aa3b8021c5e5e3cbcbbc0a6ed",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 129705,
"upload_time": "2023-10-26T00:33:00",
"upload_time_iso_8601": "2023-10-26T00:33:00.668907Z",
"url": "https://files.pythonhosted.org/packages/45/71/fd3c058759033766595546310c5479f09327b238039b00f30106184fe590/aiatools-0.5.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-10-26 00:33:00",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mit-cml",
"github_project": "aiatools",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "aiatools"
}