diff --git a/README.md b/README.md index ce92087..8823d0b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,2 @@ -# appstore +# Backendless search and website generator from activity bundles prototype -App store for Python 3 activities \ No newline at end of file diff --git a/generator/GeneralFunctions/DataStructureManupulations.py b/generator/GeneralFunctions/DataStructureManupulations.py new file mode 100644 index 0000000..5187a05 --- /dev/null +++ b/generator/GeneralFunctions/DataStructureManupulations.py @@ -0,0 +1,106 @@ +from sys import platform as OperatingSystem + +Quotes = '"\'' + +if OperatingSystem == "win32": + PathSlash = '\\' +else: + PathSlash = '/' +FileProtocol = "file:" + 2*PathSlash + +def ConvertToStandardPathFormat(Path): + """ Example, + Input: '"file:///some/path/somefile.extension" + Output: /some/path/somefile.extension + """ + Path = Path.strip(Quotes) + if Path.startswith(FileProtocol): + Path = Path[len(FileProtocol):] + return Path + +def GetTextAfter(Text, ReadlinesTextFile): + for Lines in range(len(ReadlinesTextFile)): + Line = ReadlinesTextFile[Lines].strip('\n') + if Line.startswith(Text): + return Line[len(Text):] + return '' + +def SingleQuoteString(String): + if len(String) > 0: + if String[0] != '\'' or String[-1] != '\'': + String = '\'' + String + '\'' + return String + +def DoubleQuoteString(String): + if len(String) > 0: + if String[0] != '"' or String[-1] != '"': + String = '"' + String + '"' + return String + +def ListIntoString(List, QuoteItems=0, Seprator=' '): + if QuoteItems == 2: + for i in range(len(List)): + Quoteditem = DoubleQuoteString(List[i]) + List[i] = Quoteditem + elif QuoteItems == 1: + for i in range(len(List)): + Quoteditem = SingleQuoteString(List[i]) + List[i] = Quoteditem + Stringoflist = (Seprator).join(List) + return Stringoflist + +# strip=0 => remove both ' & ", 1 => remove ', 2 => remove " +def UnquoteString(String, strip=0): + while True: + if ( + strip != 2 + and String.startswith('"') + and String.endswith('"')): + String = String.strip('"') + elif ( + strip != 1 + and String.startswith("'") + and String.endswith("'")): + String = String.strip("'") + else: + break + return String + +def StandardVariableName(Variable): + Variable = Variable.casefold() + Variable = Variable.replace('_', '').replace(' ', '') + return Variable + +#def DictionaryToJsonStr(Dict, BaseIndentation=0): + #BI = '\t'*BaseIndentation + #JsonStr = BI+'{\n' + #for k, v in Dict.items(): + #JsonStr += BI+'\t"'+k+'" : "'+v+'",\n' + #JsonStr = JsonStr[:-2] + #JsonStr += '\n'+BI+'}' + #return JsonStr + +def StringToKeyValuePair(String, Seprator): + SepratorAt = String.find(Seprator) + if SepratorAt >= 0: + Key = String[:SepratorAt] + Value = String[SepratorAt+1:] + return Key, Value + else: + return "", String + +def FormatStrForDictinary(String): + String = String.strip(" \n\r") + return UnquoteString(String) + +def StrListToDictionary( + List, + Seprator = '=', + FormatFunction = FormatStrForDictinary): + Dictionary = {} + for i in List: + k, v = StringToKeyValuePair(i, Seprator) + k, v = FormatFunction(k), FormatFunction(v) + if len(k) > 0: + Dictionary[k] = v + return Dictionary diff --git a/generator/GeneralFunctions/InputOutput.py b/generator/GeneralFunctions/InputOutput.py new file mode 100644 index 0000000..a503596 --- /dev/null +++ b/generator/GeneralFunctions/InputOutput.py @@ -0,0 +1,29 @@ +from os.path import isfile as DoesFileExist + +from .DataStructureManupulations import ConvertToStandardPathFormat + +def ReadTextFile(FilePath): + FilePath = ConvertToStandardPathFormat(FilePath) + if DoesFileExist(FilePath) is True: + File = open(FilePath) + ReadFile = File.read() + File.close() + return ReadFile + else: + return '' + +def ReadlinesTextFile(FilePath): + String = ReadTextFile(FilePath) + return String.split('\n') + +def WriteTextFiles(FilePath, Text): + if type(Text) != str: + Text = '\n'.join(Text) + File = open(FilePath, 'w') + File.write(Text) + File.close() + +def WriteBinaryToFile(Filepath, Data): + File=open(Filepath, 'wb') + File.write(Data) + File.close() diff --git a/generator/GeneralFunctions/Network.py b/generator/GeneralFunctions/Network.py new file mode 100644 index 0000000..c506405 --- /dev/null +++ b/generator/GeneralFunctions/Network.py @@ -0,0 +1,8 @@ +import ssl +from urllib.request import urlopen + +HttpsContext = ssl.create_default_context() + +def Download(Url): + return urlopen(Url, context=HttpsContext).read() + diff --git a/generator/GeneralFunctions/OS.py b/generator/GeneralFunctions/OS.py new file mode 100644 index 0000000..eb250db --- /dev/null +++ b/generator/GeneralFunctions/OS.py @@ -0,0 +1,16 @@ +import os + +def CallFuncInDir(Directory, Function, *args, **kwArgs): + CurrentDir = os.getcwd() + os.chdir(Directory) + Function(*args, **kwArgs) + os.chdir(CurrentDir) + +# return True if operation succesful and False if failed +def CreateDir(Directory): + if not os.path.isfile(Directory): + if not os.path.isdir(Directory): + os.mkdir(Directory) + return True + else: + return False diff --git a/generator/GeneralFunctions/__init__.py b/generator/GeneralFunctions/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/generator/GeneralFunctions/__init__.py @@ -0,0 +1 @@ + diff --git a/generator/main.py b/generator/main.py new file mode 100644 index 0000000..877cbe8 --- /dev/null +++ b/generator/main.py @@ -0,0 +1,288 @@ +#!/usr/bin/python3 + +""" Run as: +python3 thisscript.py "/bundles/directoty" "/website/template/root/directory/ +All sub-directories of bundles directory will be scanned for activity +bundles i.e. .xo files. +""" + +""" FIXME: paths hard coded unix style & most likely will not work on winodws. +Use code written for IMM to handle it. +""" + +import glob +import json +import os +import shutil +import sys +from urllib.parse import quote as strToHtmlFmt +import zipfile + +from GeneralFunctions.DataStructureManupulations import ( + GetTextAfter, + UnquoteString, + StrListToDictionary + ) +from GeneralFunctions.InputOutput import ( + WriteTextFiles, + WriteBinaryToFile + ) +from GeneralFunctions.OS import ( + CallFuncInDir, + CreateDir + ) + +class extractData: + + def findInfoFiles(self, bundle): + infoFiles = [] + for File in bundle.namelist(): + if File.endswith("activity/activity.info"): + infoFiles.append(File) + return infoFiles + + def copyBundle(self, bundleSrc, activityName): + CallFuncInDir( + self.websiteDir, + shutil.copy2, + self.bundlesDir+bundleSrc, + "bundles/"+activityName+".xo" + ) + + def createDirectories(self): + assert(CreateDir("app")) + assert(CreateDir("icons")) + assert(CreateDir("bundles")) + assert(CreateDir("js")) + + def extractActivityInfo(self, infoFilePath, zipFile): + infoList, infoDict = [], {} + infoList = zipFile.read( + infoFilePath).decode("utf-8").split('\n') + return StrListToDictionary(infoList) + + def extractInfoAndIconFromBundles(self): + for bundlePath in self.activityBundles: + bundle = zipfile.ZipFile(bundlePath, "r") + + infoFiles = self.findInfoFiles(bundle) + if len(infoFiles) != 1: + self.bundlesNotExactlyOneInfoFile.append(bundlePath) + else: + + infoDict = self.extractActivityInfo(infoFiles[0], bundle) + self.bundlesInfoList.append(infoDict) + + # FIXME: create seprate function for it + # extract and copy icon + activityName = infoDict.get("name") + if type(activityName) == str: + iconRelativePath = infoDict.get("icon") + if type(iconRelativePath) == str: + iconFolder = infoFiles[0][:infoFiles[0].rfind("/")+1] + iconAbsolutePath = ( + iconFolder+iconRelativePath+".svg") + if iconAbsolutePath in bundle.namelist(): + icon = bundle.read(iconAbsolutePath) + iconPath = ( + "icons/"+ + activityName + +".svg" + ) + CallFuncInDir( + self.websiteDir, + WriteBinaryToFile, + iconPath, + icon + ) + else: + # Conitnue without icon since non-fatal error + self.iconErroredBundles.append(bundlePath) + + bundle.close() + # FIXME: uncomment below function. + # Disabled sometime during devlopment as time consuming + self.copyBundle(bundlePath, activityName) + bundle.close() + + def generateAppsHtmlPages(self): + iconsDir = "../icons/" + bundlesDir = "../bundles/" + for appInfo in self.indexDictList: + pathName = strToHtmlFmt(appInfo["name"], safe='') + + html = ( + '\n\n
\n'+appInfo["summary"]+ + '
\n'+ + appInfo["description"]+'
\n