Skip to content

Commit

Permalink
Version - v2.1.0 (#30)
Browse files Browse the repository at this point in the history
Check #30 for release notes.

Co-authored-by: Nathan <[email protected]>
Co-authored-by: Shreyash Saitwal <[email protected]>
Co-authored-by: StormiFire <[email protected]>
  • Loading branch information
4 people authored Nov 21, 2020
1 parent 26d0eda commit 87261a1
Show file tree
Hide file tree
Showing 17 changed files with 930 additions and 908 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
patreon: ysfchn
41 changes: 32 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# 🧱 DynamicComponents-AI2 `Extension`
![Icon](assets/icon.png)

[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c9fee4822c864505a2ade6d19731caa5)](https://app.codacy.com/manual/ysfchn/DynamicComponents-AI2?utm_source=github.com&utm_medium=referral&utm_content=ysfchn/DynamicComponents-AI2&utm_campaign=Badge_Grade_Dashboard)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fysfchn%2FDynamicComponents-AI2.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fysfchn%2FDynamicComponents-AI2?ref=badge_shield)
# DynamicComponents-AI2 `Extension`

[![Maintainability](https://api.codeclimate.com/v1/badges/31e4cd31de1bd0e186c8/maintainability)](https://codeclimate.com/github/ysfchn/DynamicComponents-AI2/maintainability)

Fully supported Dynamic Components extension for MIT App Inventor 2. It is based on Java's reflection feature, so it creates the components by searching for a class by just typing its name. So it doesn't have a limited support for specific components, because it supports every component which is ever added to your App Inventor distribution!

So if you use Kodular, you will able to create all Kodular components, if you use App Inventor, you will able to create all App Inventor components and so on. Extension components are supported too!

> ⚠ The `beta` branch will be reset after every release. So stay on the `main` branch if you don't know what you do.
[![forthebadge](https://forthebadge.com/images/badges/its-not-a-lie-if-you-believe-it.svg)](https://forthebadge.com)

## 🧩 Blocks

<table style="width:100%">
Expand Down Expand Up @@ -41,15 +46,15 @@ So if you use Kodular, you will able to create all Kodular components, if you us
<br><br>
<table>
<tr>
<td><img src="assets/blocks/text.png"></td>
<td><img src="assets/other/text.png"></td>
<td><b>Name of the component.</b><br>✅ Doesn't require to add existing component.<br> ❌ Only components can be created.</td>
</tr>
<tr>
<td><img src="assets/blocks/component_block.png"></td>
<td><img src="assets/other/component_block.png"></td>
<td><b>Block of existing component to create new one from it.</b><br>❌ Requires a existing component.<br>✅ Extensions can be created also.</td>
</tr>
<tr>
<td><img src="assets/other/class_text.png" href="assets/blocks/class_text_full.png"></td></td>
<td><img src="assets/other/class_text.png" href="assets/other/class_text_full.png"></td></td>
<td><b>Full class name of the component.</b><br>✅ Doesn't require to add existing component.<br>✅ Extensions can be created also.<br><br>To learn the class name of the component use <code>GetName</code> block.</td>
</tr>
</table>
Expand Down Expand Up @@ -142,8 +147,7 @@ So if you use Kodular, you will able to create all Kodular components, if you us
<td>
Set a property of a component by typing its property name. Can be known as a Setter property block.<br>
It can be also used to set properties that only exists in Designer.
Supported values are; "string", "boolean", "integer" and "float". For other values, you should use
Any Component blocks.
It works for common types. For other values, you should use Any Component blocks.
</td>
</tr>
<!-- SET PROPERTIES -->
Expand Down Expand Up @@ -337,7 +341,7 @@ So if you use Kodular, you will able to create all Kodular components, if you us
<img src="assets/blocks/method_lastusedid.png">
</td>
<td>
Returns the last created component's ID by Create block.
Returns the last component's ID.
</td>
</tr>
<!-- USED IDS -->
Expand Down Expand Up @@ -394,6 +398,16 @@ So if you use Kodular, you will able to create all Kodular components, if you us
Returns the version name of the extension.
</td>
</tr>
<!-- ASYNC -->
<tr>
<td align="right">
<img src="assets/blocks/setget_async.png"><br>
<img src="assets/blocks/setget_async_2.png">
</td>
<td>
Sets whether component creation should work asynchronously or synchronously.
</td>
</tr>
<!-- SCHEMA CREATED -->
<tr>
<td align="right">
Expand All @@ -403,6 +417,15 @@ So if you use Kodular, you will able to create all Kodular components, if you us
Raises after Schema has been created with Schema block.
</td>
</tr>
<!-- COMPONENT CREATED -->
<tr>
<td align="right">
<img src="assets/blocks/event_componentcreated.png">
</td>
<td>
Raises after a component has been created using the Create block. It also will be raised for components that created with Schema.
</td>
</tr>
</table>

## 🔨 Building
Expand Down
11 changes: 5 additions & 6 deletions TemplateCreator/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ⚡ Template Creator <small>for Dynamic Components AI2</small>
This sub-project allows you to create templates from App Inventor Project files automatically! So you don't need to write templates manually anymore!

It includes two scripts. `TemplateCreate.py` does the main job which is generating the template, and `cli.py` is made for you to access the `TemplateCreate.py` easily.
It includes two scripts. `TemplateCreate.py` does the main job which is generating the template, and `menu.py` is made for you to access the `TemplateCreate.py` easily.

> This script requires Python that needs to be installed, but if you have a solution that will work on everyone's computer without installing something, you can always create a Pull Request and a new tool for that :)
Expand All @@ -15,13 +15,12 @@ Then install the external modules by executing `pip install -r requirements.txt`

## 📦 Usage

* Insert your .aia file in this directory. And remember its name for later step.
* Execute the `main.py` file.
You can do that in your terminal by entering this folder and executing `python main.py`.

* Let's suppose your .aia file name is "HelloWorld.aia", then execute this command:<br>
`python cli.py "HelloWorld.aia"`
* A file dialog will open, just select the AIA/AIS file that you want to convert, then click "Open" button.

You can also type the screen name that you want to get template of it.<br>
`python cli.py "HelloWorld.aia" --screen=Screen1`
* If the project contains more than one screen, you will be asked to select which screen will be used with selection dialog.

If everything goes well, you will see the generated JSON file in this directory.

Expand Down
181 changes: 94 additions & 87 deletions TemplateCreator/TemplateCreate.py
Original file line number Diff line number Diff line change
@@ -1,122 +1,129 @@
# --------------------------------------------
# TemplateCreator
# by Yusuf Cihan
#
# Generates DynamicComponents-AI2 schemas by
# parsing App Inventor project file automatically.
#
# - Yusuf Cihan
#
# MIT license.
# --------------------------------------------

import ast
from flatten_json import flatten, unflatten_list
import re

# Color converter
def BuildColor(R : int, G : int, B : int, A : int):
if A == 0:
return 255
else:
return (B + (G + (R + (256 * A)) * 256) * 256) - 4294967296

def GenerateTemplate(SCM : dict, extensions : dict, legacy : bool = False):
import json

EXTENSIONS = {}
KEYS = []

# Color converter for AI2
def BuildColor(code : str):
A = 255
val = str(code)[2:]
if len(val) != 6:
A = int(str(val)[0:2], 16)
R = int(str(val)[2:4], 16)
G = int(str(val)[4:6], 16)
B = int(str(val)[6:], 16)
return A << 24 | R << 16 | G << 8 | B


def Rearrange(obj : dict):
global EXTENSIONS
global KEYS
for key, value in obj.copy().items():
if key in obj:
# If key ends with Uuid or Version, ignore it.
# Because DynamicComponents-AI2 extension's JSON templates doesn't need it.
if key == "Uuid" or key == "$Version":
del obj[key]
# Rename the $Name according to the template structure.
elif key == "$Name":
obj["id"] = value
del obj[key]
# Rename the $Type according to the template structure.
elif key == "$Type":
# Use the extension's full class name if it is defined in the extensions dictionary.
if value in EXTENSIONS:
obj["type"] = EXTENSIONS[value]
del obj[key]
else:
obj["type"] = value
del obj[key]
# Rename the $Components according to the template structure.
elif key == "$Components":
obj["components"] = value
del obj[key]
# Move the properties inside a "properties" object.
# components/Button/Text --> components/Button/properties/Text
else:
# Copy of the value for editing purposes.
v = value
# Create a empty dictionary named properties if doesn't exists.
if "properties" not in obj:
obj["properties"] = {}
# Convert value to color if it presents a color code.
# Colors are started with &H.
if str(value).startswith("&H") and (len(str(value)) == 8 or len(str(value)) == 10):
v = BuildColor(v)
# Parse the values to their type automatically.
# If any error raised, use the value without changing the type.
try:
x = ast.literal_eval(v)
if key == "Text":
obj["properties"][key] = str(v)
else:
obj["properties"][key] = x
except:
obj["properties"][key] = v
del obj[key]
# If value contains template parameters, add them to the KEYS list.
if value is str:
for parameter in re.findall(r'(?<=(?<!\{)\{)[^{}]*(?=\}(?!\}))', value):
if parameter not in KEYS:
KEYS.append(parameter)
return obj


def GenerateTemplate(SCM : dict, extensions : dict):
# Template that will be modified later.
template = {
# Use app name as template name.
"name": SCM["Properties"]["AppName"],

# Current metadata version.
# Current metadata version.
# Needs to be 1, until a new type of metadata releases.
"metadata-version": 1,

# Extension version that this template generated for.
"extension_version": 5,

# Template author name.
"author": "<your name>",

# List of AI2 distributions that will template work on.
"platforms": SCM["authURL"],

# Contains used extensions in this template along with their class names.
# Example:
# {
# "HelloWorld": "io.foo.HelloWorld"
# }
"extensions": extensions,

# Template parameters.
# Will be generated automatically from SCM.
"keys": [],

# Components that will be created.
# Will be generated automatically from SCM.
"components": []
}

# Create a variable to store modified flatted JSON.
flatten_json = {}

# Edit the flatten JSON.
for key, value in flatten(SCM["Properties"], "/").items():
k = str(key)
val = value
# If key ends with Uuid or Version, ignore it.
# Because DynamicComponents-AI2 extension's JSON templates doesn't need it.
if k.endswith("/Uuid") or k.endswith("/$Version"):
continue
# Else;
else:
# Replace the "$Components" with "components" according to the template structure.
# $Components --> components
k = k.replace("/$Components/", "/components/")

# Rename the $Name and $Type according to the template structure.
# $Name --> id
# $Type --> type
if k[-5:] in ["$Name", "$Type"]:
k = k.replace("/$Name", "/id").replace("/$Type", "/type")
# Move the properties inside a "properties" object.
# components/Button/Text --> components/Button/properties/Text
else:
path = k.split("/")
path.insert(-1, "properties")
k = "/".join(path)

# Check if value contains template parameter(s).
# Parameters are defined with curly brackets.
# {text}, {age}, {color}
for parameter in re.findall(r'(?<=(?<!\{)\{)[^{}]*(?=\}(?!\}))', str(value) + " " + k):
if parameter not in template["keys"]:
template["keys"].append(parameter)

# Try to convert the value automatically.
# So if value is "True" or "False", then it will be converted to the bool and so on.
try:
val = ast.literal_eval(value)
except:
pass

# An exception for the color converting.
if str(val).startswith("&H"):
val = str(val)[2:]
if len(val) == 6:
alpha = int("FF", 16)
else:
alpha = int(str(val)[0:2], 16)
val = BuildColor(int(str(val)[2:4], 16), int(str(val)[4:6], 16), int(str(val)[6:], 16), alpha)

# If the component name is in the extensions list,
# then use its full internal name as it is an external package that
# doesn't exists in the App Inventor sources.
if k.endswith("/type") and (val in extensions):
val = extensions[val]

# Add the value and key to the modified flatten dictionary.
flatten_json[k] = val

# Now, unflat the modified flatten dictionary.
# Save the output to the template.
template["components"] = unflatten_list(flatten_json, "/")["$Components"]

# Remove DynamicComponent instances from template, because it is not needed.
global EXTENSIONS
global KEYS
# Save extensions in the list.
EXTENSIONS = extensions
# Convert SCM data to Dynamic Components schema data.
template["components"] = json.loads(json.dumps(SCM), object_hook = Rearrange)["properties"]["Properties"]["components"]
# Save found keys in the template.
template["keys"] = KEYS
# Remove DynamicComponent instances from template, because they are not needed.
for component in template["components"].copy():
if component["type"] == "DynamicComponents":
if component in template["components"]:
template["components"].remove(component)

# Return the template.
return template
48 changes: 0 additions & 48 deletions TemplateCreator/cli.py

This file was deleted.

Loading

0 comments on commit 87261a1

Please sign in to comment.