Is there any way to compute the number of source lines of code (SLOC) or blocks used in an App Inventor 2 project?
I wrote a Python function aia_count_blocks(aia_filename)
to count the number of blocks in a given AIA file by counting the number of occurrences of the string <block
in the bky files:
from __future__ import print_function
from __future__ import division
import glob
import ntpath
import os
import shutil
import zipfile
def unzip(source_filename, dest_dir):
'''
Source: http://stackoverflow.com/questions/12886768/how-to-unzip-file-in-python-on-all-oses
'''
with zipfile.ZipFile(source_filename) as zf:
for member in zf.infolist():
# Path traversal defense copied from
# http://hg.python.org/cpython/file/tip/Lib/http/server.py#l789
words = member.filename.split('/')
path = dest_dir
for word in words[:-1]:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir, ''): continue
path = os.path.join(path, word)
zf.extract(member, path)
def list_subdirectories(dir):
'''
Source: http://stackoverflow.com/questions/800197/get-all-of-the-immediate-subdirectories-in-python
'''
return filter(os.path.isdir, [os.path.join(dir,f) for f in os.listdir(dir)])
def path_leaf(path):
'''
Source: http://stackoverflow.com/questions/8384737/python-extract-file-name-from-path-no-matter-what-the-os-path-format
'''
head, tail = ntpath.split(path)
return tail or ntpath.basename(head)
def bky_count_blocks(bky_filename):
return open(bky_filename).read().count('<block ')
def aia_count_blocks(aia_filename):
'''
Count blocks in an AIA project
'''
# unzip
temp_folder = 'temp_aia'
unzip(aia_filename, temp_folder)
# Build path to .bky files, which contains all blocks for each screen of the AI2 project
bky_files_path = os.path.join(temp_folder, 'src', 'appinventor', )
for i in range(6): bky_files_path = list_subdirectories(bky_files_path)[0] # walk inside...
#print(bky_files_path)
# Count
total_blocks_count = 0
bky_filenames = glob.glob(os.path.join(bky_files_path, '*.bky'))
for bky_filename in bky_filenames :
bky_block_count = bky_count_blocks(bky_filename)
print('Screen {0} contains {1} blocks'.format(path_leaf(bky_filename), bky_block_count))
total_blocks_count += bky_block_count
print('The AIA project {0} contains {1} blocks spread across {2} screens.'.format(aia_filename, total_blocks_count, len(bky_filenames)))
# Clean temp files
shutil.rmtree(temp_folder)
def main():
'''
This is the main function
'''
aia_filename = 'test.aia'
aia_count_blocks(aia_filename)
if __name__ == "__main__":
main()
#cProfile.run('main()') # if you want to do some profiling
Outputs:
Screen address.bky contains 42 blocks
Screen edit.bky contains 265 blocks
Screen list.bky contains 233 blocks
Screen logic.bky contains 954 blocks
Screen plan.bky contains 70 blocks
Screen table1.bky contains 206 blocks
Screen table2.bky contains 157 blocks
Screen Screen1.bky contains 16 blocks
The AIA project test.aia contains 1943 blocks spread across 8 screens.
There could be a lot of improvements such avoiding counting disabled blocks, counting by block types, etc.