1""" 2 Flask server for hosting Lua Interpreter tool. 3""" 4 5import ctypes 6import json 7import os 8import flask 9 10app = flask.Flask(__name__) 11lua_lib = ctypes.cdll.LoadLibrary('./liblua_engine.so') 12lua_lib.NewLuaEngine.argtypes = None 13lua_lib.NewLuaEngine.restype = ctypes.c_void_p 14lua_lib.ExecuteScript.argtypes = [ 15 ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, 16 ctypes.c_char_p 17] 18lua_lib.ExecuteScript.restype = ctypes.c_void_p 19engine = lua_lib.NewLuaEngine() 20 21 22class LuaOutput(ctypes.Structure): 23 """Python wrapper class around LuaOutput struct as defined in lua_engine.h. 24 """ 25 _fields_ = [('output', ctypes.POINTER(ctypes.c_char_p)), 26 ('size', ctypes.c_int), ('saved_state', ctypes.c_char_p)] 27 28 29@app.route('/') 30def index(): 31 """Renders the main page of the tool. 32 33 Returns: 34 A JSON response with a string of the rendered index.html page. 35 """ 36 return flask.render_template('index.html') 37 38 39@app.route('/get_published_data_file_names_and_content', methods=['POST']) 40def get_published_data_file_names_and_content(): 41 """Returns the list of all the JSON file names under the data 42 directory without their file extensions. Also returns the 43 JSON of each published data file under the data directory as a string. 44 45 Returns: 46 A JSON response with file names under the key "file_names" and each 47 of the published data strings under the key of their file name. 48 """ 49 file_path = os.path.join(os.path.dirname(__file__), "data") 50 all_json_files = filter(lambda file: file.endswith('.json'), 51 os.listdir(file_path)) 52 file_names = list( 53 # lamda function leverages splitext, which produces a tuple of 54 # the file name and the extension. 55 map(lambda file: os.path.splitext(file)[0], all_json_files)) 56 57 response = {"file_names": file_names} 58 59 for file_name in file_names: 60 json_file_path = os.path.join(file_path, file_name + '.json') 61 json_file = open(json_file_path) 62 response[file_name] = json.dumps(json.load(json_file), indent=2) 63 json_file.close() 64 65 return response 66 67 68@app.route('/execute_script', methods=['POST']) 69def execute_script(): 70 """Executes the Lua script from the request 71 re-rendering the home page with the output. 72 73 Returns: 74 A JSON response containing the string of the rendered index.html 75 page with output, script, published data, and the saved state specified. 76 """ 77 script = flask.request.form['script'] 78 function_name = flask.request.form['function-name'] 79 published_data = flask.request.form['published-data'] 80 saved_state = flask.request.form['saved-state'] 81 # ctypes requires that strings are encoded to bytes 82 lua_output = LuaOutput.from_address( 83 lua_lib.ExecuteScript(engine, script.encode(), function_name.encode(), 84 published_data.encode(), saved_state.encode())) 85 86 # ctypes encodes strings as bytes so they must be decoded back to strings. 87 decoded_output = [] 88 for i in range(lua_output.size): 89 decoded_output.append(prettify_json(lua_output.output[i].decode())) 90 91 new_saved_state = prettify_json( 92 ctypes.string_at(lua_output.saved_state).decode()) 93 saved_state = new_saved_state if new_saved_state else saved_state 94 95 lua_lib.FreeLuaOutput(ctypes.byref(lua_output)) 96 97 return flask.render_template('index.html', 98 script=script, 99 output=decoded_output, 100 function_name=function_name, 101 published_data=published_data, 102 saved_state=saved_state) 103 104 105def prettify_json(string): 106 """Prettifies the string if it represents a JSON with an indent of 2. 107 108 Args: 109 string (str): String to prettify 110 111 Returns: 112 A string of the formatted JSON. If the string does not represent a 113 JSON, the string is returned back with no changes. 114 """ 115 try: 116 # If the string cannot be loaded into a JSON, 117 # json.loads throws a JSONDecodeError exception. 118 json_object = json.loads(string) 119 json_formatted_str = json.dumps(json_object, indent=2) 120 return json_formatted_str 121 except json.JSONDecodeError as e: 122 return string 123 124 125if __name__ == '__main__': 126 app.run()