def save_program (user): body = request.json if not type_check (body, 'dict'): return 'body must be an object', 400 if not object_check (body, 'code', 'str'): return 'code must be a string', 400 if not object_check (body, 'name', 'str'): return 'name must be a string', 400 if not object_check (body, 'level', 'int'): return 'level must be an integer', 400 if 'adventure_name' in body: if not object_check (body, 'adventure_name', 'str'): return 'if present, adventure_name must be a string', 400 # We execute the saved program to see if it would generate an error or not error = None try: hedy_errors = TRANSLATIONS.get_translations(requested_lang(), 'HedyErrorMessages') result = hedy.transpile(body ['code'], body ['level']) except hedy.HedyException as E: error_template = hedy_errors[E.error_code] error = error_template.format(**E.arguments) except Exception as E: error = str(E) name = body ['name'] # If name ends with (N) or (NN), we strip them since it's very likely these addenda were added by our server to avoid overwriting existing programs. name = re.sub (' \(\d+\)$', '', name) # We check if a program with a name `xyz` exists in the database for the username. If it does, we exist whether `xyz (1)` exists, until we find a program `xyz (NN)` that doesn't exist yet. # It'd be ideal to search by username & program name, but since DynamoDB doesn't allow searching for two indexes at the same time, this would require to create a special index to that effect, which is cumbersome. # For now, we bring all existing programs for the user and then search within them for repeated names. existing = db_get_many ('programs', {'username': user ['username']}, True) name_counter = 0 for program in existing: if re.match ('^' + re.escape (name) + '( \(\d+\))*', program ['name']): name_counter = name_counter + 1 if name_counter: name = name + ' (' + str (name_counter) + ')' stored_program = { 'id': uuid.uuid4().hex, 'session': session_id(), 'date': timems (), 'lang': requested_lang(), 'version': version(), 'level': body ['level'], 'code': body ['code'], 'name': name, 'server_error': error, 'username': user ['username'] } if 'adventure_name' in body: stored_program ['adventure_name'] = body ['adventure_name'] db_set('programs', stored_program) program_count = 0 if 'program_count' in user: program_count = user ['program_count'] db_set('users', {'username': user ['username'], 'program_count': program_count + 1}) return jsonify({'name': name})
def test_transpile_ask(self): result = hedy.transpile("antwoord is ask 'wat is je lievelingskleur?'", self.level) expected = "antwoord = input('wat is je lievelingskleur?')" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle)
def test_print_with_var(self): result = hedy.transpile("naam is Hedy\nprint('ik heet' naam)", 19) self.assertEqual("naam = 'Hedy'\nprint('ik heet'+str(naam))", result)
def test_transpile_ask(self): result = hedy.transpile( "antwoord is input('wat is je lievelingskleur?')", 17) self.assertEqual(result, "antwoord = input('wat is je lievelingskleur?')")
def test_print_with_var(self): result = hedy.transpile("naam is Hedy\nprint 'ik heet' naam", self.level) expected = "naam = 'Hedy'\nprint('ik heet'+str(naam))" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle)
def test_print(self): result = hedy.transpile("print Hallo welkom bij Hedy!", self.level) expected = "print('Hallo welkom bij Hedy!')" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle) self.assertEqual('Hallo welkom bij Hedy!', self.run_code(result))
def parse(): body = request.json if not body: return "body must be an object", 400 if 'code' not in body: return "body.code must be a string", 400 if 'level' not in body: return "body.level must be a string", 400 if 'adventure_name' in body and not isinstance(body['adventure_name'], str): return "if present, body.adventure_name must be a string", 400 code = body['code'] level = int(body['level']) # Language should come principally from the request body, # but we'll fall back to browser default if it's missing for whatever # reason. lang = body.get('lang', requested_lang()) # true if kid enabled the read aloud option read_aloud = body.get('read_aloud', False) response = {} username = current_user(request)['username'] or None querylog.log_value(level=level, lang=lang, session_id=session_id(), username=username) try: hedy_errors = TRANSLATIONS.get_translations(lang, 'HedyErrorMessages') with querylog.log_time('transpile'): transpile_result = hedy.transpile(code, level) python_code = transpile_result.code has_turtle = transpile_result.has_turtle response['has_turtle'] = has_turtle if has_turtle: response["Code"] = textwrap.dedent("""\ # coding=utf8 import random, time, turtle t = turtle.Turtle() t.hideturtle() t.speed(0) t.penup() t.goto(50,100) t.showturtle() t.pendown() t.speed(3) """) + python_code else: response["Code"] = "# coding=utf8\nimport random\n" + python_code except hedy.InvalidSpaceException as ex: traceback.print_exc() response = invalid_space_error_to_response(ex, hedy_errors) except hedy.ParseException as ex: traceback.print_exc() response = parse_error_to_response(ex, hedy_errors) except hedy.HedyException as ex: traceback.print_exc() response = hedy_error_to_response(ex, hedy_errors) except Exception as E: traceback.print_exc() print(f"error transpiling {code}") response["Error"] = str(E) querylog.log_value(server_error=response.get('Error')) parse_logger.log({ 'session': session_id(), 'date': str(datetime.datetime.now()), 'level': level, 'lang': lang, 'code': code, 'server_error': response.get('Error'), 'version': version(), 'username': username, 'read_aloud': read_aloud, 'is_test': 1 if os.getenv('IS_TEST_ENV') else None, 'adventure_name': body.get('adventure_name', None) }) return jsonify(response)
def test_echo_without_argument(self): result = hedy.transpile("ask wat?\necho", self.level) expected = "answer = input('wat?')\nprint(answer)" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle)
def test_forward(self): result = hedy.transpile("forward 50", self.level) expected = textwrap.dedent("""\ t.forward(50) time.sleep(0.1)""") self.assertEqual(expected, result.code)
def test_print_multiple_lines(self): result = hedy.transpile("print Hallo welkom bij Hedy\nprint Mooi hoor", self.level) expected = "print('Hallo welkom bij Hedy')\nprint('Mooi hoor')" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle)
def test_ask_Spanish(self): result = hedy.transpile("ask ask Cuál es tu color favorito?", self.level) expected = "answer = input('ask Cuál es tu color favorito?')" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle)
def test_transpile_print_level_2(self): with self.assertRaises(Exception) as context: result = hedy.transpile("print felienne 123", 3) self.assertEqual('Unquoted Text', context.exception.args[0] ) # hier moet nog we een andere foutmelding komen!
def test_transpile_other(self): with self.assertRaises(Exception) as context: result = hedy.transpile("abc felienne 123", 3) self.assertEqual(str(context.exception), 'Invalid')
def test_simple_calculation_without_space(self): code = "nummer is 4+5" result = hedy.transpile(code, 6) expected = "nummer = int(4) + int(5)" self.assertEqual(expected, result)
def test_incomplete_gives_Incomplete(self): with self.assertRaises(hedy.IncompleteCommandException) as context: result = hedy.transpile("print", self.level) self.assertEqual('Incomplete', context.exception.error_code)
def test_turn_no_args(self): result = hedy.transpile("turn", self.level) expected = textwrap.dedent("""\ t.right(90)""") self.assertEqual(expected, result.code) self.assertEqual(True, result.has_turtle)
def test_incomplete_on_line_2_gives_Incomplete(self): with self.assertRaises(hedy.IncompleteCommandException) as context: result = hedy.transpile("print lalalala\nprint", self.level) self.assertEqual('Incomplete', context.exception.error_code) self.assertEqual( 'print', str(context.exception.arguments['incomplete_command']))
def test_one_turn_left(self): result = hedy.transpile("turn left", self.level) expected = textwrap.dedent("""\ t.left(90)""") self.assertEqual(expected, result.code) self.assertEqual(True, result.has_turtle)
def test_identifies_backtick_inside_conditional(self): self.assertRaises(hedy.UnquotedTextException, lambda: hedy.transpile("if 1 is 1 print `yay!` else print `nay`", self.level))
def test_lines_with_spaces_gives_invalid(self): with self.assertRaises(hedy.InvalidSpaceException) as context: result = hedy.transpile( " print Hallo welkom bij Hedy!\n print Hallo welkom bij Hedy!", self.level) self.assertEqual('Invalid Space', context.exception.error_code)
def parse(): body = request.json if not body: return "body must be an object", 400 if 'code' not in body: return "body.code must be a string", 400 if 'level' not in body: return "body.level must be a string", 400 code = body['code'] level = int(body['level']) # Language should come principally from the request body, # but we'll fall back to browser default if it's missing for whatever # reason. lang = body.get('lang', requested_lang()) # For debugging print(f"got code {code}") response = {} username = current_user(request)['username'] or None # Check if user sent code if not code: response["Error"] = "no code found, please send code." # is so, parse else: try: hedy_errors = TRANSLATIONS.get_translations( lang, 'HedyErrorMessages') result = hedy.transpile(code, level) response["Code"] = "# coding=utf8\nimport random\n" + result except hedy.HedyException as E: # some 'errors' can be fixed, for these we throw an exception, but also # return fixed code, so it can be ran if E.args[0] == "Invalid Space": error_template = hedy_errors[E.error_code] response[ "Code"] = "# coding=utf8\n" + E.arguments['fixed_code'] response["Warning"] = error_template.format(**E.arguments) elif E.args[0] == "Parse": error_template = hedy_errors[E.error_code] # Localize the names of characters if 'character_found' in E.arguments: E.arguments['character_found'] = hedy_errors[ E.arguments['character_found']] response["Error"] = error_template.format(**E.arguments) elif E.args[0] == "Unquoted Text": error_template = hedy_errors[E.error_code] response["Error"] = error_template.format(**E.arguments) else: error_template = hedy_errors[E.error_code] response["Error"] = error_template.format(**E.arguments) except Exception as E: print(f"error transpiling {code}") response["Error"] = str(E) logger.log({ 'session': session_id(), 'date': str(datetime.datetime.now()), 'level': level, 'lang': lang, 'code': code, 'server_error': response.get('Error'), 'version': version(), 'username': username, 'is_test': 1 if os.getenv('IS_TEST_ENV') else None }) return jsonify(response)
def test_word_plus_period_gives_invalid(self): with self.assertRaises(hedy.InvalidCommandException) as context: result = hedy.transpile("word.", self.level) self.assertEqual('Invalid', context.exception.error_code)
def test_print(self): result = hedy.transpile("print 'ik heet'", self.level) expected = "print('ik heet')" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle)
def test_empty_gives_exception(self): with self.assertRaises(hedy.EmptyProgramException) as context: result = hedy.transpile("", self.level)
def test_print_with_calc_no_spaces(self): result = hedy.transpile("print '5 keer 5 is ' 5*5", self.level) expected = "print('5 keer 5 is '+str(int(5) * int(5)))" self.assertEqual(expected, result.code) self.assertEqual(False, result.has_turtle)
def test_non_keyword_gives_Invalid(self): with self.assertRaises(hedy.InvalidCommandException) as context: result = hedy.transpile("groen", self.level) self.assertEqual('Invalid', context.exception.error_code)
def test_print(self): result = hedy.transpile("print('ik heet')", 19) self.assertEqual("print('ik heet')", result)
def test_lonely_echo_gives_LonelyEcho(self): code = "echo wat dan?" with self.assertRaises(hedy.LonelyEchoException) as context: result = hedy.transpile(code, self.level) self.assertEqual('Lonely Echo', context.exception.error_code)
def test_print_with_calc_no_spaces(self): result = hedy.transpile("print('5 keer 5 is ' 5*5)", 19) self.assertEqual("print('5 keer 5 is '+str(int(5) * int(5)))", result)
def test_transpile_ask(self): result = hedy.transpile("kleur is ask wat is je lievelingskleur?", 2) self.assertEqual( result, "import random\nkleur = input('wat is je lievelingskleur'+'?')")