diff --git a/.gitignore b/.gitignore index 659cf51c..7b6cfdd7 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,4 @@ homepage/static/css/**/*.css translations/codes.json .DS_Store +translations/chunks/ diff --git a/tests/golden_files/zh/test_transcript.json b/tests/golden_files/zh/test_transcript.json index e032ca5d..1ebbd701 100644 --- a/tests/golden_files/zh/test_transcript.json +++ b/tests/golden_files/zh/test_transcript.json @@ -1,11 +1,11 @@ [ { - "page": "Introducing The Shell", + "page": "\u4ecb\u7ecd Shell", "program": [ "'literally anything'" ], "response": { - "message": "

Awesome, you're trying out your own experiments!\nThat's a great sign. Keep it up.\nJust letting you know that you do need to eventually type 1+2 for the book to move forward.

", + "message": "

\u5f88\u68d2\uff0c\u4f60\u5728\u5c1d\u8bd5\u81ea\u5df1\u7684\u5b9e\u9a8c\uff01\n\u8fd9\u771f\u662f\u4e2a\u597d\u5146\u5934\u3002\u7ee7\u7eed\u4fdd\u6301\u3002\n\u53ea\u662f\u8ba9\u4f60\u77e5\u9053\uff0c\u4f60\u6700\u7ec8\u9700\u8981\u8f93\u5165 1+2 \u624d\u80fd\u7ee7\u7eed\u8fdb\u884c\u3002

", "passed": false, "result": [ { @@ -17,12 +17,12 @@ "step": "first_expression" }, { - "page": "Introducing The Shell", + "get_solution": "program", + "page": "\u4ecb\u7ecd Shell", "program": [ "1+2" ], "response": { - "message": "", "passed": true, "result": [ { @@ -34,16 +34,17 @@ "step": "first_expression" }, { - "page": "Introducing The Shell", + "page": "\u4ecb\u7ecd Shell", "program": [ "3 x 4" ], "response": { - "message": "

I see an 'x'. If you're trying to multiply, use an asterisk, e.g:

\n
3 * 4\n
", + "message": "

\u6211\u770b\u5230\u4e00\u4e2a 'x'\u3002\u5982\u679c\u4f60\u60f3\u8981\u4e58\u6cd5\uff0c\u8bf7\u4f7f\u7528\u661f\u53f7\uff0c\u4f8b\u5982\uff1a

\n
3 * 4\n
", "passed": false, "result": [ { - "text": " 3 x 4\n ^\nSyntaxError: invalid syntax\nat line 1\n\nA `SyntaxError` occurs when Python cannot understand your code.\n\nCurrently, I cannot guess the likely cause of this error.\nTry to examine closely the line indicated as well as the line\nimmediately above to see if you can identify some misspelled\nword, or missing symbols, like (, ), [, ], :, etc.\n\nUnless your code uses type annotations, which are beyond our scope,\nif you think that this is something which should be handled\nby friendly, please report this case to\nhttps://github.com/aroberge/friendly/issues\n\n\n\n", + "friendly": "

A SyntaxError occurs when Python cannot understand your code.

\n

Currently, I cannot guess the likely cause of this error.\nTry to examine closely the line indicated as well as the line\nimmediately above to see if you can identify some misspelled\nword, or missing symbols, like (, ), [, ], :, etc.

\n

Unless your code uses type annotations, which are beyond our scope,\nif you think that this is something which should be handled\nby friendly, please report this case to\nhttps://github.com/friendly-traceback/friendly-traceback/issues

", + "text": " 3 x 4\n ^\nSyntaxError: invalid syntax\n\u5728\u884c 1\n", "type": "syntax_error" } ] @@ -51,12 +52,12 @@ "step": "more_calculation" }, { - "page": "Introducing The Shell", + "get_solution": "program", + "page": "\u4ecb\u7ecd Shell", "program": [ "5 - 6" ], "response": { - "message": "", "passed": true, "result": [ { @@ -68,12 +69,12 @@ "step": "more_calculation" }, { - "page": "Introducing Strings", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5b57\u7b26\u4e32", "program": [ "'hello'" ], "response": { - "message": "", "passed": true, "result": [ { @@ -85,12 +86,12 @@ "step": "hello_string" }, { - "page": "Adding Strings", + "get_solution": "program", + "page": "\u6dfb\u52a0\u5b57\u7b26\u4e32", "program": [ "'hello' + 'world'" ], "response": { - "message": "", "passed": true, "result": [ { @@ -102,12 +103,12 @@ "step": "hello_world_concat" }, { - "page": "Adding Strings", + "page": "\u6dfb\u52a0\u5b57\u7b26\u4e32", "program": [ "'hello world'" ], "response": { - "message": "

You must still add two or more strings together.

", + "message": "

\u4f60\u4ecd\u7136\u5fc5\u987b\u5c06\u4e24\u4e2a\u6216\u66f4\u591a\u5b57\u7b26\u4e32\u76f8\u52a0\u3002

", "passed": false, "result": [ { @@ -119,15 +120,12 @@ "step": "hello_world_space" }, { - "get_solution": [ - "'hello ' + 'world'" - ], - "page": "Adding Strings", + "get_solution": "program", + "page": "\u6dfb\u52a0\u5b57\u7b26\u4e32", "program": [ "'hello ' + 'world'" ], "response": { - "message": "", "passed": true, "result": [ { @@ -139,36 +137,36 @@ "step": "hello_world_space" }, { - "page": "Introducing Variables", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u53d8\u91cf", "program": [ "word = 'Hello'" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "word_assign" }, { - "page": "Introducing Variables", + "page": "\u4ecb\u7ecd\u53d8\u91cf", "program": [ "word = 2" ], "response": { - "message": "

Oops, you need to set word = 'Hello' before we can continue.

", + "message": "

\u54ce\u5440\uff0c\u4f60\u9700\u8981\u5148\u8bbe\u7f6e word = 'Hello'\uff0c\u624d\u80fd\u7ee7\u7eed\u3002

", "passed": false, "result": [] }, "step": "word_check" }, { - "page": "Introducing Variables", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u53d8\u91cf", "program": [ "word" ], "response": { - "message": "", "passed": true, "result": [ { @@ -180,12 +178,12 @@ "step": "word_check" }, { - "page": "Introducing Variables", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u53d8\u91cf", "program": [ "'word'" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "'word'", @@ -194,7 +192,7 @@ "'word'", "Hello", "'Hello'", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -207,21 +205,21 @@ "step": "word_string_check" }, { - "page": "Introducing Variables", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u53d8\u91cf", "program": [ "sunshine" ], "response": { - "message": "", "passed": true, "prediction": { - "answer": "Error", + "answer": "\u9519\u8bef", "choices": [ "sunshine", "'sunshine'", "Hello", "'Hello'", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -235,11 +233,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 1, "lines": [ { - "content": "sunshine", "is_current": true, "lineno": 1, + "text": "sunshine", "type": "line" } ], @@ -254,7 +254,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 1, in ", + " File \"/my_program.py\", line 1, in ", "--> 1 | sunshine", " ^^^^^^^^", "", @@ -267,72 +267,72 @@ "step": "sunshine_undefined_check" }, { - "page": "Using Variables and print()", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "foo = 3" ], "response": { - "message": "

Put your_name before the = to create a variable called your_name.

", + "message": "

\u5728 = \u524d\u9762\u653e\u4e0a your_name \u6765\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a your_name \u7684\u53d8\u91cf\u3002

", "passed": false, "result": [] }, "step": "name_assign" }, { - "page": "Using Variables and print()", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "your_name = ''" ], "response": { - "message": "

For this exercise, choose a non-empty string

", + "message": "

\u5bf9\u4e8e\u8fd9\u4e2a\u7ec3\u4e60\uff0c\u9009\u62e9\u4e00\u4e2a\u975e\u7a7a\u5b57\u7b26\u4e32\u3002

", "passed": false, "result": [] }, "step": "name_assign" }, { - "page": "Using Variables and print()", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "your_name = 3" ], "response": { - "message": "

You've got the your_name = part right, now put a string (use quotes) on the right of the =.

", + "message": "

\u4f60\u5df2\u7ecf\u628a your_name = \u90e8\u5206\u5199\u5bf9\u4e86\uff0c\u73b0\u5728\u5728 = \u7684\u53f3\u8fb9\u653e\u4e00\u4e2a\u5b57\u7b26\u4e32\uff08\u4f7f\u7528\u5f15\u53f7\uff09\u3002

", "passed": false, "result": [] }, "step": "name_assign" }, { - "page": "Using Variables and print()", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "your_name = ' Alex'" ], "response": { - "message": "

For this exercise, choose a name that doesn't start with a space.

", + "message": "

\u5bf9\u4e8e\u8fd9\u4e2a\u7ec3\u4e60\uff0c\u9009\u62e9\u4e00\u4e2a\u4e0d\u4ee5\u7a7a\u683c\u5f00\u5934\u7684\u540d\u5b57\u3002

", "passed": false, "result": [] }, "step": "name_assign" }, { - "page": "Using Variables and print()", + "get_solution": "program", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "your_name = 'Alex'" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "name_assign" }, { - "page": "Using Variables and print()", + "get_solution": "program", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "'Hello ' + your_name" ], "response": { - "message": "", "passed": true, "result": [ { @@ -344,24 +344,24 @@ "step": "hello_plus_name" }, { - "page": "Using Variables and print()", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "word = 2" ], "response": { - "message": "

Oops, you need to set word = 'Hello' before we can continue.

", + "message": "

\u54ce\u5440\uff0c\u4f60\u9700\u8981\u5148\u8bbe\u7f6e word = 'Hello'\uff0c\u624d\u80fd\u7ee7\u7eed\u3002

", "passed": false, "result": [] }, "step": "word_plus_name" }, { - "page": "Using Variables and print()", + "get_solution": "program", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "word + your_name" ], "response": { - "message": "", "passed": true, "result": [ { @@ -373,27 +373,24 @@ "step": "word_plus_name" }, { - "page": "Using Variables and print()", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "word = 2" ], "response": { - "message": "

Oops, you need to set word = 'Hello' before we can continue.

", + "message": "

\u54ce\u5440\uff0c\u4f60\u9700\u8981\u5148\u8bbe\u7f6e word = 'Hello'\uff0c\u624d\u80fd\u7ee7\u7eed\u3002

", "passed": false, "result": [] }, "step": "word_plus_name_with_space" }, { - "get_solution": [ - "word + ' ' + your_name" - ], - "page": "Using Variables and print()", + "get_solution": "program", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "word + ' ' + your_name" ], "response": { - "message": "", "passed": true, "result": [ { @@ -405,24 +402,24 @@ "step": "word_plus_name_with_space" }, { - "page": "Using Variables and print()", + "get_solution": "program", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "word = 'Goodbye'" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "word_assign_goodbye" }, { - "page": "Using Variables and print()", + "get_solution": "program", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "word + ' ' + your_name" ], "response": { - "message": "", "passed": true, "result": [ { @@ -434,12 +431,12 @@ "step": "goodbye_plus_name" }, { - "page": "Using Variables and print()", + "get_solution": "program", + "page": "\u4f7f\u7528\u53d8\u91cf\u548c print()", "program": [ "print(word + ' ' + your_name)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -451,7 +448,8 @@ "step": "first_print" }, { - "page": "Writing Programs", + "get_solution": "program", + "page": "\u7f16\u5199\u7a0b\u5e8f", "program": [ "word = 'Hello'", "name = 'World'", @@ -460,7 +458,6 @@ "print(word + ' ' + name)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -472,7 +469,8 @@ "step": "editor_hello_world" }, { - "page": "Storing Calculations In Variables", + "get_solution": "program", + "page": "\u5728\u53d8\u91cf\u4e2d\u5b58\u50a8\u8ba1\u7b97", "program": [ "word = 'Hello'", "name = 'World'", @@ -480,7 +478,6 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Hello World", @@ -491,7 +488,7 @@ "'Hello' + ' ' + 'World'", "Hello World", "'Hello World'", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -504,7 +501,8 @@ "step": "sentence_equals_word_plus_name" }, { - "page": "Storing Calculations In Variables", + "get_solution": "program", + "page": "\u5728\u53d8\u91cf\u4e2d\u5b58\u50a8\u8ba1\u7b97", "program": [ "word = 'Hello'", "name = 'World'", @@ -514,7 +512,6 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Hello World\nHello World", @@ -522,7 +519,7 @@ "Hello World\nHello World", "Hello World\nGoodbye World", "Goodbye World\nGoodbye World", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -535,13 +532,13 @@ "step": "sentence_doesnt_change" }, { - "page": "Introducing For Loops", + "get_solution": "program", + "page": "\u5f15\u5165 For \u5faa\u73af", "program": [ "name = 'World'", "for character in name: print(character)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -553,17 +550,18 @@ "step": "first_for_loop" }, { - "page": "Indentation", + "get_solution": "program", + "page": "\u7f29\u8fdb", "program": [ "for character in name:", "print(character)" ], "response": { - "message": "", "passed": true, "result": [ { - "text": " print(character)\n ^\nIndentationError: expected an indented block\nat line 2\n\nAn `IndentationError` occurs when a given line of code is\nnot indented (aligned vertically with other lines) as expected.\n\nLine `2` identified above was expected to begin a new indented block.\n\n\n", + "friendly": "

An IndentationError occurs when a given line of code is\nnot indented (aligned vertically with other lines) as expected.

\n

Line 2 identified above was expected to begin a new indented block.

", + "text": " print(character)\n ^^^^^\nIndentationError: expected an indented block after 'for' statement on line 1\n\u5728\u884c 2\n", "type": "syntax_error" } ] @@ -571,7 +569,8 @@ "step": "missing_indentation" }, { - "page": "Indentation", + "get_solution": "program", + "page": "\u7f29\u8fdb", "program": [ "name = 'World'", "", @@ -580,7 +579,6 @@ " print('---')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -592,7 +590,8 @@ "step": "two_indented_lines" }, { - "page": "Indentation", + "get_solution": "program", + "page": "\u7f29\u8fdb", "program": [ "name = 'World'", "", @@ -601,7 +600,6 @@ "print('---')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -613,18 +611,19 @@ "step": "one_indented_line" }, { - "page": "Indentation", + "get_solution": "program", + "page": "\u7f29\u8fdb", "program": [ "for character in name:", " print(character)", " print('---')" ], "response": { - "message": "", "passed": true, "result": [ { - "text": " print('---')\n ^\nIndentationError: unindent does not match any outer indentation level\nat line 3\n\nAn `IndentationError` occurs when a given line of code is\nnot indented (aligned vertically with other lines) as expected.\n\nLine `3` identified above is less indented than expected.\n\n\n", + "friendly": "

An IndentationError occurs when a given line of code is\nnot indented (aligned vertically with other lines) as expected.

\n

Line 3 identified above is less indented than expected.

", + "text": " print('---')\n ^\nIndentationError: unindent does not match any outer indentation level\n\u5728\u884c 3\n", "type": "syntax_error" } ] @@ -636,14 +635,13 @@ "for character in name:", " print('---' + character)" ], - "page": "Basic For Loop Exercises", + "page": "\u57fa\u672c\u7684 for \u5faa\u73af\u7ec3\u4e60", "program": [ "name = 'World'", "for character in name:", " print('---' + character)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -659,14 +657,13 @@ "for _ in name:", " print(name)" ], - "page": "Basic For Loop Exercises", + "page": "\u57fa\u672c\u7684 for \u5faa\u73af\u7ec3\u4e60", "program": [ "name = 'World'", "for _ in name:", " print(name)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -678,7 +675,8 @@ "step": "loop_exercise_2" }, { - "page": "Building Up Strings", + "get_solution": "program", + "page": "\u6784\u5efa\u5b57\u7b26\u4e32", "program": [ "hello = 'Hello'", "print(hello)", @@ -686,7 +684,6 @@ "print(hello)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Hello\nHello!", @@ -694,7 +691,7 @@ "Hello\nHello", "Hello\nHello!", "Hello!\nHello!", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -707,7 +704,8 @@ "step": "hello_plus_equals" }, { - "page": "Building Up Strings", + "get_solution": "program", + "page": "\u6784\u5efa\u5b57\u7b26\u4e32", "program": [ "name = 'World'", "line = '-'", @@ -716,7 +714,6 @@ " print(line)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "-W\n-Wo\n-Wor\n-Worl\n-World", @@ -729,7 +726,7 @@ "-World\n-Worl\n-Wor\n-Wo\n-W", "-World\n-World\n-World\n-World\n-World", "-World\n--World\n---World\n----World\n-----World", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -742,7 +739,8 @@ "step": "name_triangle" }, { - "page": "Building Up Strings", + "get_solution": "program", + "page": "\u6784\u5efa\u5b57\u7b26\u4e32", "program": [ "name = 'World'", "line = '-'", @@ -751,7 +749,6 @@ " line = line + char" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "-\n-W\n-Wo\n-Wor\n-Worl", @@ -759,7 +756,7 @@ "-W\n-Wo\n-Wor\n-Worl\n-World", "-Wo\n-Wor\n-Worl\n-World", "-\n-W\n-Wo\n-Wor\n-Worl", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -772,12 +769,12 @@ "step": "name_triangle_missing_last_line" }, { - "page": "Building Up Strings", + "get_solution": "program", + "page": "\u6784\u5efa\u5b57\u7b26\u4e32", "program": [ "'' + '' + ''" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "''", @@ -791,7 +788,7 @@ "'' '' ''", "' '' '' '", "++", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -810,7 +807,7 @@ " line = line + char", " print(line)" ], - "page": "Building Up Strings", + "page": "\u6784\u5efa\u5b57\u7b26\u4e32", "program": [ "name = 'World'", "line = ''", @@ -819,7 +816,6 @@ " print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -837,7 +833,7 @@ " line = line + char + ' '", " print(line)" ], - "page": "Building Up Strings Exercises", + "page": "\u5b57\u7b26\u4e32\u6784\u5efa\u7ec3\u4e60", "program": [ "name = 'World'", "line = ''", @@ -846,7 +842,6 @@ " print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -864,7 +859,7 @@ " line = char + line", " print(line)" ], - "page": "Building Up Strings Exercises", + "page": "\u5b57\u7b26\u4e32\u6784\u5efa\u7ec3\u4e60", "program": [ "name = 'World'", "line = ''", @@ -873,7 +868,6 @@ " print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -892,7 +886,7 @@ "print(name)", "print(line)" ], - "page": "Building Up Strings Exercises", + "page": "\u5b57\u7b26\u4e32\u6784\u5efa\u7ec3\u4e60", "program": [ "name = 'World'", "line = ''", @@ -902,7 +896,6 @@ "print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -923,7 +916,7 @@ "print('|' + name + '|')", "print(line)" ], - "page": "Building Up Strings Exercises", + "page": "\u5b57\u7b26\u4e32\u6784\u5efa\u7ec3\u4e60", "program": [ "name = 'World'", "line = ''", @@ -935,7 +928,6 @@ "print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -958,7 +950,7 @@ " print(char + spaces + char)", "print(line)" ], - "page": "Building Up Strings Exercises", + "page": "\u5b57\u7b26\u4e32\u6784\u5efa\u7ec3\u4e60", "program": [ "name = 'World'", "line = '+' + name + '+'", @@ -972,7 +964,6 @@ "print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -984,7 +975,7 @@ "step": "name_box_2" }, { - "page": "Building Up Strings Exercises", + "page": "\u5b57\u7b26\u4e32\u6784\u5efa\u7ec3\u4e60", "program": [ "name = 'World'", "spaces = ''", @@ -993,7 +984,7 @@ " print(spaces + char)" ], "response": { - "message": "

Almost there! You have one space too many before each letter.\nMake sure that the first time your loop calls print\nyour variable which will contain the spaces is an empty string.\nCheck the order of your code.

", + "message": "

\u5feb\u5230\u4e86\uff01\u6bcf\u4e2a\u5b57\u6bcd\u524d\u9762\u591a\u4e86\u4e00\u4e2a\u7a7a\u683c\u3002\n\u786e\u4fdd\u4f60\u7684\u5faa\u73af\u7b2c\u4e00\u6b21\u8c03\u7528 print\n\u65f6\uff0c\u5305\u542b\u7a7a\u683c\u7684\u53d8\u91cf\u662f\u4e00\u4e2a\u7a7a\u5b57\u7b26\u4e32\u3002\n\u68c0\u67e5\u4f60\u7684\u4ee3\u7801\u987a\u5e8f\u3002

", "passed": false, "result": [ { @@ -1011,7 +1002,7 @@ " print(spaces + char)", " spaces += ' '" ], - "page": "Building Up Strings Exercises", + "page": "\u5b57\u7b26\u4e32\u6784\u5efa\u7ec3\u4e60", "program": [ "name = 'World'", "spaces = ''", @@ -1020,7 +1011,6 @@ " spaces += ' '" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1032,7 +1022,8 @@ "step": "diagonal_name_bonus_challenge" }, { - "page": "Introducing If Statements", + "get_solution": "program", + "page": "\u4ecb\u7ecd If \u8bed\u53e5", "program": [ "condition = True", "print(condition)", @@ -1040,7 +1031,6 @@ "print(condition)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1052,7 +1042,8 @@ "step": "introducing_booleans" }, { - "page": "Introducing If Statements", + "get_solution": "program", + "page": "\u4ecb\u7ecd If \u8bed\u53e5", "program": [ "if True:", " print('This gets printed')", @@ -1061,7 +1052,6 @@ " print('This does not')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1073,7 +1063,8 @@ "step": "first_if_statements" }, { - "page": "Introducing If Statements", + "get_solution": "program", + "page": "\u4ecb\u7ecd If \u8bed\u53e5", "program": [ "sentence = 'Hello World'", "excited = True", @@ -1082,14 +1073,13 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Hello World!", "choices": [ "Hello World", "Hello World!", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -1102,7 +1092,8 @@ "step": "excited_example" }, { - "page": "Introducing If Statements", + "get_solution": "program", + "page": "\u4ecb\u7ecd If \u8bed\u53e5", "program": [ "sentence = 'Hello World'", "excited = False", @@ -1111,14 +1102,13 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Hello World", "choices": [ "Hello World", "Hello World!", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -1138,7 +1128,7 @@ " sentence += '?'", "print(sentence)" ], - "page": "Introducing If Statements", + "page": "\u4ecb\u7ecd If \u8bed\u53e5", "program": [ "sentence = 'Hello'", "excited = True", @@ -1150,7 +1140,6 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1162,7 +1151,8 @@ "step": "excited_confused_exercise" }, { - "page": "Combining Compound Statements", + "get_solution": "program", + "page": "\u7ec4\u5408\u590d\u5408\u8bed\u53e5", "program": [ "sentence = 'Hello World'", "excited = True", @@ -1176,7 +1166,6 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "H!e!l!l!o! !W!o!r!l!d!", @@ -1190,7 +1179,7 @@ "!Hello World!", "H!e!l!l!o! !W!o!r!l!d!", "!H!e!l!l!o! !W!o!r!l!d", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -1203,7 +1192,8 @@ "step": "for_inside_if" }, { - "page": "Understanding Programs With snoop", + "get_solution": "program", + "page": "\u4f7f\u7528 snoop \u7406\u89e3\u7a0b\u5e8f", "program": [ "sentence = 'Hello World'", "", @@ -1217,7 +1207,6 @@ "print(new_sentence)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "ello World", @@ -1227,7 +1216,7 @@ "Hello Worl", "H", "d", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -1240,7 +1229,8 @@ "step": "print_tail" }, { - "page": "Understanding Programs With snoop", + "get_solution": "program", + "page": "\u4f7f\u7528 snoop \u7406\u89e3\u7a0b\u5e8f", "program": [ "sentence = 'Hello World'", "", @@ -1254,208 +1244,43 @@ "print(new_sentence)" ], "response": { - "message": "", "passed": true, "result": [ { - "text": "", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 1\u001b[0m | sentence \u001b[38;5;197m=\u001b[39m \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mHello World\u001b[39m\u001b[38;5;186m'\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 3\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mFalse\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 4\u001b[0m | new_sentence \u001b[38;5;197m=\u001b[39m \u001b[38;5;186m'\u001b[39m\u001b[38;5;186m'\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mH\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186me\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186me\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186ml\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mel\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mell\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mo\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186m \u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello \u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mW\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello W\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mo\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello Wo\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mr\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello Wor\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186ml\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 1\u001b[0m | sentence \u001b[38;5;204m=\u001b[39m \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mHello World\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 3\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mFalse\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 4\u001b[0m | new_sentence \u001b[38;5;204m=\u001b[39m \u001b[38;5;186m'\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mH\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186me\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186me\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello Worl\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186ml\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mel\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mell\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mo\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186m \u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello \u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186md\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mW\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello W\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mo\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello Wo\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 7\u001b[0m | new_sentence \u001b[38;5;197m+\u001b[39m\u001b[38;5;197m=\u001b[39m char\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mr\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello Wor\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186ml\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello Worl\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello World\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | include \u001b[38;5;197m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m...... char = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186md\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | \u001b[38;5;81mif\u001b[39m include:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 7\u001b[0m | new_sentence \u001b[38;5;204m+\u001b[39m\u001b[38;5;204m=\u001b[39m char\n\u001b[38;5;245m \u001b[0m.............. new_sentence = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mello World\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | include \u001b[38;5;204m=\u001b[39m \u001b[38;5;81mTrue\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;204min\u001b[39m sentence:\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 10\u001b[0m | print(new_sentence)\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mfor\u001b[39m char \u001b[38;5;197min\u001b[39m sentence:\n", + "text": "ello World", "type": "stdout" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 10\u001b[0m | print(new_sentence)\n", + "text": "\n", "type": "stdout" }, { - "text": "ello World\n", - "type": "stdout" + "text": "", + "type": "snoop" } ] }, @@ -1472,7 +1297,7 @@ "", "print(new_sentence)" ], - "page": "Understanding Programs With snoop", + "page": "\u4f7f\u7528 snoop \u7406\u89e3\u7a0b\u5e8f", "program": [ "sentence = 'Hello there'", "include = True", @@ -1485,7 +1310,6 @@ "print(new_sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1497,7 +1321,8 @@ "step": "print_first_character" }, { - "page": "if and else", + "get_solution": "program", + "page": "if \u548c else", "program": [ "condition = True", "if condition:", @@ -1506,7 +1331,6 @@ " print('No')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1518,7 +1342,8 @@ "step": "first_if_else" }, { - "page": "if and else", + "get_solution": "program", + "page": "if \u548c else", "program": [ "condition = False", "if condition:", @@ -1527,7 +1352,6 @@ " print('No')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1539,7 +1363,8 @@ "step": "first_if_else_false" }, { - "page": "if and else", + "get_solution": "program", + "page": "if \u548c else", "program": [ "sentence = 'Hello World'", "excited = True", @@ -1550,7 +1375,6 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1562,7 +1386,8 @@ "step": "if_upper_else_lower" }, { - "page": "if and else", + "get_solution": "program", + "page": "if \u548c else", "program": [ "sentence = 'Hello World'", "excited = False", @@ -1573,7 +1398,6 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1585,7 +1409,8 @@ "step": "if_upper_else_lower_false" }, { - "page": "if and else", + "get_solution": "program", + "page": "if \u548c else", "program": [ "sentence = 'Hello World'", "excited = False", @@ -1597,14 +1422,13 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "prediction": { - "answer": "Error", + "answer": "\u9519\u8bef", "choices": [ "Hello World", "Hello World!", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -1620,11 +1444,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 6, "lines": [ { - "content": "sentence += char", "is_current": true, "lineno": 6, + "text": "sentence += char", "type": "line" } ], @@ -1648,7 +1474,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 6, in ", + " File \"/my_program.py\", line 6, in ", " 5 | char = '!'", "--> 6 | sentence += char", " ^^^^", @@ -1664,7 +1490,16 @@ "step": "undefined_char" }, { - "page": "if and else", + "get_solution": [ + "if excited:", + " char = '!'", + "else:", + " char = '.'", + "sentence += char", + "", + "print(sentence)" + ], + "page": "if \u548c else", "program": [ "sentence = 'Hello there'", "excited = True", @@ -1677,7 +1512,6 @@ "print(sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1702,7 +1536,7 @@ "", "print(new_sentence)" ], - "page": "if and else", + "page": "if \u548c else", "program": [ "sentence = 'HELLO THERE'", "upper = True", @@ -1718,7 +1552,6 @@ "print(new_sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1744,7 +1577,7 @@ "", "print(new_sentence)" ], - "page": "if and else", + "page": "if \u548c else", "program": [ "sentence = 'One more exercise, and then you can relax.'", "upper = True", @@ -1761,7 +1594,6 @@ "print(new_sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1773,14 +1605,14 @@ "step": "spongebob" }, { - "page": "The Equality Operator", + "get_solution": "program", + "page": "\u7b49\u53f7\u8fd0\u7b97\u7b26", "program": [ "print(1 + 2 == 3)", "print(4 + 5 == 6)", "print('ab' + 'c' == 'a' + 'bc')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1792,16 +1624,17 @@ "step": "introducing_equality" }, { - "page": "The Equality Operator", + "get_solution": "program", + "page": "\u7b49\u53f7\u8fd0\u7b97\u7b26", "program": [ "print(1 + 2 = 3)" ], "response": { - "message": "", "passed": true, "result": [ { - "text": " print(1 + 2 = 3)\n ^\nSyntaxError: expression cannot contain assignment, perhaps you meant \"==\"?\nat line 1\n\nA `SyntaxError` occurs when Python cannot understand your code.\n\nYou likely called a function with a named argument:\n\n a_function(invalid=something) \n\nwhere `invalid` is not a valid variable name in Python\neither because it starts with a number, or is a string,\nor contains a period, etc.\n\n\n", + "friendly": "

A SyntaxError occurs when Python cannot understand your code.

\n

You likely called a function with a named argument:

\n
a_function(invalid=something)\n
\n

where invalid is not a valid variable name in Python\neither because it starts with a number, or is a string,\nor contains a period, etc.

", + "text": " print(1 + 2 = 3)\n ^^^^^^^\nSyntaxError: expression cannot contain assignment, perhaps you meant \"==\"?\n\u5728\u884c 1\n", "type": "syntax_error" } ] @@ -1809,7 +1642,8 @@ "step": "equality_vs_assignment" }, { - "page": "The Equality Operator", + "get_solution": "program", + "page": "\u7b49\u53f7\u8fd0\u7b97\u7b26", "program": [ "name = 'kesha'", "new_name = ''", @@ -1821,7 +1655,6 @@ "print(new_name)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1846,7 +1679,7 @@ "", "print(new_name)" ], - "page": "The Equality Operator", + "page": "\u7b49\u53f7\u8fd0\u7b97\u7b26", "program": [ "name = 'kesha'", "new_name = ''", @@ -1862,7 +1695,6 @@ "print(new_name)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1874,7 +1706,8 @@ "step": "if_equals_replacing_characters_exercise" }, { - "page": "Introducing elif", + "get_solution": "program", + "page": "\u4ecb\u7ecdelif", "program": [ "dna = 'AGTAGCGTC'", "opposite_dna = ''", @@ -1892,7 +1725,6 @@ "print(opposite_dna)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1921,7 +1753,7 @@ "", "print(opposite_dna)" ], - "page": "Introducing elif", + "page": "\u4ecb\u7ecdelif", "program": [ "dna = 'AGTAGCGTCCTTAGTTACAGGATGGCTTAT'", "opposite_dna = ''", @@ -1941,7 +1773,6 @@ "print(opposite_dna)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1953,7 +1784,8 @@ "step": "dna_example_with_else" }, { - "page": "Introducing elif", + "get_solution": "program", + "page": "\u4ecb\u7ecdelif", "program": [ "dna = 'AGTAGCGTC'", "opposite_dna = ''", @@ -1971,7 +1803,6 @@ "print(opposite_dna)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -1983,13 +1814,21 @@ "step": "dna_example_with_elif" }, { - "page": "Other Comparison Operators", + "get_solution": "program", + "page": "\u5176\u4ed6\u6bd4\u8f83\u8fd0\u7b97\u7b26", "program": [ "1 != 2" ], "response": { - "message": "", "passed": true, + "prediction": { + "answer": "True", + "choices": [ + "True", + "False", + "\u9519\u8bef" + ] + }, "result": [ { "text": "True\n", @@ -2000,7 +1839,8 @@ "step": "try_not_equals" }, { - "page": "Other Comparison Operators", + "get_solution": "program", + "page": "\u5176\u4ed6\u6bd4\u8f83\u8fd0\u7b97\u7b26", "program": [ "sentence = 'The e key on my keyboard is broken'", "new_sentence = ''", @@ -2010,7 +1850,6 @@ "print(new_sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2022,15 +1861,12 @@ "step": "brokn_kyboard" }, { - "get_solution": [ - "1 < 2" - ], - "page": "Other Comparison Operators", + "get_solution": "program", + "page": "\u5176\u4ed6\u6bd4\u8f83\u8fd0\u7b97\u7b26", "program": [ "1 < 2" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2042,15 +1878,12 @@ "step": "introducing_less_than" }, { - "get_solution": [ - "'1' < '2'" - ], - "page": "Other Comparison Operators", + "get_solution": "program", + "page": "\u5176\u4ed6\u6bd4\u8f83\u8fd0\u7b97\u7b26", "program": [ "'1' < '2'" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2062,7 +1895,8 @@ "step": "comparing_strings" }, { - "page": "Other Comparison Operators", + "get_solution": "program", + "page": "\u5176\u4ed6\u6bd4\u8f83\u8fd0\u7b97\u7b26", "program": [ "percentage = 73", "", @@ -2078,7 +1912,6 @@ "print(grade)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "B", @@ -2087,7 +1920,7 @@ "C", "B", "A", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -2113,7 +1946,7 @@ " first = x3", "print(first)" ], - "page": "Other Comparison Operators", + "page": "\u5176\u4ed6\u6bd4\u8f83\u8fd0\u7b97\u7b26", "program": [ "x1 = 1", "x2 = 2", @@ -2131,7 +1964,6 @@ "print(first)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2143,7 +1975,8 @@ "step": "min_three_exercise" }, { - "page": "Introducing Lists", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5217\u8868", "program": [ "words = ['This', 'is', 'a', 'list']", "", @@ -2151,7 +1984,6 @@ " print(word)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2163,14 +1995,14 @@ "step": "first_list" }, { - "page": "Introducing Lists", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5217\u8868", "program": [ "x = 1", "things = ['Hello', x, x + 3]", "print(things)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2182,7 +2014,8 @@ "step": "can_contain_anything" }, { - "page": "Introducing Lists", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5217\u8868", "program": [ "numbers = [3, 1, 4, 1, 5, 9]", "", @@ -2193,7 +2026,6 @@ "print(total)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2212,7 +2044,7 @@ "", "print(total)" ], - "page": "Introducing Lists", + "page": "\u4ecb\u7ecd\u5217\u8868", "program": [ "words = ['This', 'is', 'a', 'list']", "total = ''", @@ -2222,7 +2054,6 @@ "print(total)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2246,7 +2077,7 @@ "", "print(total)" ], - "page": "Introducing Lists", + "page": "\u4ecb\u7ecd\u5217\u8868", "program": [ "words = ['This', 'is', 'a', 'list']", "separator = ' - '", @@ -2262,7 +2093,6 @@ "print(total)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2280,7 +2110,7 @@ " double += [number * 2]", "print(double)" ], - "page": "Building New Lists", + "page": "\u6784\u5efa\u65b0\u5217\u8868", "program": [ "numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]", "double = []", @@ -2289,7 +2119,6 @@ "print(double)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2308,7 +2137,7 @@ " big_numbers.append(number)", "print(big_numbers)" ], - "page": "Building New Lists", + "page": "\u6784\u5efa\u65b0\u5217\u8868", "program": [ "numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]", "big_numbers = []", @@ -2318,7 +2147,6 @@ "print(big_numbers)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2338,7 +2166,7 @@ "", "print(found)" ], - "page": "Using break to end a loop early", + "page": "\u4f7f\u7528 break \u63d0\u524d\u7ed3\u675f\u5faa\u73af", "program": [ "things = ['This', 'is', 'a', 'list']", "thing_to_find = 'is'", @@ -2350,7 +2178,6 @@ "print(found)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2362,7 +2189,8 @@ "step": "list_contains_exercise" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "words = ['This', 'is', 'a', 'list']", "", @@ -2372,7 +2200,6 @@ "print(words[3])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2384,12 +2211,12 @@ "step": "introducing_subscripting" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "words[4]" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2402,11 +2229,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 1, "lines": [ { - "content": "words[4]", "is_current": true, "lineno": 1, + "text": "words[4]", "type": "line" } ], @@ -2426,7 +2255,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 1, in ", + " File \"/my_program.py\", line 1, in ", "--> 1 | words[4]", " ^^^^^^^^", "words = ['This', 'is', 'a', 'list']", @@ -2440,7 +2269,8 @@ "step": "index_error" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "words = ['This', 'is', 'a', 'list']", "indices = [0, 1, 2, 3]", @@ -2450,7 +2280,6 @@ " print(words[index])" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "0\nThis\n1\nis\n2\na\n3\nlist", @@ -2461,7 +2290,7 @@ "This\n0\nis\n1\na\n2\nlist\n3", "0\n1\n2\n3\nThis\nis\na\nlist", "This\nis\na\nlist\n0\n1\n2\n3", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -2474,7 +2303,8 @@ "step": "introducing_len_and_range" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "words = ['This', 'is', 'a', 'list']", "indices = range(4)", @@ -2484,7 +2314,6 @@ " print(words[index])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2496,7 +2325,8 @@ "step": "range_len" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "indices = range(4)", "", @@ -2506,7 +2336,6 @@ "print(indices[3])" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "0\n1\n2\n3", @@ -2516,7 +2345,7 @@ "[0]\n[1]\n[2]\n[3]", "[1]\n[2]\n[3]\n[4]", "This\nis\na\nlist", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -2529,22 +2358,22 @@ "step": "printing_the_range" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "indices[4]" ], "response": { - "message": "", "passed": true, "prediction": { - "answer": "Error", + "answer": "\u9519\u8bef", "choices": [ "0", "1", "2", "3", "4", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -2558,11 +2387,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 1, "lines": [ { - "content": "indices[4]", "is_current": true, "lineno": 1, + "text": "indices[4]", "type": "line" } ], @@ -2576,13 +2407,13 @@ ] } ], - "friendly": "

An IndexError occurs when you try to get an item from a list,\na tuple, or a similar object (sequence), and use an index which\ndoes not exist; typically, this happens because the index you give\nis greater than the length of the sequence.

\n

You have tried to get the item with index 4 of indices,\nrange object of length 4.\nThe valid index values of indices are integers ranging from\n-4 to 3.

", + "friendly": "

An IndexError occurs when you try to get an item from a list,\na tuple, or a similar object (sequence), and use an index which\ndoes not exist; typically, this happens because the index you give\nis greater than the length of the sequence.

\n

You have tried to get the item with index 4 of indices,\nrange object of length 4.\nThe valid index values of indices are integers ranging from\n-4 to 3.

", "tail": "" } ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 1, in ", + " File \"/my_program.py\", line 1, in ", "--> 1 | indices[4]", " ^^^^^^^^^^", "indices = range(0, 4)", @@ -2596,12 +2427,12 @@ "step": "indices_out_of_bounds" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "range(4)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2613,12 +2444,12 @@ "step": "range_almost_the_same_as_list" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "list(range(4))" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "[0, 1, 2, 3]", @@ -2630,7 +2461,7 @@ "range(0, 1, 2, 3)", "(0, 1, 2, 3)", "[0, 1, 2, 3]", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -2643,13 +2474,13 @@ "step": "range_versus_list" }, { - "page": "Getting elements at a position, range(), and len()", + "get_solution": "program", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "words = ['This', 'is', 'a', 'list']", "print(len(words))" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "4", @@ -2660,7 +2491,7 @@ "3", "4", "5", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -2676,13 +2507,12 @@ "get_solution": [ "print(words[len(words) - 1])" ], - "page": "Getting elements at a position, range(), and len()", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "words = ['Python']", "print(words[len(words) - 1])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2699,7 +2529,7 @@ " print(index)", " print(words[index])" ], - "page": "Getting elements at a position, range(), and len()", + "page": "\u83b7\u53d6\u4f4d\u7f6e\u7684\u5143\u7d20\uff0crange() \u548c len()", "program": [ "words = ['Python']", "for index in range(len(words)):", @@ -2707,7 +2537,6 @@ " print(words[index])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2719,7 +2548,7 @@ "step": "print_indices_and_words" }, { - "page": "Exercises with range() and len()", + "page": "\u4f7f\u7528 range() \u548c len() \u7684\u7ec3\u4e60", "program": [ "things = ['on', 'the', 'way', 'to', 'the', 'store']", "to_find = 'the'", @@ -2728,7 +2557,7 @@ " print(i)" ], "response": { - "message": "

You're almost there! However, this prints all the indices,\nnot just the first one.

", + "message": "

\u4f60\u5feb\u5230\u4e86\uff01\u4f46\u662f\uff0c\u8fd9\u4f1a\u6253\u5370\u6240\u6709\u7684\u7d22\u5f15\uff0c\n\u800c\u4e0d\u4ec5\u4ec5\u662f\u7b2c\u4e00\u4e2a\u3002

", "passed": false, "result": [ { @@ -2740,7 +2569,7 @@ "step": "index_exercise" }, { - "page": "Exercises with range() and len()", + "page": "\u4f7f\u7528 range() \u548c len() \u7684\u7ec3\u4e60", "program": [ "things = ['on', 'the', 'way', 'to', 'the', 'store']", "to_find = 'the'", @@ -2751,7 +2580,7 @@ "print(answer)" ], "response": { - "message": "

You're almost there! However, this prints the last index,\nnot the first one.

", + "message": "

\u4f60\u5feb\u5230\u4e86\uff01\u4f46\u662f\uff0c\u8fd9\u4f1a\u6253\u5370 \u6700\u540e \u4e00\u4e2a\u7d22\u5f15\uff0c\n\u800c\u4e0d\u662f\u7b2c\u4e00\u4e2a\u3002

", "passed": false, "result": [ { @@ -2769,7 +2598,7 @@ " print(i)", " break" ], - "page": "Exercises with range() and len()", + "page": "\u4f7f\u7528 range() \u548c len() \u7684\u7ec3\u4e60", "program": [ "things = ['on', 'the', 'way', 'to', 'the', 'store']", "to_find = 'the'", @@ -2779,7 +2608,6 @@ " break" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2797,7 +2625,7 @@ " char2 = string2[i]", " print(char1 + ' ' + char2)" ], - "page": "Exercises with range() and len()", + "page": "\u4f7f\u7528 range() \u548c len() \u7684\u7ec3\u4e60", "program": [ "string1 = 'Hello'", "string2 = 'World'", @@ -2807,7 +2635,6 @@ " print(char1 + ' ' + char2)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2841,7 +2668,7 @@ "", " print(char1 + ' ' + char2)" ], - "page": "Exercises with range() and len()", + "page": "\u4f7f\u7528 range() \u548c len() \u7684\u7ec3\u4e60", "program": [ "string1 = 'Goodbye'", "string2 = 'World'", @@ -2867,7 +2694,6 @@ " print(char1 + ' ' + char2)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2879,13 +2705,13 @@ "step": "zip_longest_exercise" }, { - "page": "Terminology: Calling functions and methods", + "get_solution": "program", + "page": "\u672f\u8bed\uff1a\u8c03\u7528\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "print(len)", "print(print)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2897,12 +2723,12 @@ "step": "print_functions" }, { - "page": "Terminology: Calling functions and methods", + "get_solution": "program", + "page": "\u672f\u8bed\uff1a\u8c03\u7528\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "print(callable(len))" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2914,14 +2740,14 @@ "step": "introducing_callable" }, { - "page": "Terminology: Calling functions and methods", + "get_solution": "program", + "page": "\u672f\u8bed\uff1a\u8c03\u7528\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "f = 'a string'", "print(callable(f))", "f()" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2940,11 +2766,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 3, "lines": [ { - "content": "f()", "is_current": true, "lineno": 3, + "text": "f()", "type": "line" } ], @@ -2958,13 +2786,13 @@ ] } ], - "friendly": "

A TypeError is usually caused by trying\nto combine two incompatible types of objects,\nby calling a function with the wrong type of object,\nor by trying to do an operation not allowed on a given type of object.

\n

The parenthesis () following f are interpreted\nby Python as a function call for f.\nHowever, f is not a function but an object of type str.

", + "friendly": "

A TypeError is usually caused by trying\nto combine two incompatible types of objects,\nby calling a function with the wrong type of object,\nor by trying to do an operation not allowed on a given type of object.

\n

Because of the surrounding parenthesis, ` \nis interpreted by Python as indicating a function call forf, which is an object of typestr`\nwhich cannot be called.

", "tail": "" } ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 3, in ", + " File \"/my_program.py\", line 3, in ", " 2 | print(callable(f))", "--> 3 | f()", " ^^^", @@ -2979,7 +2807,8 @@ "step": "not_callable" }, { - "page": "Terminology: Calling functions and methods", + "get_solution": "program", + "page": "\u672f\u8bed\uff1a\u8c03\u7528\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "things = [1, 2, 3]", "length = len(things)", @@ -2987,7 +2816,6 @@ "print(printed)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -2999,13 +2827,13 @@ "step": "print_returns_none" }, { - "page": "Terminology: Calling functions and methods", + "get_solution": "program", + "page": "\u672f\u8bed\uff1a\u8c03\u7528\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "things = print([1, 2, 3])", "length = len(things)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3024,11 +2852,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 2, "lines": [ { - "content": "length = len(things)", "is_current": true, "lineno": 2, + "text": "length = len(things)", "type": "line" } ], @@ -3048,7 +2878,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 2, in ", + " File \"/my_program.py\", line 2, in ", " 1 | things = print([1, 2, 3])", "--> 2 | length = len(things)", " ^^^^^^^^^^^", @@ -3063,14 +2893,14 @@ "step": "len_of_none" }, { - "page": "Terminology: Calling functions and methods", + "get_solution": "program", + "page": "\u672f\u8bed\uff1a\u8c03\u7528\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "word = 'Hello'", "print(word.upper)", "print(word.upper())" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3082,13 +2912,13 @@ "step": "methods_of_str" }, { - "page": "Terminology: Calling functions and methods", + "get_solution": "program", + "page": "\u672f\u8bed\uff1a\u8c03\u7528\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "word = 'Hello'", "word.append('!')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3101,11 +2931,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 2, "lines": [ { - "content": "word.append('!')", "is_current": true, "lineno": 2, + "text": "word.append('!')", "type": "line" } ], @@ -3125,7 +2957,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 2, in ", + " File \"/my_program.py\", line 2, in ", " 1 | word = 'Hello'", "--> 2 | word.append('!')", " ^^^^^^^^^^^", @@ -3140,7 +2972,8 @@ "step": "no_append_for_str" }, { - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "nums = [1, 2, 3]", "new_nums = nums + [4, 5]", @@ -3150,7 +2983,6 @@ "print(nums)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3162,14 +2994,14 @@ "step": "append_vs_concatenate" }, { - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "nums = [1, 2, 3]", "nums[1] = 9", "print(nums)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "[1, 9, 3]", @@ -3180,7 +3012,7 @@ "[9, 2, 3]", "[1, 9, 3]", "[1, 2, 9]", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3193,12 +3025,12 @@ "step": "subscript_assignment_predict" }, { - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "[7, 8, 9, 8].index(8)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "1", @@ -3209,7 +3041,7 @@ "1", "2", "3", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3222,14 +3054,14 @@ "step": "index_predict_exercise" }, { - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "nums = [1, 2, 3]", "print(nums.pop(1))", "print(nums)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "2\n[1, 3]", @@ -3240,7 +3072,7 @@ "2\n[2, 3]", "1\n[2, 1, 3]", "2\n[2, 1, 3]", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3253,14 +3085,14 @@ "step": "pop_predict_exercise" }, { - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "nums = [1, 2, 3]", "nums.remove(1)", "print(nums)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "[2, 3]", @@ -3271,7 +3103,7 @@ "1", "2", "3", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3284,19 +3116,14 @@ "step": "remove_predict_exercise" }, { - "get_solution": [ - "x = ['a', 'b', 'c']", - "x.append(x.pop(0))", - "print(x)" - ], - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = ['a', 'b', 'c']", "x.append(x.pop(0))", "print(x)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3308,19 +3135,14 @@ "step": "pop_remove_index_subscript_assignment" }, { - "get_solution": [ - "x = ['a', 'b', 'c']", - "x[len(x) - 1] = x[0]", - "print(x)" - ], - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = ['a', 'b', 'c']", "x[len(x) - 1] = x[0]", "print(x)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3332,19 +3154,14 @@ "step": "subscript_assignment_exercise" }, { - "get_solution": [ - "x = ['a', 'b', 'c']", - "y = x + [x[0]]", - "print(y)" - ], - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = ['a', 'b', 'c']", "y = x + [x[0]]", "print(y)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3356,19 +3173,14 @@ "step": "negative_index_concatenation_exercise" }, { - "get_solution": [ - "x = [1, 2, 0, 3]", - "x.pop(x.index(0))", - "print(x)" - ], - "page": "Functions and Methods for Lists", + "get_solution": "program", + "page": "\u5217\u8868\u7684\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = [1, 2, 0, 3]", "x.pop(x.index(0))", "print(x)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3380,12 +3192,12 @@ "step": "remove_exercise" }, { - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "sorted([2, 9, 1, 8, 5, 6])" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "[1, 2, 5, 6, 8, 9]", @@ -3394,7 +3206,7 @@ "[1, 8, 6, 2, 5, 9]", "[1, 2, 5, 6, 8, 9]", "[2, 9, 1, 8, 5, 6]", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3407,14 +3219,14 @@ "step": "sorted_predict_exercise" }, { - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "nums = [2, 9, 1, 8, 5, 64]", "print(7 in nums)", "print(2 in nums)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "False\nTrue", @@ -3423,7 +3235,7 @@ "False\nTrue", "True\nTrue", "False\nFalse", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3436,12 +3248,12 @@ "step": "in_predict_exercise" }, { - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "sum([5, 3, 4])" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "12", @@ -3449,7 +3261,7 @@ "10", "12", "7", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3462,12 +3274,12 @@ "step": "sum_predict_exercise" }, { - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "[1, 2, 3, 2, 7, 2, 5].count(2)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "3", @@ -3476,7 +3288,7 @@ "1", "2", "3", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -3489,19 +3301,14 @@ "step": "count_predict_exercise" }, { - "get_solution": [ - "x = [1, 2, 0, 3]", - "y = x.count(1) > 0", - "print(y)" - ], - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = [1, 2, 0, 3]", "y = x.count(1) > 0", "print(y)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3513,19 +3320,14 @@ "step": "count_in_sorted_sum" }, { - "get_solution": [ - "x = [15, 12, -6, 3]", - "y = sum(x) / len(x)", - "print(y)" - ], - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = [15, 12, -6, 3]", "y = sum(x) / len(x)", "print(y)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3537,19 +3339,14 @@ "step": "average_exercise" }, { - "get_solution": [ - "x = 100", - "y = sum(range(x + 1))", - "print(y)" - ], - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = 100", "y = sum(range(x + 1))", "print(y)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3561,19 +3358,14 @@ "step": "sum_range_exercise" }, { - "get_solution": [ - "x = [12, -6, 2, -1, 3]", - "y = sorted(x)[1]", - "print(y)" - ], - "page": "More List Functions and Methods", + "get_solution": "program", + "page": "\u66f4\u591a\u5217\u8868\u51fd\u6570\u548c\u65b9\u6cd5", "program": [ "x = [12, -6, 2, -1, 3]", "y = sorted(x)[1]", "print(y)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3585,12 +3377,12 @@ "step": "second_smallest_in_list_exercise" }, { - "page": "String Methods and Immutability", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u65b9\u6cd5\u4e0e\u4e0d\u53ef\u53d8\u6027", "program": [ "print('the' in 'feed the dog and the cat')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3602,14 +3394,14 @@ "step": "string_in_step" }, { - "page": "String Methods and Immutability", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u65b9\u6cd5\u4e0e\u4e0d\u53ef\u53d8\u6027", "program": [ "string = 'feed the dog and the cat'", "print(string.count('the'))", "print(string.index('the'))" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3621,12 +3413,12 @@ "step": "string_count_index" }, { - "page": "String Methods and Immutability", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u65b9\u6cd5\u4e0e\u4e0d\u53ef\u53d8\u6027", "program": [ "'Python'.append(' is cool!')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3639,11 +3431,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 1, "lines": [ { - "content": "'Python'.append(' is cool!')", "is_current": true, "lineno": 1, + "text": "'Python'.append(' is cool!')", "type": "line" } ], @@ -3658,7 +3452,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 1, in ", + " File \"/my_program.py\", line 1, in ", "--> 1 | 'Python'.append(' is cool!')", " ^^^^^^^^^^^^^^^", "", @@ -3671,7 +3465,8 @@ "step": "mutation_string_append" }, { - "page": "String Methods and Immutability", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u65b9\u6cd5\u4e0e\u4e0d\u53ef\u53d8\u6027", "program": [ "sentence = \"Python rocks!\"", "new_sentence = sentence.upper()", @@ -3679,7 +3474,6 @@ "print(new_sentence)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3691,15 +3485,12 @@ "step": "string_lower_upper" }, { - "get_solution": [ - "max([21, 55, 4, 91, 62, 49])" - ], - "page": "How to Find Information with Google, and more", + "get_solution": "program", + "page": "\u5982\u4f55\u4f7f\u7528\u8c37\u6b4c\u7b49\u67e5\u627e\u4fe1\u606f", "program": [ "max([21, 55, 4, 91, 62, 49])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3711,19 +3502,14 @@ "step": "sum_list" }, { - "get_solution": [ - "nums = [1, 2, 3, 4, 5]", - "nums.insert(2, 9)", - "print(nums)" - ], - "page": "How to Find Information with Google, and more", + "get_solution": "program", + "page": "\u5982\u4f55\u4f7f\u7528\u8c37\u6b4c\u7b49\u67e5\u627e\u4fe1\u606f", "program": [ "nums = [1, 2, 3, 4, 5]", "nums.insert(2, 9)", "print(nums)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3735,16 +3521,16 @@ "step": "list_insert" }, { - "page": "How to Find Information with Google, and more", + "get_solution": "program", + "page": "\u5982\u4f55\u4f7f\u7528\u8c37\u6b4c\u7b49\u67e5\u627e\u4fe1\u606f", "program": [ "dir([])" ], "response": { - "message": "", "passed": true, "result": [ { - "text": "['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']\n", + "text": "['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']\n", "type": "stdout" } ] @@ -3752,7 +3538,8 @@ "step": "dir_list" }, { - "page": "Understanding Programs With Python Tutor", + "get_solution": "program", + "page": "\u4f7f\u7528 Python Tutor \u7406\u89e3\u7a0b\u5e8f", "program": [ "all_numbers = [2, 4, 8, 1, 9, 7]", "", @@ -3769,7 +3556,6 @@ "print(big_numbers)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3781,7 +3567,8 @@ "step": "run_with_python_tutor" }, { - "page": "== vs is, and Having Multiple Names for One Value", + "get_solution": "program", + "page": "== \u4e0e is\uff0c\u4ee5\u53ca\u4e00\u4e2a\u503c\u7684\u591a\u4e2a\u540d\u79f0", "program": [ "list1 = [1, 2, 3]", "list2 = [1, 2, 3]", @@ -3798,7 +3585,6 @@ "print(list2)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3810,7 +3596,8 @@ "step": "two_separate_lists" }, { - "page": "== vs is, and Having Multiple Names for One Value", + "get_solution": "program", + "page": "== \u4e0e is\uff0c\u4ee5\u53ca\u4e00\u4e2a\u503c\u7684\u591a\u4e2a\u540d\u79f0", "program": [ "list1 = [1, 2, 3]", "list2 = list1", @@ -3827,7 +3614,6 @@ "print(list2)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3839,7 +3625,8 @@ "step": "same_list" }, { - "page": "Modifying While Iterating", + "get_solution": "program", + "page": "\u8fed\u4ee3\u65f6\u7684\u4fee\u6539", "program": [ "numbers = [10, 7, 8, 3, 12, 15]", "for i in range(len(numbers)):", @@ -3849,7 +3636,6 @@ "print(numbers)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3862,11 +3648,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 3, "lines": [ { - "content": "number = numbers[i]", "is_current": true, "lineno": 3, + "text": "number = numbers[i]", "type": "line" } ], @@ -3906,7 +3694,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 3, in ", + " File \"/my_program.py\", line 3, in ", " 2 | for i in range(len(numbers)):", "--> 3 | number = numbers[i]", " ^^^^^^^^^^", @@ -3926,7 +3714,8 @@ "step": "run_broken_with_python_tutor" }, { - "page": "Modifying While Iterating", + "get_solution": "program", + "page": "\u8fed\u4ee3\u65f6\u7684\u4fee\u6539", "program": [ "numbers = [10, 7, 8, 3, 12, 15]", "for number in numbers:", @@ -3935,7 +3724,6 @@ "print(numbers)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3947,7 +3735,8 @@ "step": "remove_instead_of_pop" }, { - "page": "Modifying While Iterating", + "get_solution": "program", + "page": "\u8fed\u4ee3\u65f6\u7684\u4fee\u6539", "program": [ "numbers = [10, 7, 8, 3, 12, 15]", "for number in numbers.copy():", @@ -3956,7 +3745,6 @@ "print(numbers)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3968,7 +3756,8 @@ "step": "make_copy" }, { - "page": "Modifying While Iterating", + "get_solution": "program", + "page": "\u8fed\u4ee3\u65f6\u7684\u4fee\u6539", "program": [ "numbers = [10, 7, 8, 3, 12, 15]", "big_numbers = numbers.copy()", @@ -3979,7 +3768,6 @@ "print(big_numbers)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -3991,7 +3779,8 @@ "step": "make_copy2" }, { - "page": "Modifying While Iterating", + "get_solution": "program", + "page": "\u8fed\u4ee3\u65f6\u7684\u4fee\u6539", "program": [ "numbers = [10, 7, 8, 3, 12, 15]", "big_numbers = []", @@ -4002,7 +3791,6 @@ "print(big_numbers)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4014,16 +3802,17 @@ "step": "make_new_list" }, { - "page": "Single and Double Quotes in Strings", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u4e2d\u7684\u5355\u5f15\u53f7\u548c\u53cc\u5f15\u53f7", "program": [ "print('Alice's Diner')" ], "response": { - "message": "", "passed": true, "result": [ { - "text": " print('Alice's Diner')\n ^\nSyntaxError: invalid syntax\nat line 1\n\nA `SyntaxError` occurs when Python cannot understand your code.\n\nI suspect that you were trying to use a quote character inside a string\nthat was enclosed in quotes of the same kind.\nPerhaps you should have escaped the inner quote character:\n\n print('Alice\\'s Diner')\n ^^\n\n\n", + "friendly": "

A SyntaxError occurs when Python cannot understand your code.

\n

You started writing a string with a single or double quote\nbut never ended the string with another quote on that line.

", + "text": " print('Alice's Diner')\n ^\nSyntaxError: unterminated string literal (detected at line 1)\n\u5728\u884c 1\n", "type": "syntax_error" } ] @@ -4031,12 +3820,12 @@ "step": "single_quotes_apostrophe" }, { - "page": "Single and Double Quotes in Strings", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u4e2d\u7684\u5355\u5f15\u53f7\u548c\u53cc\u5f15\u53f7", "program": [ "print(\"Alice's Diner\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4048,19 +3837,19 @@ "step": "double_quotes" }, { - "page": "Single and Double Quotes in Strings", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u4e2d\u7684\u5355\u5f15\u53f7\u548c\u53cc\u5f15\u53f7", "program": [ "'Alice' == \"Alice\"" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "True", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -4073,15 +3862,12 @@ "step": "single_double_quotes_equal" }, { - "get_solution": [ - "print(\"Special cases aren't special enough to break the rules.\")" - ], - "page": "Single and Double Quotes in Strings", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u4e2d\u7684\u5355\u5f15\u53f7\u548c\u53cc\u5f15\u53f7", "program": [ "print(\"Special cases aren't special enough to break the rules.\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4093,15 +3879,12 @@ "step": "double_quote_exercise" }, { - "get_solution": [ - "print('\"Talk is cheap. Show me the code.\" - Linus Torvalds')" - ], - "page": "Single and Double Quotes in Strings", + "get_solution": "program", + "page": "\u5b57\u7b26\u4e32\u4e2d\u7684\u5355\u5f15\u53f7\u548c\u53cc\u5f15\u53f7", "program": [ "print('\"Talk is cheap. Show me the code.\" - Linus Torvalds')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4113,6 +3896,7 @@ "step": "single_quote_exercise" }, { + "get_solution": "program", "page": "f-strings", "program": [ "name = \"Alice\"", @@ -4121,7 +3905,6 @@ "print(f\"{name} went to {meal} with {friend}.\")" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Alice went to lunch with Bob.", @@ -4135,7 +3918,7 @@ "'Alice' went to 'lunch' with 'Bob'.", "\"Alice went to lunch with Bob.\"", "Alice went to lunch with Bob.", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -4148,6 +3931,7 @@ "step": "introduce_f_strings" }, { + "get_solution": "program", "page": "f-strings", "program": [ "name = \"Alice\"", @@ -4155,16 +3939,15 @@ "print(\"Hello \" + name + \". You are \" + age + \" years old.\")" ], "response": { - "message": "", "passed": true, "prediction": { - "answer": "Error", + "answer": "\u9519\u8bef", "choices": [ "\"Hello \" + name + \". You are \" + age + \" years old.\"", "Hello name. You are age years old.", "Hello Alice. You are 20 years old.", "Hello 'Alice'. You are 20 years old.", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -4178,11 +3961,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 3, "lines": [ { - "content": "print("Hello " + name + ". You are " + age + " years old.")", "is_current": true, "lineno": 3, + "text": "print("Hello " + name + ". You are " + age + " years old.")", "type": "line" } ], @@ -4214,7 +3999,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 3, in ", + " File \"/my_program.py\", line 3, in ", " 2 | age = 20", "--> 3 | print(\"Hello \" + name + \". You are \" + age + \" years old.\")", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^", @@ -4232,11 +4017,7 @@ "step": "concatenate_string_number" }, { - "get_solution": [ - "name = \"Alice\"", - "age = 20", - "print(f'Hello {name}. You are {age} years old.')" - ], + "get_solution": "program", "page": "f-strings", "program": [ "name = \"Alice\"", @@ -4244,7 +4025,6 @@ "print(f'Hello {name}. You are {age} years old.')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4256,12 +4036,12 @@ "step": "basic_f_string_exercise" }, { + "get_solution": "program", "page": "f-strings", "program": [ "f\"2 * 3 + 4 is equal to {2 * 3 + 4}\"" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4278,15 +4058,14 @@ ], "page": "f-strings", "program": [ - "people = ['eODoUwNzS', 'ofHBEzZxs', 'zwTrQA', 'LDaOKmpHU', 'HCMLTt', 'NbRmEjaoyD']", + "people = ['Alice', 'Bob', 'Charlie']", "print(f\"There are {len(people)} people waiting, the first one's name is {people[0]}.\")" ], "response": { - "message": "", "passed": true, "result": [ { - "text": "There are 6 people waiting, the first one's name is eODoUwNzS.\n", + "text": "There are 3 people waiting, the first one's name is Alice.\n", "type": "stdout" } ] @@ -4294,7 +4073,8 @@ "step": "fix_broken_program" }, { - "page": "Introducing Nested Loops", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "for letter in \"ABC\":", " print(letter)", @@ -4303,7 +4083,6 @@ " print('---')" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "A\nA 0\nA 1\nA 2\nA 3\n---\nB\nB 0\nB 1\nB 2\nB 3\n---\nC\nC 0\nC 1\nC 2\nC 3\n---", @@ -4312,7 +4091,7 @@ "A\nA 0\nA 1\nA 2\nA 3\n---\nB\nB 0\nB 1\nB 2\nB 3\n---\nC\nC 0\nC 1\nC 2\nC 3\n---", "A 1\nA 2\nA 3\nA 4\n---\nB 1\nB 2\nB 3\nB 4\n---\nC 1\nC 2\nC 3\nC 4\n---", "A\nB\nC\n---\nA 0\nB 0\nC 0\n---\nA 1\nB 1\nC 1\n---\nA 2\nB 2\nC 2\n---\nA 3\nB 3\nC 3", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -4325,12 +4104,12 @@ "step": "first_nested_loop" }, { - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "print(1 + \"x\")" ], "response": { - "message": "

You can't add together strings and numbers. Use an f-string.

", + "message": "

\u4f60\u4e0d\u80fd\u5c06\u5b57\u7b26\u4e32\u548c\u6570\u5b57\u76f8\u52a0\u3002\u4f7f\u7528 f-string\u3002

", "passed": false, "result": [ { @@ -4343,11 +4122,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 1, "lines": [ { - "content": "print(1 + "x")", "is_current": true, "lineno": 1, + "text": "print(1 + "x")", "type": "line" } ], @@ -4362,7 +4143,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 1, in ", + " File \"/my_program.py\", line 1, in ", "--> 1 | print(1 + \"x\")", " ^^^^^^^", "", @@ -4375,25 +4156,25 @@ "step": "times_table_exercise" }, { - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "for left in range(12):", " for right in range(12):", " left += 1", " right += 1", " print(f'{left} x {right} = {left * right}')", - " print('----------')" + " print('---')" ], "response": { - "message": "

You added 1 to your outer loop variable at the wrong place!\nWhere should you do that instead to fix it?

", + "message": "

\u4f60\u5728\u9519\u8bef\u7684\u5730\u65b9\u7ed9\u5916\u90e8\u5faa\u73af\u53d8\u91cf\u52a0\u4e86 1\uff01\n\u4f60\u5e94\u8be5\u5728\u54ea\u91cc\u505a\u8fd9\u4e2a\u4ee5\u4fee\u590d\u5b83\uff1f

", "passed": false, "result": [ { - "text": "1 x 1 = 1\n2 x 2 = 4\n3 x 3 = 9\n4 x 4 = 16\n5 x 5 = 25\n6 x 6 = 36\n7 x 7 = 49\n8 x 8 = 64\n9 x 9 = 81\n10 x 10 = 100\n11 x 11 = 121\n12 x 12 = 144\n----------\n2 x 1 = 2\n3 x 2 = 6\n4 x 3 = 12\n5 x 4 = 20\n6 x 5 = 30\n7 x 6 = 42\n8 x 7 = 56\n9 x 8 = 72\n10 x 9 = 90\n11 x 10 = 110\n12 x 11 = 132\n13 x 12 = 156\n----------\n3 x 1 = 3\n4 x 2 = 8\n5 x 3 = 15\n6 x 4 = 24\n7 x 5 = 35\n8 x 6 = 48\n9 x 7 = 63\n10 x 8 = 80\n11 x 9 = 99\n12 x 10 = 120\n13 x 11 = 143\n14 x 12 = 168\n----------\n4 x 1 = 4\n5 x 2 = 10\n6 x 3 = 18\n7 x 4 = 28\n8 x 5 = 40\n9 x 6 = 54\n10 x 7 = 70\n11 x 8 = 88\n12 x 9 = 108\n13 x 10 = 130\n14 x 11 = 154\n15 x 12 = 180\n----------\n5 x 1 = 5\n6 x 2 = 12\n7 x 3 = 21\n8 x 4 = 32\n9 x 5 = 45\n10 x 6 = 60\n11 x 7 = 77\n12 x 8 = 96\n13 x 9 = 117\n14 x 10 = 140\n15 x 11 = 165\n16 x 12 = 192\n----------\n6 x 1 = 6\n7 x 2 = 14\n8 x 3 = 24\n9 x 4 = 36\n10 x 5 = 50\n11 x 6 = 66\n12 x 7 = 84\n13 x 8 = 104\n14 x 9 = 126\n15 x 10 = 150\n16 x 11 = 176\n17 x 12 = 204\n----------\n7 x 1 = 7\n8 x 2 = 16\n9 x 3 = 27\n10 x 4 = 40\n11 x 5 = 55\n12 x 6 = 72\n13 x 7 = 91", + "text": "1 x 1 = 1\n2 x 2 = 4\n3 x 3 = 9\n4 x 4 = 16\n5 x 5 = 25\n6 x 6 = 36\n7 x 7 = 49\n8 x 8 = 64\n9 x 9 = 81\n10 x 10 = 100\n11 x 11 = 121\n12 x 12 = 144\n---\n2 x 1 = 2\n3 x 2 = 6\n4 x 3 = 12\n5 x 4 = 20\n6 x 5 = 30\n7 x 6 = 42\n8 x 7 = 56\n9 x 8 = 72\n10 x 9 = 90\n11 x 10 = 110\n12 x 11 = 132\n13 x 12 = 156\n---\n3 x 1 = 3\n4 x 2 = 8\n5 x 3 = 15\n6 x 4 = 24\n7 x 5 = 35\n8 x 6 = 48\n9 x 7 = 63\n10 x 8 = 80\n11 x 9 = 99\n12 x 10 = 120\n13 x 11 = 143\n14 x 12 = 168\n---\n4 x 1 = 4\n5 x 2 = 10\n6 x 3 = 18\n7 x 4 = 28\n8 x 5 = 40\n9 x 6 = 54\n10 x 7 = 70\n11 x 8 = 88\n12 x 9 = 108\n13 x 10 = 130\n14 x 11 = 154\n15 x 12 = 180\n---\n5 x 1 = 5\n6 x 2 = 12\n7 x 3 = 21\n8 x 4 = 32\n9 x 5 = 45\n10 x 6 = 60\n11 x 7 = 77\n12 x 8 = 96\n13 x 9 = 117\n14 x 10 = 140\n15 x 11 = 165\n16 x 12 = 192\n---\n6 x 1 = 6\n7 x 2 = 14\n8 x 3 = 24\n9 x 4 = 36\n10 x 5 = 50\n11 x 6 = 66\n12 x 7 = 84\n13 x 8 = 104\n14 x 9 = 126\n15 x 10 = 150\n16 x 11 = 176\n17 x 12 = 204\n---\n7 x 1 = 7\n8 x 2 = 16\n9 x 3 = 27\n10 x 4 = 40\n11 x 5 = 55\n12 x 6 = 72\n13 x 7 = 91\n14 x 8 = 112\n15 x 9 = 135\n16 x 10 = 160\n17 x 11 = 187", "type": "stdout" }, { - "text": "\n14 x 8 = 112\n15 x 9 = 135\n16 x 10 = 160\n17 x 11 = 187\n18 x 12 = 216\n----------\n8 x 1 = 8\n9 x 2 = 18\n10 x 3 = 30\n11 x 4 = 44\n12 x 5 = 60\n13 x 6 = 78\n14 x 7 = 98\n15 x 8 = 120\n16 x 9 = 144\n17 x 10 = 170\n18 x 11 = 198\n19 x 12 = 228\n----------\n9 x 1 = 9\n10 x 2 = 20\n11 x 3 = 33\n12 x 4 = 48\n13 x 5 = 65\n14 x 6 = 84\n15 x 7 = 105\n16 x 8 = 128\n17 x 9 = 153\n18 x 10 = 180\n19 x 11 = 209\n20 x 12 = 240\n----------\n10 x 1 = 10\n11 x 2 = 22\n12 x 3 = 36\n13 x 4 = 52\n14 x 5 = 70\n15 x 6 = 90\n16 x 7 = 112\n17 x 8 = 136\n18 x 9 = 162\n19 x 10 = 190\n20 x 11 = 220\n21 x 12 = 252\n----------\n11 x 1 = 11\n12 x 2 = 24\n13 x 3 = 39\n14 x 4 = 56\n15 x 5 = 75\n16 x 6 = 96\n17 x 7 = 119\n18 x 8 = 144\n19 x 9 = 171\n20 x 10 = 200\n21 x 11 = 231\n22 x 12 = 264\n----------\n12 x 1 = 12\n13 x 2 = 26\n14 x 3 = 42\n15 x 4 = 60\n16 x 5 = 80\n17 x 6 = 102\n18 x 7 = 126\n19 x 8 = 152\n20 x 9 = 180\n21 x 10 = 210\n22 x 11 = 242\n23 x 12 = 276\n----------\n", + "text": "\n18 x 12 = 216\n---\n8 x 1 = 8\n9 x 2 = 18\n10 x 3 = 30\n11 x 4 = 44\n12 x 5 = 60\n13 x 6 = 78\n14 x 7 = 98\n15 x 8 = 120\n16 x 9 = 144\n17 x 10 = 170\n18 x 11 = 198\n19 x 12 = 228\n---\n9 x 1 = 9\n10 x 2 = 20\n11 x 3 = 33\n12 x 4 = 48\n13 x 5 = 65\n14 x 6 = 84\n15 x 7 = 105\n16 x 8 = 128\n17 x 9 = 153\n18 x 10 = 180\n19 x 11 = 209\n20 x 12 = 240\n---\n10 x 1 = 10\n11 x 2 = 22\n12 x 3 = 36\n13 x 4 = 52\n14 x 5 = 70\n15 x 6 = 90\n16 x 7 = 112\n17 x 8 = 136\n18 x 9 = 162\n19 x 10 = 190\n20 x 11 = 220\n21 x 12 = 252\n---\n11 x 1 = 11\n12 x 2 = 24\n13 x 3 = 39\n14 x 4 = 56\n15 x 5 = 75\n16 x 6 = 96\n17 x 7 = 119\n18 x 8 = 144\n19 x 9 = 171\n20 x 10 = 200\n21 x 11 = 231\n22 x 12 = 264\n---\n12 x 1 = 12\n13 x 2 = 26\n14 x 3 = 42\n15 x 4 = 60\n16 x 5 = 80\n17 x 6 = 102\n18 x 7 = 126\n19 x 8 = 152\n20 x 9 = 180\n21 x 10 = 210\n22 x 11 = 242\n23 x 12 = 276\n---\n", "type": "stdout" } ] @@ -4401,7 +4182,7 @@ "step": "times_table_exercise" }, { - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "for left in range(12):", " left += 1", @@ -4417,18 +4198,18 @@ " print(left, 'x 10 =', left * 10)", " print(left, 'x 11 =', left * 11)", " print(left, 'x 12 =', left * 12)", - " print('----------')" + " print('---')" ], "response": { - "message": "

Your solution is too long. You only need a few lines of code for this problem.\nUse a nested loop so that you don't need to repeat yourself.\nThe computer will do the repetition for you!

", + "message": "

\u4f60\u7684\u89e3\u51b3\u65b9\u6848\u592a\u957f\u4e86\u3002\u8fd9\u4e2a\u95ee\u9898\u53ea\u9700\u8981\u51e0\u884c\u4ee3\u7801\u3002\n\u4f7f\u7528\u5d4c\u5957\u5faa\u73af\uff0c\u8fd9\u6837\u4f60\u5c31\u4e0d\u9700\u8981\u91cd\u590d\u81ea\u5df1\u3002\n\u8ba1\u7b97\u673a\u4f1a\u4e3a\u4f60\u5b8c\u6210\u91cd\u590d\u7684\u5de5\u4f5c\uff01

", "passed": false, "result": [ { - "text": "1 x 1 = 1\n1 x 2 = 2\n1 x 3 = 3\n1 x 4 = 4\n1 x 5 = 5\n1 x 6 = 6\n1 x 7 = 7\n1 x 8 = 8\n1 x 9 = 9\n1 x 10 = 10\n1 x 11 = 11\n1 x 12 = 12\n----------\n2 x 1 = 2\n2 x 2 = 4\n2 x 3 = 6\n2 x 4 = 8\n2 x 5 = 10\n2 x 6 = 12\n2 x 7 = 14\n2 x 8 = 16\n2 x 9 = 18\n2 x 10 = 20\n2 x 11 = 22\n2 x 12 = 24\n----------\n3 x 1 = 3\n3 x 2 = 6\n3 x 3 = 9\n3 x 4 = 12\n3 x 5 = 15\n3 x 6 = 18\n3 x 7 = 21\n3 x 8 = 24\n3 x 9 = 27\n3 x 10 = 30\n3 x 11 = 33\n3 x 12 = 36\n----------\n4 x 1 = 4\n4 x 2 = 8\n4 x 3 = 12\n4 x 4 = 16\n4 x 5 = 20\n4 x 6 = 24\n4 x 7 = 28\n4 x 8 = 32\n4 x 9 = 36\n4 x 10 = 40\n4 x 11 = 44\n4 x 12 = 48\n----------\n5 x 1 = 5\n5 x 2 = 10\n5 x 3 = 15\n5 x 4 = 20\n5 x 5 = 25\n5 x 6 = 30\n5 x 7 = 35\n5 x 8 = 40\n5 x 9 = 45\n5 x 10 = 50\n5 x 11 = 55\n5 x 12 = 60\n----------\n6 x 1 = 6\n6 x 2 = 12\n6 x 3 = 18\n6 x 4 = 24\n6 x 5 = 30\n6 x 6 = 36\n6 x 7 = 42\n6 x 8 = 48\n6 x 9 = 54\n6 x 10 = 60\n6 x 11 = 66\n6 x 12 = 72\n----------\n7 x 1 = 7\n7 x 2 = 14\n7 x 3 = 21\n7 x 4 = 28\n7 x 5 = 35\n7 x 6 = 42\n7 x 7 = 49\n7 x 8 = 56\n7 x 9 = 63\n7 x 10 = 70\n7 x 11 = 77\n7 x 12 = 84\n----------", + "text": "1 x 1 = 1\n1 x 2 = 2\n1 x 3 = 3\n1 x 4 = 4\n1 x 5 = 5\n1 x 6 = 6\n1 x 7 = 7\n1 x 8 = 8\n1 x 9 = 9\n1 x 10 = 10\n1 x 11 = 11\n1 x 12 = 12\n---\n2 x 1 = 2\n2 x 2 = 4\n2 x 3 = 6\n2 x 4 = 8\n2 x 5 = 10\n2 x 6 = 12\n2 x 7 = 14\n2 x 8 = 16\n2 x 9 = 18\n2 x 10 = 20\n2 x 11 = 22\n2 x 12 = 24\n---\n3 x 1 = 3\n3 x 2 = 6\n3 x 3 = 9\n3 x 4 = 12\n3 x 5 = 15\n3 x 6 = 18\n3 x 7 = 21\n3 x 8 = 24\n3 x 9 = 27\n3 x 10 = 30\n3 x 11 = 33\n3 x 12 = 36\n---\n4 x 1 = 4\n4 x 2 = 8\n4 x 3 = 12\n4 x 4 = 16\n4 x 5 = 20\n4 x 6 = 24\n4 x 7 = 28\n4 x 8 = 32\n4 x 9 = 36\n4 x 10 = 40\n4 x 11 = 44\n4 x 12 = 48\n---\n5 x 1 = 5\n5 x 2 = 10\n5 x 3 = 15\n5 x 4 = 20\n5 x 5 = 25\n5 x 6 = 30\n5 x 7 = 35\n5 x 8 = 40\n5 x 9 = 45\n5 x 10 = 50\n5 x 11 = 55\n5 x 12 = 60\n---\n6 x 1 = 6\n6 x 2 = 12\n6 x 3 = 18\n6 x 4 = 24\n6 x 5 = 30\n6 x 6 = 36\n6 x 7 = 42\n6 x 8 = 48\n6 x 9 = 54\n6 x 10 = 60\n6 x 11 = 66\n6 x 12 = 72\n---\n7 x 1 = 7\n7 x 2 = 14\n7 x 3 = 21\n7 x 4 = 28\n7 x 5 = 35\n7 x 6 = 42\n7 x 7 = 49\n7 x 8 = 56\n7 x 9 = 63\n7 x 10 = 70\n7 x 11 = 77\n7 x 12 = 84\n---\n8 x 1 = 8\n8 x 2 = 16\n8 x 3 = 24\n8 x 4 = 32\n8 x 5 =", "type": "stdout" }, { - "text": "\n8 x 1 = 8\n8 x 2 = 16\n8 x 3 = 24\n8 x 4 = 32\n8 x 5 = 40\n8 x 6 = 48\n8 x 7 = 56\n8 x 8 = 64\n8 x 9 = 72\n8 x 10 = 80\n8 x 11 = 88\n8 x 12 = 96\n----------\n9 x 1 = 9\n9 x 2 = 18\n9 x 3 = 27\n9 x 4 = 36\n9 x 5 = 45\n9 x 6 = 54\n9 x 7 = 63\n9 x 8 = 72\n9 x 9 = 81\n9 x 10 = 90\n9 x 11 = 99\n9 x 12 = 108\n----------\n10 x 1 = 10\n10 x 2 = 20\n10 x 3 = 30\n10 x 4 = 40\n10 x 5 = 50\n10 x 6 = 60\n10 x 7 = 70\n10 x 8 = 80\n10 x 9 = 90\n10 x 10 = 100\n10 x 11 = 110\n10 x 12 = 120\n----------\n11 x 1 = 11\n11 x 2 = 22\n11 x 3 = 33\n11 x 4 = 44\n11 x 5 = 55\n11 x 6 = 66\n11 x 7 = 77\n11 x 8 = 88\n11 x 9 = 99\n11 x 10 = 110\n11 x 11 = 121\n11 x 12 = 132\n----------\n12 x 1 = 12\n12 x 2 = 24\n12 x 3 = 36\n12 x 4 = 48\n12 x 5 = 60\n12 x 6 = 72\n12 x 7 = 84\n12 x 8 = 96\n12 x 9 = 108\n12 x 10 = 120\n12 x 11 = 132\n12 x 12 = 144\n----------\n", + "text": " 40\n8 x 6 = 48\n8 x 7 = 56\n8 x 8 = 64\n8 x 9 = 72\n8 x 10 = 80\n8 x 11 = 88\n8 x 12 = 96\n---\n9 x 1 = 9\n9 x 2 = 18\n9 x 3 = 27\n9 x 4 = 36\n9 x 5 = 45\n9 x 6 = 54\n9 x 7 = 63\n9 x 8 = 72\n9 x 9 = 81\n9 x 10 = 90\n9 x 11 = 99\n9 x 12 = 108\n---\n10 x 1 = 10\n10 x 2 = 20\n10 x 3 = 30\n10 x 4 = 40\n10 x 5 = 50\n10 x 6 = 60\n10 x 7 = 70\n10 x 8 = 80\n10 x 9 = 90\n10 x 10 = 100\n10 x 11 = 110\n10 x 12 = 120\n---\n11 x 1 = 11\n11 x 2 = 22\n11 x 3 = 33\n11 x 4 = 44\n11 x 5 = 55\n11 x 6 = 66\n11 x 7 = 77\n11 x 8 = 88\n11 x 9 = 99\n11 x 10 = 110\n11 x 11 = 121\n11 x 12 = 132\n---\n12 x 1 = 12\n12 x 2 = 24\n12 x 3 = 36\n12 x 4 = 48\n12 x 5 = 60\n12 x 6 = 72\n12 x 7 = 84\n12 x 8 = 96\n12 x 9 = 108\n12 x 10 = 120\n12 x 11 = 132\n12 x 12 = 144\n---\n", "type": "stdout" } ] @@ -4436,7 +4217,7 @@ "step": "times_table_exercise" }, { - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "for left in range(12):", " left += 1", @@ -4444,18 +4225,18 @@ " right += 1", " # for the sake of translation", " print(f'{left} x {right} = {left * right}'.replace('x', '*'))", - " print('----------')" + " print('---')" ], "response": { - "message": "

That's almost correct! Make sure to display the right character x in your table.\nFor example, your solution should display 3 x 4 = 12 and not 3 * 4 = 12.

", + "message": "

\u8fd9\u51e0\u4e4e\u662f\u6b63\u786e\u7684\uff01\u786e\u4fdd\u5728\u4f60\u7684\u8868\u4e2d\u663e\u793a\u6b63\u786e\u7684\u5b57\u7b26 x\u3002\n\u4f8b\u5982\uff0c\u4f60\u7684\u89e3\u51b3\u65b9\u6848\u5e94\u8be5\u663e\u793a 3 x 4 = 12 \u800c\u4e0d\u662f 3 * 4 = 12\u3002

", "passed": false, "result": [ { - "text": "1 * 1 = 1\n1 * 2 = 2\n1 * 3 = 3\n1 * 4 = 4\n1 * 5 = 5\n1 * 6 = 6\n1 * 7 = 7\n1 * 8 = 8\n1 * 9 = 9\n1 * 10 = 10\n1 * 11 = 11\n1 * 12 = 12\n----------\n2 * 1 = 2\n2 * 2 = 4\n2 * 3 = 6\n2 * 4 = 8\n2 * 5 = 10\n2 * 6 = 12\n2 * 7 = 14\n2 * 8 = 16\n2 * 9 = 18\n2 * 10 = 20\n2 * 11 = 22\n2 * 12 = 24\n----------\n3 * 1 = 3\n3 * 2 = 6\n3 * 3 = 9\n3 * 4 = 12\n3 * 5 = 15\n3 * 6 = 18\n3 * 7 = 21\n3 * 8 = 24\n3 * 9 = 27\n3 * 10 = 30\n3 * 11 = 33\n3 * 12 = 36\n----------\n4 * 1 = 4\n4 * 2 = 8\n4 * 3 = 12\n4 * 4 = 16\n4 * 5 = 20\n4 * 6 = 24\n4 * 7 = 28\n4 * 8 = 32\n4 * 9 = 36\n4 * 10 = 40\n4 * 11 = 44\n4 * 12 = 48\n----------\n5 * 1 = 5\n5 * 2 = 10\n5 * 3 = 15\n5 * 4 = 20\n5 * 5 = 25\n5 * 6 = 30\n5 * 7 = 35\n5 * 8 = 40\n5 * 9 = 45\n5 * 10 = 50\n5 * 11 = 55\n5 * 12 = 60\n----------\n6 * 1 = 6\n6 * 2 = 12\n6 * 3 = 18\n6 * 4 = 24\n6 * 5 = 30\n6 * 6 = 36\n6 * 7 = 42\n6 * 8 = 48\n6 * 9 = 54\n6 * 10 = 60\n6 * 11 = 66\n6 * 12 = 72\n----------\n7 * 1 = 7\n7 * 2 = 14\n7 * 3 = 21\n7 * 4 = 28\n7 * 5 = 35\n7 * 6 = 42\n7 * 7 = 49\n7 * 8 = 56\n7 * 9 = 63\n7 * 10 = 70\n7 * 11 = 77\n7 * 12 = 84\n----------", + "text": "1 * 1 = 1\n1 * 2 = 2\n1 * 3 = 3\n1 * 4 = 4\n1 * 5 = 5\n1 * 6 = 6\n1 * 7 = 7\n1 * 8 = 8\n1 * 9 = 9\n1 * 10 = 10\n1 * 11 = 11\n1 * 12 = 12\n---\n2 * 1 = 2\n2 * 2 = 4\n2 * 3 = 6\n2 * 4 = 8\n2 * 5 = 10\n2 * 6 = 12\n2 * 7 = 14\n2 * 8 = 16\n2 * 9 = 18\n2 * 10 = 20\n2 * 11 = 22\n2 * 12 = 24\n---\n3 * 1 = 3\n3 * 2 = 6\n3 * 3 = 9\n3 * 4 = 12\n3 * 5 = 15\n3 * 6 = 18\n3 * 7 = 21\n3 * 8 = 24\n3 * 9 = 27\n3 * 10 = 30\n3 * 11 = 33\n3 * 12 = 36\n---\n4 * 1 = 4\n4 * 2 = 8\n4 * 3 = 12\n4 * 4 = 16\n4 * 5 = 20\n4 * 6 = 24\n4 * 7 = 28\n4 * 8 = 32\n4 * 9 = 36\n4 * 10 = 40\n4 * 11 = 44\n4 * 12 = 48\n---\n5 * 1 = 5\n5 * 2 = 10\n5 * 3 = 15\n5 * 4 = 20\n5 * 5 = 25\n5 * 6 = 30\n5 * 7 = 35\n5 * 8 = 40\n5 * 9 = 45\n5 * 10 = 50\n5 * 11 = 55\n5 * 12 = 60\n---\n6 * 1 = 6\n6 * 2 = 12\n6 * 3 = 18\n6 * 4 = 24\n6 * 5 = 30\n6 * 6 = 36\n6 * 7 = 42\n6 * 8 = 48\n6 * 9 = 54\n6 * 10 = 60\n6 * 11 = 66\n6 * 12 = 72\n---\n7 * 1 = 7\n7 * 2 = 14\n7 * 3 = 21\n7 * 4 = 28\n7 * 5 = 35\n7 * 6 = 42\n7 * 7 = 49\n7 * 8 = 56\n7 * 9 = 63\n7 * 10 = 70\n7 * 11 = 77\n7 * 12 = 84\n---\n8 * 1 = 8\n8 * 2 = 16\n8 * 3 = 24\n8 * 4 = 32\n8 * 5 = 40", "type": "stdout" }, { - "text": "\n8 * 1 = 8\n8 * 2 = 16\n8 * 3 = 24\n8 * 4 = 32\n8 * 5 = 40\n8 * 6 = 48\n8 * 7 = 56\n8 * 8 = 64\n8 * 9 = 72\n8 * 10 = 80\n8 * 11 = 88\n8 * 12 = 96\n----------\n9 * 1 = 9\n9 * 2 = 18\n9 * 3 = 27\n9 * 4 = 36\n9 * 5 = 45\n9 * 6 = 54\n9 * 7 = 63\n9 * 8 = 72\n9 * 9 = 81\n9 * 10 = 90\n9 * 11 = 99\n9 * 12 = 108\n----------\n10 * 1 = 10\n10 * 2 = 20\n10 * 3 = 30\n10 * 4 = 40\n10 * 5 = 50\n10 * 6 = 60\n10 * 7 = 70\n10 * 8 = 80\n10 * 9 = 90\n10 * 10 = 100\n10 * 11 = 110\n10 * 12 = 120\n----------\n11 * 1 = 11\n11 * 2 = 22\n11 * 3 = 33\n11 * 4 = 44\n11 * 5 = 55\n11 * 6 = 66\n11 * 7 = 77\n11 * 8 = 88\n11 * 9 = 99\n11 * 10 = 110\n11 * 11 = 121\n11 * 12 = 132\n----------\n12 * 1 = 12\n12 * 2 = 24\n12 * 3 = 36\n12 * 4 = 48\n12 * 5 = 60\n12 * 6 = 72\n12 * 7 = 84\n12 * 8 = 96\n12 * 9 = 108\n12 * 10 = 120\n12 * 11 = 132\n12 * 12 = 144\n----------\n", + "text": "\n8 * 6 = 48\n8 * 7 = 56\n8 * 8 = 64\n8 * 9 = 72\n8 * 10 = 80\n8 * 11 = 88\n8 * 12 = 96\n---\n9 * 1 = 9\n9 * 2 = 18\n9 * 3 = 27\n9 * 4 = 36\n9 * 5 = 45\n9 * 6 = 54\n9 * 7 = 63\n9 * 8 = 72\n9 * 9 = 81\n9 * 10 = 90\n9 * 11 = 99\n9 * 12 = 108\n---\n10 * 1 = 10\n10 * 2 = 20\n10 * 3 = 30\n10 * 4 = 40\n10 * 5 = 50\n10 * 6 = 60\n10 * 7 = 70\n10 * 8 = 80\n10 * 9 = 90\n10 * 10 = 100\n10 * 11 = 110\n10 * 12 = 120\n---\n11 * 1 = 11\n11 * 2 = 22\n11 * 3 = 33\n11 * 4 = 44\n11 * 5 = 55\n11 * 6 = 66\n11 * 7 = 77\n11 * 8 = 88\n11 * 9 = 99\n11 * 10 = 110\n11 * 11 = 121\n11 * 12 = 132\n---\n12 * 1 = 12\n12 * 2 = 24\n12 * 3 = 36\n12 * 4 = 48\n12 * 5 = 60\n12 * 6 = 72\n12 * 7 = 84\n12 * 8 = 96\n12 * 9 = 108\n12 * 10 = 120\n12 * 11 = 132\n12 * 12 = 144\n---\n", "type": "stdout" } ] @@ -4463,17 +4244,18 @@ "step": "times_table_exercise" }, { - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "pass", "3 x 4" ], "response": { - "message": "

To multiply numbers, use *

", + "message": "

\u8981\u4e58\u6570\u5b57\uff0c\u4f7f\u7528 *

", "passed": false, "result": [ { - "text": " 3 x 4\n ^\nSyntaxError: invalid syntax\nat line 2\n\nA `SyntaxError` occurs when Python cannot understand your code.\n\nCurrently, I cannot guess the likely cause of this error.\nTry to examine closely the line indicated as well as the line\nimmediately above to see if you can identify some misspelled\nword, or missing symbols, like (, ), [, ], :, etc.\n\nUnless your code uses type annotations, which are beyond our scope,\nif you think that this is something which should be handled\nby friendly, please report this case to\nhttps://github.com/aroberge/friendly/issues\n\n\n\n", + "friendly": "

A SyntaxError occurs when Python cannot understand your code.

\n

Currently, I cannot guess the likely cause of this error.\nTry to examine closely the line indicated as well as the line\nimmediately above to see if you can identify some misspelled\nword, or missing symbols, like (, ), [, ], :, etc.

\n

Unless your code uses type annotations, which are beyond our scope,\nif you think that this is something which should be handled\nby friendly, please report this case to\nhttps://github.com/friendly-traceback/friendly-traceback/issues

", + "text": " 3 x 4\n ^\nSyntaxError: invalid syntax\n\u5728\u884c 2\n", "type": "syntax_error" } ] @@ -4481,33 +4263,25 @@ "step": "times_table_exercise" }, { - "get_solution": [ - "for left in range(12):", - " left += 1", - " for right in range(12):", - " right += 1", - " print(f'{left} x {right} = {left * right}')", - " print('----------')" - ], - "page": "Introducing Nested Loops", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "for left in range(12):", " left += 1", " for right in range(12):", " right += 1", " print(f'{left} x {right} = {left * right}')", - " print('----------')" + " print('---')" ], "response": { - "message": "", "passed": true, "result": [ { - "text": "1 x 1 = 1\n1 x 2 = 2\n1 x 3 = 3\n1 x 4 = 4\n1 x 5 = 5\n1 x 6 = 6\n1 x 7 = 7\n1 x 8 = 8\n1 x 9 = 9\n1 x 10 = 10\n1 x 11 = 11\n1 x 12 = 12\n----------\n2 x 1 = 2\n2 x 2 = 4\n2 x 3 = 6\n2 x 4 = 8\n2 x 5 = 10\n2 x 6 = 12\n2 x 7 = 14\n2 x 8 = 16\n2 x 9 = 18\n2 x 10 = 20\n2 x 11 = 22\n2 x 12 = 24\n----------\n3 x 1 = 3\n3 x 2 = 6\n3 x 3 = 9\n3 x 4 = 12\n3 x 5 = 15\n3 x 6 = 18\n3 x 7 = 21\n3 x 8 = 24\n3 x 9 = 27\n3 x 10 = 30\n3 x 11 = 33\n3 x 12 = 36\n----------\n4 x 1 = 4\n4 x 2 = 8\n4 x 3 = 12\n4 x 4 = 16\n4 x 5 = 20\n4 x 6 = 24\n4 x 7 = 28\n4 x 8 = 32\n4 x 9 = 36\n4 x 10 = 40\n4 x 11 = 44\n4 x 12 = 48\n----------\n5 x 1 = 5\n5 x 2 = 10\n5 x 3 = 15\n5 x 4 = 20\n5 x 5 = 25\n5 x 6 = 30\n5 x 7 = 35\n5 x 8 = 40\n5 x 9 = 45\n5 x 10 = 50\n5 x 11 = 55\n5 x 12 = 60\n----------\n6 x 1 = 6\n6 x 2 = 12\n6 x 3 = 18\n6 x 4 = 24\n6 x 5 = 30\n6 x 6 = 36\n6 x 7 = 42\n6 x 8 = 48\n6 x 9 = 54\n6 x 10 = 60\n6 x 11 = 66\n6 x 12 = 72\n----------\n7 x 1 = 7\n7 x 2 = 14\n7 x 3 = 21\n7 x 4 = 28\n7 x 5 = 35\n7 x 6 = 42\n7 x 7 = 49\n7 x 8 = 56\n7 x 9 = 63\n7 x 10 = 70\n7 x 11 = 77\n7 x 12 = 84\n----------", + "text": "1 x 1 = 1\n1 x 2 = 2\n1 x 3 = 3\n1 x 4 = 4\n1 x 5 = 5\n1 x 6 = 6\n1 x 7 = 7\n1 x 8 = 8\n1 x 9 = 9\n1 x 10 = 10\n1 x 11 = 11\n1 x 12 = 12\n---\n2 x 1 = 2\n2 x 2 = 4\n2 x 3 = 6\n2 x 4 = 8\n2 x 5 = 10\n2 x 6 = 12\n2 x 7 = 14\n2 x 8 = 16\n2 x 9 = 18\n2 x 10 = 20\n2 x 11 = 22\n2 x 12 = 24\n---\n3 x 1 = 3\n3 x 2 = 6\n3 x 3 = 9\n3 x 4 = 12\n3 x 5 = 15\n3 x 6 = 18\n3 x 7 = 21\n3 x 8 = 24\n3 x 9 = 27\n3 x 10 = 30\n3 x 11 = 33\n3 x 12 = 36\n---\n4 x 1 = 4\n4 x 2 = 8\n4 x 3 = 12\n4 x 4 = 16\n4 x 5 = 20\n4 x 6 = 24\n4 x 7 = 28\n4 x 8 = 32\n4 x 9 = 36\n4 x 10 = 40\n4 x 11 = 44\n4 x 12 = 48\n---\n5 x 1 = 5\n5 x 2 = 10\n5 x 3 = 15\n5 x 4 = 20\n5 x 5 = 25\n5 x 6 = 30\n5 x 7 = 35\n5 x 8 = 40\n5 x 9 = 45\n5 x 10 = 50\n5 x 11 = 55\n5 x 12 = 60\n---\n6 x 1 = 6\n6 x 2 = 12\n6 x 3 = 18\n6 x 4 = 24\n6 x 5 = 30\n6 x 6 = 36\n6 x 7 = 42\n6 x 8 = 48\n6 x 9 = 54\n6 x 10 = 60\n6 x 11 = 66\n6 x 12 = 72\n---\n7 x 1 = 7\n7 x 2 = 14\n7 x 3 = 21\n7 x 4 = 28\n7 x 5 = 35\n7 x 6 = 42\n7 x 7 = 49\n7 x 8 = 56\n7 x 9 = 63\n7 x 10 = 70\n7 x 11 = 77\n7 x 12 = 84\n---\n8 x 1 = 8\n8 x 2 = 16\n8 x 3 = 24\n8 x 4 = 32\n8 x 5 = 40", "type": "stdout" }, { - "text": "\n8 x 1 = 8\n8 x 2 = 16\n8 x 3 = 24\n8 x 4 = 32\n8 x 5 = 40\n8 x 6 = 48\n8 x 7 = 56\n8 x 8 = 64\n8 x 9 = 72\n8 x 10 = 80\n8 x 11 = 88\n8 x 12 = 96\n----------\n9 x 1 = 9\n9 x 2 = 18\n9 x 3 = 27\n9 x 4 = 36\n9 x 5 = 45\n9 x 6 = 54\n9 x 7 = 63\n9 x 8 = 72\n9 x 9 = 81\n9 x 10 = 90\n9 x 11 = 99\n9 x 12 = 108\n----------\n10 x 1 = 10\n10 x 2 = 20\n10 x 3 = 30\n10 x 4 = 40\n10 x 5 = 50\n10 x 6 = 60\n10 x 7 = 70\n10 x 8 = 80\n10 x 9 = 90\n10 x 10 = 100\n10 x 11 = 110\n10 x 12 = 120\n----------\n11 x 1 = 11\n11 x 2 = 22\n11 x 3 = 33\n11 x 4 = 44\n11 x 5 = 55\n11 x 6 = 66\n11 x 7 = 77\n11 x 8 = 88\n11 x 9 = 99\n11 x 10 = 110\n11 x 11 = 121\n11 x 12 = 132\n----------\n12 x 1 = 12\n12 x 2 = 24\n12 x 3 = 36\n12 x 4 = 48\n12 x 5 = 60\n12 x 6 = 72\n12 x 7 = 84\n12 x 8 = 96\n12 x 9 = 108\n12 x 10 = 120\n12 x 11 = 132\n12 x 12 = 144\n----------\n", + "text": "\n8 x 6 = 48\n8 x 7 = 56\n8 x 8 = 64\n8 x 9 = 72\n8 x 10 = 80\n8 x 11 = 88\n8 x 12 = 96\n---\n9 x 1 = 9\n9 x 2 = 18\n9 x 3 = 27\n9 x 4 = 36\n9 x 5 = 45\n9 x 6 = 54\n9 x 7 = 63\n9 x 8 = 72\n9 x 9 = 81\n9 x 10 = 90\n9 x 11 = 99\n9 x 12 = 108\n---\n10 x 1 = 10\n10 x 2 = 20\n10 x 3 = 30\n10 x 4 = 40\n10 x 5 = 50\n10 x 6 = 60\n10 x 7 = 70\n10 x 8 = 80\n10 x 9 = 90\n10 x 10 = 100\n10 x 11 = 110\n10 x 12 = 120\n---\n11 x 1 = 11\n11 x 2 = 22\n11 x 3 = 33\n11 x 4 = 44\n11 x 5 = 55\n11 x 6 = 66\n11 x 7 = 77\n11 x 8 = 88\n11 x 9 = 99\n11 x 10 = 110\n11 x 11 = 121\n11 x 12 = 132\n---\n12 x 1 = 12\n12 x 2 = 24\n12 x 3 = 36\n12 x 4 = 48\n12 x 5 = 60\n12 x 6 = 72\n12 x 7 = 84\n12 x 8 = 96\n12 x 9 = 108\n12 x 10 = 120\n12 x 11 = 132\n12 x 12 = 144\n---\n", "type": "stdout" } ] @@ -4521,7 +4295,7 @@ " if player1 != player2:", " print(f'{player1} vs {player2}')" ], - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "players = ['Alice', 'Bob', 'Charlie']", "for player1 in players:", @@ -4530,7 +4304,6 @@ " print(f'{player1} vs {player2}')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4549,7 +4322,7 @@ " for c4 in letters:", " print(c1 + c2 + c3 + c4)" ], - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "letters = 'AB'", "for c1 in letters:", @@ -4559,7 +4332,6 @@ " print(c1 + c2 + c3 + c4)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4579,7 +4351,7 @@ " line += '+'", " print(line)" ], - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "size = 3", "for i in range(size):", @@ -4590,7 +4362,6 @@ " print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4608,7 +4379,7 @@ " if i < j:", " print(f'{players[i]} vs {players[j]}')" ], - "page": "Introducing Nested Loops", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5faa\u73af", "program": [ "players = ['Alice', 'Bob', 'Charlie']", "for i in range(len(players)):", @@ -4617,7 +4388,6 @@ " print(f'{players[i]} vs {players[j]}')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4629,7 +4399,8 @@ "step": "player_vs_player_bonus" }, { - "page": "Understanding Programs with birdseye", + "get_solution": "program", + "page": "\u4f7f\u7528 birdseye \u7406\u89e3\u7a0b\u5e8f", "program": [ "a = 2", "b = 3", @@ -4638,7 +4409,6 @@ "print(a * b + c * d)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4650,7 +4420,8 @@ "step": "first_birdseye_example" }, { - "page": "Understanding Programs with birdseye", + "get_solution": "program", + "page": "\u4f7f\u7528 birdseye \u7406\u89e3\u7a0b\u5e8f", "program": [ "word = 'Amazing'", "vowels = []", @@ -4664,7 +4435,6 @@ "print(consonants)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4680,14 +4450,13 @@ "string = strings[1]", "print(string[0])" ], - "page": "Introducing Nested Lists", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5217\u8868", "program": [ "strings = ['abc', 'def', 'ghi']", "string = strings[1]", "print(string[0])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4699,13 +4468,13 @@ "step": "string_list_exercise" }, { - "page": "Introducing Nested Lists", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5217\u8868", "program": [ "strings = [\"abc\", \"def\", \"ghi\"]", "print(strings[1][0])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4720,13 +4489,12 @@ "get_solution": [ "print(strings[-2][-1])" ], - "page": "Introducing Nested Lists", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5217\u8868", "program": [ "strings = ['abc', 'de', 'fghi', 'jklmn', 'o']", "print(strings[-2][-1])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4738,13 +4506,13 @@ "step": "double_subscripting_exercise" }, { - "page": "Introducing Nested Lists", + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5217\u8868", "program": [ "strings = [['hello', 'there'], ['how', 'are', 'you']]", "print(strings[1][0])" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "how", @@ -4760,7 +4528,7 @@ "t", "e", "a", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -4776,13 +4544,12 @@ "get_solution": [ "print(strings[1][2][0])" ], - "page": "Introducing Nested Lists", + "page": "\u4ecb\u7ecd\u5d4c\u5957\u5217\u8868", "program": [ "strings = [['hello', 'there'], ['how', 'are', 'you']]", "print(strings[1][2][0])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4794,7 +4561,8 @@ "step": "triple_subscripting" }, { - "page": "Looping Over Nested Lists", + "get_solution": "program", + "page": "\u904d\u5386\u5d4c\u5957\u5217\u8868", "program": [ "numbers = [[1, 2, 3], [4, 5], [6], []]", "for sublist in numbers:", @@ -4803,7 +4571,6 @@ " print('---')" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "1\n2\n3\n---\n4\n5\n---\n6\n---\n---", @@ -4813,7 +4580,7 @@ "1\n2\n3\n---\n4\n5\n---\n6\n---", "1 2 3\n---\n4 5\n---\n6\n---\n---", "1 2 3\n---\n4 5\n---\n6\n---", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -4826,7 +4593,8 @@ "step": "nested_list_nested_loop_example" }, { - "page": "Looping Over Nested Lists", + "get_solution": "program", + "page": "\u904d\u5386\u5d4c\u5957\u5217\u8868", "program": [ "numbers = [[1, 2, 3], [4, 5], [6], []]", "for sublist in numbers:", @@ -4835,7 +4603,6 @@ " print('---')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4853,7 +4620,7 @@ " if word in string:", " print(string)" ], - "page": "Looping Over Nested Lists", + "page": "\u904d\u5386\u5d4c\u5957\u5217\u8868", "program": [ "strings = [['hello there', 'how are you'], ['goodbye world', 'hello world']]", "word = 'hello'", @@ -4863,7 +4630,6 @@ " print(string)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4883,7 +4649,7 @@ " present = True", " print(present)" ], - "page": "Looping Over Nested Lists", + "page": "\u904d\u5386\u5d4c\u5957\u5217\u8868", "program": [ "strings = [['hello there', 'how are you'], ['goodbye world', 'hello world']]", "word = 'goodbye'", @@ -4895,7 +4661,6 @@ " print(present)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4915,7 +4680,7 @@ " present = True", "print(present)" ], - "page": "Looping Over Nested Lists", + "page": "\u904d\u5386\u5d4c\u5957\u5217\u8868", "program": [ "strings = [['hello there', 'how are you'], ['goodbye world', 'hello world']]", "word = 'are'", @@ -4927,7 +4692,6 @@ "print(present)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4946,7 +4710,7 @@ " line += string[i]", " print(line)" ], - "page": "Looping Over Nested Lists", + "page": "\u904d\u5386\u5d4c\u5957\u5217\u8868", "program": [ "strings = ['abc', 'def', 'ghi']", "for i in range(len(strings[0])):", @@ -4956,7 +4720,6 @@ " print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -4983,7 +4746,7 @@ " line += string[i]", " print(line)" ], - "page": "Looping Over Nested Lists", + "page": "\u904d\u5386\u5d4c\u5957\u5217\u8868", "program": [ "strings = ['abcqwe', 'def', 'ghiq']", "lengths = []", @@ -5001,7 +4764,6 @@ " print(line)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5013,7 +4775,8 @@ "step": "zip_longest_strings_exercise" }, { - "page": "Defining Functions", + "get_solution": "program", + "page": "\u5b9a\u4e49\u51fd\u6570", "program": [ "def greet(name):", " print(f\"Hello {name}!\")", @@ -5022,7 +4785,6 @@ "greet(\"Bob\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5034,7 +4796,8 @@ "step": "define_greet" }, { - "page": "Defining Functions", + "get_solution": "program", + "page": "\u5b9a\u4e49\u51fd\u6570", "program": [ "def greet(name):", " print(f\"Hello {name}!\")", @@ -5044,7 +4807,6 @@ "greet(\"Bob\")" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Hello Alice!\nHow are you?\nHello Bob!\nHow are you?", @@ -5052,7 +4814,7 @@ "Hello Alice!\nHow are you?\nHello Bob!\nHow are you?", "Hello Alice!\nHello Bob!\nHow are you?", "Hello Alice!\nHow are you?\nHello Bob!", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5065,15 +4827,8 @@ "step": "how_are_you" }, { - "get_solution": [ - "def say_hello(name):", - " print(f\"Hello {name}!\")", - " print(\"How are you?\")", - "", - "say_hello(\"Alice\")", - "say_hello(\"Bob\")" - ], - "page": "Defining Functions", + "get_solution": "program", + "page": "\u5b9a\u4e49\u51fd\u6570", "program": [ "def say_hello(name):", " print(f\"Hello {name}!\")", @@ -5083,7 +4838,6 @@ "say_hello(\"Bob\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5095,15 +4849,8 @@ "step": "change_function_name" }, { - "get_solution": [ - "def say_hello(person_name):", - " print(f\"Hello {person_name}!\")", - " print(\"How are you?\")", - "", - "say_hello(\"Alice\")", - "say_hello(\"Bob\")" - ], - "page": "Defining Functions", + "get_solution": "program", + "page": "\u5b9a\u4e49\u51fd\u6570", "program": [ "def say_hello(person_name):", " print(f\"Hello {person_name}!\")", @@ -5113,7 +4860,6 @@ "say_hello(\"Bob\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5125,26 +4871,22 @@ "step": "change_parameter_name" }, { - "get_solution": [ - "def print_twice(x):", - " print(x)", - " print(x)" - ], - "page": "Defining Functions", + "get_solution": "program", + "page": "\u5b9a\u4e49\u51fd\u6570", "program": [ "def print_twice(x):", " print(x)", " print(x)" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "print_twice_exercise" }, { - "page": "Defining Functions", + "get_solution": "program", + "page": "\u5b9a\u4e49\u51fd\u6570", "program": [ "def print_many(thing, n):", " for _ in range(n):", @@ -5153,7 +4895,6 @@ "print_many(\"Hello\", 3)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Hello\nHello\nHello", @@ -5163,7 +4904,7 @@ "Hello\nHello\nHello", "Hello", "H\ne\nl\nl\no", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5176,15 +4917,9 @@ "step": "print_many" }, { - "get_solution": [ - "def print_many(n, thing):", - " for _ in range(n):", - " print(thing)", - "", - "print_many(3, \"Hello\")" - ], - "page": "Defining Functions", - "program": [ + "get_solution": "program", + "page": "\u5b9a\u4e49\u51fd\u6570", + "program": [ "def print_many(n, thing):", " for _ in range(n):", " print(thing)", @@ -5192,7 +4927,6 @@ "print_many(3, \"Hello\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5204,7 +4938,8 @@ "step": "swap_parameters" }, { - "page": "Calling Functions Within Functions", + "get_solution": "program", + "page": "\u5728\u51fd\u6570\u4e2d\u8c03\u7528\u51fd\u6570", "program": [ "def print_many(n, thing):", " for _ in range(n):", @@ -5216,7 +4951,6 @@ "print_twice(\"Hello\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5228,7 +4962,8 @@ "step": "print_twice_call_print_many" }, { - "page": "Calling Functions Within Functions", + "get_solution": "program", + "page": "\u5728\u51fd\u6570\u4e2d\u8c03\u7528\u51fd\u6570", "program": [ "def print_many(n, thing):", " for _ in range(n):", @@ -5240,83 +4975,55 @@ "print_twice(\"Hello\")" ], "response": { - "message": "", "passed": true, "result": [ { - "text": "", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 1\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_many\u001b[39m(n, thing):\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 1\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_many\u001b[39m(n, thing):\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_twice\u001b[39m(x):\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | print_twice(\u001b[38;5;186m\"\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m\"\u001b[39m)\n\u001b[38;5;245m \u001b[0m\u001b[36m\u001b[1m>>> Call to print_twice in File \"/my_program.py\", line 5\u001b[0m\n\u001b[38;5;245m \u001b[0m...... x = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 5\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_twice\u001b[39m(x):\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | print_many(\u001b[38;5;141m2\u001b[39m, x)\n\u001b[38;5;245m \u001b[0m\u001b[36m\u001b[1m>>> Call to print_many in File \"/my_program.py\", line 1\u001b[0m\n\u001b[38;5;245m \u001b[0m...... n = \u001b[38;5;141m2\u001b[39m\n\u001b[38;5;245m \u001b[0m...... thing = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 1\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_many\u001b[39m(n, thing):\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 2\u001b[0m | \u001b[38;5;81mfor\u001b[39m _ \u001b[38;5;204min\u001b[39m range(n):\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_twice\u001b[39m(x):\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m.......... _ = \u001b[38;5;141m0\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 3\u001b[0m | print(thing)\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | print_twice(\u001b[38;5;186m\"\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m\"\u001b[39m)\n", + "text": "Hello", "type": "stdout" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[36m\u001b[1m>>> Call to print_twice in File \"my_program.py\", line 5\u001b[0m\n\u001b[38;5;242m \u001b[0m...... x = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 5\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_twice\u001b[39m(x):\n", + "text": "\n", "type": "stdout" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | print_many(\u001b[38;5;141m2\u001b[39m, x)\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 2\u001b[0m | \u001b[38;5;81mfor\u001b[39m _ \u001b[38;5;204min\u001b[39m range(n):\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[36m\u001b[1m>>> Call to print_many in File \"my_program.py\", line 1\u001b[0m\n\u001b[38;5;242m \u001b[0m...... n = \u001b[38;5;141m2\u001b[39m\n\u001b[38;5;242m \u001b[0m...... thing = \u001b[38;5;186m'\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m'\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 1\u001b[0m | \u001b[38;5;81mdef\u001b[39m \u001b[38;5;148mprint_many\u001b[39m(n, thing):\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m.......... _ = \u001b[38;5;141m1\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 3\u001b[0m | print(thing)\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 2\u001b[0m | \u001b[38;5;81mfor\u001b[39m _ \u001b[38;5;197min\u001b[39m range(n):\n", + "text": "Hello", "type": "stdout" }, { - "text": "\u001b[38;5;242m \u001b[0m.......... _ = \u001b[38;5;141m0\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 3\u001b[0m | print(thing)\n", + "text": "\n", "type": "stdout" }, { - "text": "Hello\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 2\u001b[0m | \u001b[38;5;81mfor\u001b[39m _ \u001b[38;5;197min\u001b[39m range(n):\n", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 2\u001b[0m | \u001b[38;5;81mfor\u001b[39m _ \u001b[38;5;204min\u001b[39m range(n):\n", + "type": "snoop" }, { - "text": "\u001b[38;5;242m \u001b[0m.......... _ = \u001b[38;5;141m1\u001b[39m\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 3\u001b[0m | print(thing)\n", - "type": "stdout" - }, - { - "text": "Hello\n\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 2\u001b[0m | \u001b[38;5;81mfor\u001b[39m _ \u001b[38;5;197min\u001b[39m range(n):\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[32m\u001b[1m<<< Return value from print_many: \u001b[0m\u001b[38;5;81mNone\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 6\u001b[0m | print_many(\u001b[38;5;141m2\u001b[39m, x)\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[32m\u001b[1m<<< Return value from print_twice: \u001b[0m\u001b[38;5;81mNone\u001b[39m\n", - "type": "stdout" - }, - { - "text": "\u001b[38;5;242m \u001b[0m\u001b[38;5;242m 8\u001b[0m | print_twice(\u001b[38;5;186m\"\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m\"\u001b[39m)\n", - "type": "stdout" - }, - { - "text": "", - "type": "stdout" + "text": "\u001b[38;5;245m \u001b[0m\u001b[32m\u001b[1m<<< Return value from print_many: \u001b[0m\u001b[38;5;81mNone\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 6\u001b[0m | print_many(\u001b[38;5;141m2\u001b[39m, x)\n\u001b[38;5;245m \u001b[0m\u001b[32m\u001b[1m<<< Return value from print_twice: \u001b[0m\u001b[38;5;81mNone\u001b[39m\n\u001b[38;5;245m \u001b[0m\u001b[38;5;245m 8\u001b[0m | print_twice(\u001b[38;5;186m\"\u001b[39m\u001b[38;5;186mHello\u001b[39m\u001b[38;5;186m\"\u001b[39m)\n", + "type": "snoop" } ] }, "step": "see_stack_in_snoop" }, { - "page": "Calling Functions Within Functions", + "get_solution": "program", + "page": "\u5728\u51fd\u6570\u4e2d\u8c03\u7528\u51fd\u6570", "program": [ "def print_many(n, thing):", " for _ in range(n):", @@ -5328,7 +5035,6 @@ "print_twice(\"Hello\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5340,7 +5046,8 @@ "step": "see_stack_in_pythontutor" }, { - "page": "Calling Functions Within Functions", + "get_solution": "program", + "page": "\u5728\u51fd\u6570\u4e2d\u8c03\u7528\u51fd\u6570", "program": [ "def print_many(n, thing):", " for _ in range(n):", @@ -5352,7 +5059,6 @@ "print_twice(\"Hello\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5364,7 +5070,8 @@ "step": "see_stack_in_birdseye" }, { - "page": "Returning Values From Functions", + "get_solution": "program", + "page": "\u4ece\u51fd\u6570\u8fd4\u56de\u503c", "program": [ "def double(x):", " return x * 2", @@ -5375,7 +5082,6 @@ "print(twice)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "5\n10", @@ -5383,7 +5089,7 @@ "5\n5", "5\n10", "10\n10", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5396,7 +5102,8 @@ "step": "first_return" }, { - "page": "Returning Values From Functions", + "get_solution": "program", + "page": "\u4ece\u51fd\u6570\u8fd4\u56de\u503c", "program": [ "def double(x):", " return x * 2", @@ -5406,14 +5113,13 @@ "print(number)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "5", "choices": [ "5", "10", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5426,13 +5132,13 @@ "step": "losing_return_value" }, { - "page": "Returning Values From Functions", + "page": "\u4ece\u51fd\u6570\u8fd4\u56de\u503c", "program": [ "def quadruple(x):", " return x * 4" ], "response": { - "message": "

You cannot use *, +, or even any numbers inside quadruple.\nYou must call double to solve the problem.

", + "message": "

\u4f60\u4e0d\u80fd\u5728 quadruple \u5185\u90e8\u4f7f\u7528 *\u3001+\uff0c\u751a\u81f3\u4efb\u4f55\u6570\u5b57\u3002\n\u4f60\u5fc5\u987b\u8c03\u7528 double \u6765\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\u3002

", "passed": false, "result": [] }, @@ -5443,7 +5149,7 @@ "def quadruple(x):", " return double(double(x))" ], - "page": "Returning Values From Functions", + "page": "\u4ece\u51fd\u6570\u8fd4\u56de\u503c", "program": [ "def double(x):", " return x * 2", @@ -5452,14 +5158,14 @@ " return double(double(x))" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "quadruple_exercise" }, { - "page": "Testing Functions", + "get_solution": "program", + "page": "\u6d4b\u8bd5\u51fd\u6570", "program": [ "def double(x):", " return x * 2", @@ -5468,7 +5174,6 @@ "assert_equal(double(5), 10)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "OK\nOK", @@ -5478,7 +5183,7 @@ "Error! 2 != 4", "OK\nOK", "OK", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5491,7 +5196,8 @@ "step": "introducing_assert_equal" }, { - "page": "Testing Functions", + "get_solution": "program", + "page": "\u6d4b\u8bd5\u51fd\u6570", "program": [ "def double(x):", " return x * 3", @@ -5500,7 +5206,6 @@ "assert_equal(double(5), 10)" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "Error! 6 != 4\nError! 15 != 10", @@ -5509,7 +5214,7 @@ "Error! 6 != 4\nError! 15 != 10", "Error! 4 != 6\nError! 10 != 15", "OK\nOK", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5522,7 +5227,8 @@ "step": "make_tests_fail" }, { - "page": "Testing Functions", + "get_solution": "program", + "page": "\u6d4b\u8bd5\u51fd\u6570", "program": [ "def double(x):", " return x * 2", @@ -5534,7 +5240,6 @@ "assert_equal(quadruple(5), 20)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5546,24 +5251,20 @@ "step": "complete_quadruple_tests" }, { - "get_solution": [ - "def surround(string, sides):", - " return sides + string + sides" - ], - "page": "Testing Functions", + "get_solution": "program", + "page": "\u6d4b\u8bd5\u51fd\u6570", "program": [ "def surround(string, sides):", " return sides + string + sides" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "surround_exercise" }, { - "page": "Testing Functions", + "page": "\u6d4b\u8bd5\u51fd\u6570", "program": [ "def surround(string, sides):", " return sides + string + sides", @@ -5575,21 +5276,21 @@ " return string" ], "response": { - "message": "

In the alert function, you placed your return statement at the wrong place!\nPay attention to your indentations in alert. You might be ending a loop too early with return.

", + "message": "

\u5728 alert \u51fd\u6570\u4e2d\uff0c\u4f60\u628a return \u8bed\u53e5\u653e\u9519\u4e86\u5730\u65b9\uff01\n\u6ce8\u610f\u4f60\u5728 alert \u4e2d\u7684\u7f29\u8fdb\u3002\u4f60\u53ef\u80fd\u8fc7\u65e9\u5730\u7528 return \u7ed3\u675f\u4e86\u4e00\u4e2a\u5faa\u73af\u3002

", "passed": false, "result": [] }, "step": "alert_exercise" }, { - "page": "Testing Functions", + "page": "\u6d4b\u8bd5\u51fd\u6570", "program": [ "def alert(string, level):", " marks = '!' * level", " return marks + ' ' + string + ' ' + marks" ], "response": { - "message": "

You cannot use string concatenation/formatting/interpolation/multiplication or f-strings in alert.\nYou must call surround to solve the problem.

", + "message": "

\u4f60\u4e0d\u80fd\u5728 alert \u4e2d\u4f7f\u7528\u5b57\u7b26\u4e32\u8fde\u63a5/\u683c\u5f0f\u5316/\u63d2\u503c/\u4e58\u6cd5\u6216 f-strings\u3002\n\u4f60\u5fc5\u987b\u8c03\u7528 surround \u6765\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\u3002

", "passed": false, "result": [] }, @@ -5603,7 +5304,7 @@ " string = surround(string, '!')", " return string" ], - "page": "Testing Functions", + "page": "\u6d4b\u8bd5\u51fd\u6570", "program": [ "def surround(string, sides):", " return sides + string + sides", @@ -5615,14 +5316,14 @@ " return string" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "alert_exercise" }, { - "page": "return ends the function call", + "get_solution": "program", + "page": "return\u7ed3\u675f\u51fd\u6570\u8c03\u7528", "program": [ "def foo():", " return 1", @@ -5631,7 +5332,6 @@ "print(foo())" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "1", @@ -5641,7 +5341,7 @@ "[1, 2]", "1\n2", "1 2", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5654,7 +5354,8 @@ "step": "double_return_in_one_function" }, { - "page": "return ends the function call", + "get_solution": "program", + "page": "return\u7ed3\u675f\u51fd\u6570\u8c03\u7528", "program": [ "def double_numbers(numbers):", " for x in numbers:", @@ -5663,7 +5364,6 @@ "assert_equal(double_numbers([1, 2, 3]), [2, 4, 6])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5675,7 +5375,8 @@ "step": "cannot_return_multiple_values" }, { - "page": "return ends the function call", + "get_solution": "program", + "page": "return\u7ed3\u675f\u51fd\u6570\u8c03\u7528", "program": [ "def foo():", " for letter in 'abc':", @@ -5687,7 +5388,6 @@ "foo()" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "a 0\na 1\na 2\nb 0", @@ -5697,7 +5397,7 @@ "a 0\na 1\na 2\nb 0\nb 1\nb 2", "a 0\na 1\na 2\nb 0\nc 0\nc 1\nc 2", "a 0\na 1\na 2\nb 0\nb 1\nb 2\nc 0\nc 1\nc 2", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5710,7 +5410,8 @@ "step": "return_ends_whole_function" }, { - "page": "return ends the function call", + "get_solution": "program", + "page": "return\u7ed3\u675f\u51fd\u6570\u8c03\u7528", "program": [ "def foo():", " for letter in 'abc':", @@ -5722,7 +5423,6 @@ "foo()" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "a 0\na 1\na 2\nb 0\nc 0\nc 1\nc 2", @@ -5732,7 +5432,7 @@ "a 0\na 1\na 2\nb 0\nb 1\nb 2", "a 0\na 1\na 2\nb 0\nc 0\nc 1\nc 2", "a 0\na 1\na 2\nb 0\nb 1\nb 2\nc 0\nc 1\nc 2", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5745,7 +5445,8 @@ "step": "break_vs_return" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "def is_friend(name):", " if name == \"Alice\":", @@ -5760,7 +5461,6 @@ "assert_equal(is_friend(\"Charlie\"), False)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5772,19 +5472,19 @@ "step": "InputAliceBob" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "True or True" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "True", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5797,19 +5497,19 @@ "step": "TrueOrTrue" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "True or False" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "True", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5822,19 +5522,19 @@ "step": "TrueOrFalse" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "False or False" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "False", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5847,7 +5547,8 @@ "step": "FalseOrFalse" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "def is_friend(name):", " if name == \"Alice\" or name == \"Bob\":", @@ -5860,7 +5561,6 @@ "assert_equal(is_friend(\"Charlie\"), False)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5872,7 +5572,8 @@ "step": "ImprovingWithOr" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "def is_friend(name):", " return name == \"Alice\" or name == \"Bob\"", @@ -5882,7 +5583,6 @@ "assert_equal(is_friend(\"Charlie\"), False)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5894,7 +5594,8 @@ "step": "FurtherImprovement" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "def is_friend(name):", " return name == \"Alice\" or \"Bob\"", @@ -5904,7 +5605,6 @@ "assert_equal(is_friend(\"Charlie\"), False)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5916,7 +5616,8 @@ "step": "ACommonMistake" }, { - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "def is_friend(name):", " return name == \"Alice\" or \"Bob\"", @@ -5926,7 +5627,6 @@ "assert_equal(is_friend(\"Charlie\"), False)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -5938,14 +5638,8 @@ "step": "InspectWithBirdseye" }, { - "get_solution": [ - "def is_valid_percentage(x):", - " if x < 0 or x > 100:", - " return False", - " else:", - " return True" - ], - "page": "Introducing or", + "get_solution": "program", + "page": "\u4ecb\u7ecd or", "program": [ "def is_valid_percentage(x):", " if x < 0 or x > 100:", @@ -5954,26 +5648,25 @@ " return True" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "AnExercise" }, { - "page": "Introducing and", + "get_solution": "program", + "page": "\u4ecb\u7ecd and", "program": [ "True and True" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "True", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -5986,19 +5679,19 @@ "step": "TrueAndTrue" }, { - "page": "Introducing and", + "get_solution": "program", + "page": "\u4ecb\u7ecd and", "program": [ "True and False" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "False", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6011,19 +5704,19 @@ "step": "TrueAndFalse" }, { - "page": "Introducing and", + "get_solution": "program", + "page": "\u4ecb\u7ecd and", "program": [ "False and False" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "False", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6036,14 +5729,8 @@ "step": "FalseAndFalse" }, { - "get_solution": [ - "def is_valid_percentage(x):", - " if 0 <= x and x <= 100:", - " return True", - " else:", - " return False" - ], - "page": "Introducing and", + "get_solution": "program", + "page": "\u4ecb\u7ecd and", "program": [ "def is_valid_percentage(x):", " if 0 <= x and x <= 100:", @@ -6052,41 +5739,37 @@ " return False" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "AndExercise" }, { - "get_solution": [ - "def all_equal(row):", - " return row[0] == row[1] and row[0] == row[2]" - ], - "page": "Introducing and", + "get_solution": "program", + "page": "\u4ecb\u7ecd and", "program": [ "def all_equal(row):", " return row[0] == row[1] and row[0] == row[2]" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "TicTacToeWinningRow" }, { - "page": "Multi-line statements", + "get_solution": "program", + "page": "\u591a\u884c\u8bed\u53e5", "program": [ "is_friend = name == \"Alice\" or", " name == \"Bob\"" ], "response": { - "message": "", "passed": true, "result": [ { - "text": " is_friend = name == \"Alice\" or\n ^\nSyntaxError: invalid syntax\nat line 1\n\nA `SyntaxError` occurs when Python cannot understand your code.\n\nThe Python keyword `or` can only be used for boolean expressions.\nPerhaps you meant to write\n\n`is_friend = name == \"Alice\" ,`\n\n\n", + "friendly": "

A SyntaxError occurs when Python cannot understand your code.

\n

The Python keyword or can only be used for boolean expressions.\nPerhaps you meant to write

\n

is_friend = name == \"Alice\" ,

", + "text": " is_friend = name == \"Alice\" or\n ^\nSyntaxError: invalid syntax\n\u5728\u884c 1\n", "type": "syntax_error" } ] @@ -6094,7 +5777,8 @@ "step": "invalid_multiline" }, { - "page": "Multi-line statements", + "get_solution": "program", + "page": "\u591a\u884c\u8bed\u53e5", "program": [ "name = \"Bob\"", "", @@ -6114,7 +5798,6 @@ " name == \"Bob\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6126,12 +5809,12 @@ "step": "valid_multiline" }, { - "page": "Combining and and or", + "get_solution": "program", + "page": "\u7ec4\u5408 and \u548c or", "program": [ "True or False and False" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6143,15 +5826,8 @@ "step": "CombiningAndOr" }, { - "get_solution": [ - "def diagonal_winner(board):", - " middle = board[1][1]", - " return (", - " (middle == board[0][0] and middle == board[2][2]) or", - " (middle == board[0][2] and middle == board[2][0])", - " )" - ], - "page": "Combining and and or", + "get_solution": "program", + "page": "\u7ec4\u5408 and \u548c or", "program": [ "def diagonal_winner(board):", " middle = board[1][1]", @@ -6161,26 +5837,25 @@ " )" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "AndHasHigherPriority" }, { - "page": "Introducing not", + "get_solution": "program", + "page": "\u4ecb\u7ecd not", "program": [ "not True" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "False", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6193,19 +5868,19 @@ "step": "IntroducingNot" }, { - "page": "Introducing not", + "get_solution": "program", + "page": "\u4ecb\u7ecd not", "program": [ "not False" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "True", "choices": [ "True", "False", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6218,13 +5893,13 @@ "step": "NotFalse" }, { - "page": "Introducing not", + "get_solution": "program", + "page": "\u4ecb\u7ecd not", "program": [ "b = True", "print(not b or b)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6236,24 +5911,20 @@ "step": "NotTrueOrTrue" }, { - "get_solution": [ - "def invalid_image(filename):", - " return not (filename.endswith(\".png\") or filename.endswith(\".jpg\"))" - ], - "page": "Introducing not", + "get_solution": "program", + "page": "\u4ecb\u7ecd not", "program": [ "def invalid_image(filename):", " return not (filename.endswith(\".png\") or filename.endswith(\".jpg\"))" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "NotPriority" }, { - "page": "Checking the board for winners", + "page": "\u68c0\u67e5\u68cb\u76d8\u4e0a\u7684\u83b7\u80dc\u8005", "program": [ "def row_winner(board):", " for row in board:", @@ -6268,27 +5939,15 @@ " return False" ], "response": { - "message": "

Keep in mind that some entries might be ' '. An empty row is not a winning row.

", + "message": "

\u8bf7\u8bb0\u4f4f\uff0c\u6709\u4e9b\u6761\u76ee\u53ef\u80fd\u662f ' '\u3002\u7a7a\u884c\u4e0d\u662f\u83b7\u80dc\u884c\u3002

", "passed": false, "result": [] }, "step": "intro_row_winner" }, { - "get_solution": [ - "def row_winner(board):", - " for row in board:", - " all_equal = True", - " piece = row[0]", - " for entry in row:", - " if entry == ' ' or piece != entry:", - " all_equal = False", - " break", - " if all_equal:", - " return True", - " return False" - ], - "page": "Checking the board for winners", + "get_solution": "program", + "page": "\u68c0\u67e5\u68cb\u76d8\u4e0a\u7684\u83b7\u80dc\u8005", "program": [ "def row_winner(board):", " for row in board:", @@ -6303,27 +5962,14 @@ " return False" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "intro_row_winner" }, { - "get_solution": [ - "def column_winner(board):", - " for col in range(len(board[0])):", - " all_equal = True", - " piece = board[0][col]", - " for row in board:", - " if row[col] == ' ' or row[col] != piece:", - " all_equal = False", - " break", - " if all_equal:", - " return True", - " return False" - ], - "page": "Checking the board for winners", + "get_solution": "program", + "page": "\u68c0\u67e5\u68cb\u76d8\u4e0a\u7684\u83b7\u80dc\u8005", "program": [ "def column_winner(board):", " for col in range(len(board[0])):", @@ -6338,27 +5984,14 @@ " return False" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "column_winner" }, { - "get_solution": [ - "def diagonal_winner(board):", - " all_equal1 = True", - " all_equal2 = True", - " topleft = board[0][0]", - " topright = board[0][-1]", - " for i in range(len(board)):", - " if board[i][i] == ' ' or board[i][i] != topleft:", - " all_equal1 = False", - " if board[i][-i - 1] == ' ' or board[i][-i - 1] != topright:", - " all_equal2 = False", - " return all_equal1 or all_equal2" - ], - "page": "Checking the board for winners", + "get_solution": "program", + "page": "\u68c0\u67e5\u68cb\u76d8\u4e0a\u7684\u83b7\u80dc\u8005", "program": [ "def diagonal_winner(board):", " all_equal1 = True", @@ -6373,7 +6006,6 @@ " return all_equal1 or all_equal2" ], "response": { - "message": "", "passed": true, "result": [] }, @@ -6384,7 +6016,7 @@ "def winner(board):", " return row_winner(board) or column_winner(board) or diagonal_winner(board)" ], - "page": "Checking the board for winners", + "page": "\u68c0\u67e5\u68cb\u76d8\u4e0a\u7684\u83b7\u80dc\u8005", "program": [ "def winning_line(strings):", " piece = strings[0]", @@ -6422,14 +6054,14 @@ " return row_winner(board) or column_winner(board) or diagonal_winner(board)" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "winner" }, { - "page": "The newline character, format_board", + "get_solution": "program", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "def print_board(board):", " for row in board:", @@ -6442,7 +6074,6 @@ "])" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6454,7 +6085,8 @@ "step": "one_way_to_print_board" }, { - "page": "The newline character, format_board", + "get_solution": "program", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "assert_equal(", " format_board([", @@ -6468,11 +6100,11 @@ ")" ], "response": { - "message": "", "passed": true, "result": [ { - "text": " \"XOX\n ^\nSyntaxError: EOL while scanning string literal\nat line 7\n\nA `SyntaxError` occurs when Python cannot understand your code.\n\nYou started writing a string with a single or double quote\nbut never ended the string with another quote on that line.\n\n\n", + "friendly": "

A SyntaxError occurs when Python cannot understand your code.

\n

You started writing a string with a single or double quote\nbut never ended the string with another quote on that line.

", + "text": " \"XOX\n ^\nSyntaxError: unterminated string literal (detected at line 7)\n\u5728\u884c 7\n", "type": "syntax_error" } ] @@ -6480,14 +6112,14 @@ "step": "invalid_multi_line_string" }, { - "page": "The newline character, format_board", + "get_solution": "program", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "string = \"\"\"First line", "Second line\"\"\"", "print(string)" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6499,24 +6131,24 @@ "step": "multi_line_strings_triple_quotes" }, { - "page": "The newline character, format_board", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "string = 'a'" ], "response": { - "message": "

Oops, string doesn't have the right value. Run the program from the previous step again.

", + "message": "

\u54ce\u5440\uff0cstring \u7684\u503c\u4e0d\u6b63\u786e\u3002\u8bf7\u518d\u6b21\u8fd0\u884c\u4e0a\u4e00\u6b65\u7684\u7a0b\u5e8f\u3002

", "passed": false, "result": [] }, "step": "discovering_newline" }, { - "page": "The newline character, format_board", + "get_solution": "program", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "string" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6528,19 +6160,19 @@ "step": "discovering_newline" }, { - "page": "The newline character, format_board", + "get_solution": "program", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "len('\\n')" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "1", "choices": [ "1", "2", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6553,17 +6185,8 @@ "step": "introducing_newline" }, { - "get_solution": [ - "def format_board(board):", - " result = ''", - " for i in range(len(board)):", - " for char in board[i]:", - " result += char", - " if i != len(board) - 1:", - " result += '\\n'", - " return result" - ], - "page": "The newline character, format_board", + "get_solution": "program", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "def format_board(board):", " result = ''", @@ -6575,25 +6198,14 @@ " return result" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "format_board_simple" }, { - "get_solution": [ - "def format_board(board):", - " joined_rows = []", - " for row in board:", - " joined_rows.append(\"|\".join(row))", - " lines = []", - " for _ in board[0]:", - " lines.append(\"-\")", - " line = f'\\n{\"+\".join(lines)}\\n'", - " return line.join(joined_rows)" - ], - "page": "The newline character, format_board", + "get_solution": "program", + "page": "\u6362\u884c\u7b26\uff0cformat_board", "program": [ "def format_board(board):", " joined_rows = []", @@ -6606,14 +6218,14 @@ " return line.join(joined_rows)" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "format_board_bonus_challenge" }, { - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "print(type('Hello World'))", "print(type(23))", @@ -6622,7 +6234,6 @@ "print(type(4.56))" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6634,12 +6245,12 @@ "step": "five_different_types" }, { - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "type(3) == int" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6651,14 +6262,14 @@ "step": "check_type_manually" }, { - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "print('123')", "print(123)", "print(123 == '123')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6670,13 +6281,13 @@ "step": "different_types_look_same" }, { - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "print(123 + 456)", "print('123' + '456')" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "579\n123456", @@ -6687,7 +6298,7 @@ "123456\n'123456'", "579\n123456", "579\n'123456'", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6700,13 +6311,13 @@ "step": "plus_has_two_meanings" }, { - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "print(13 < 120)", "print('13' < '120')" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "True\nFalse", @@ -6715,7 +6326,7 @@ "True\nFalse", "False\nTrue", "False\nFalse", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6728,13 +6339,13 @@ "step": "less_than_has_two_meanings" }, { - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "print(sorted([120, 13, 0]))", "print(sorted(['120', '13', '0']))" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "[0, 13, 120]\n['0', '120', '13']", @@ -6745,7 +6356,7 @@ "[120, 13, 0]\n['0', '120', '13']", "[120, 13, 0]\n['13', '120', '0']", "[120, 13, 0]\n['120', '13', '0']", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6758,21 +6369,21 @@ "step": "less_than_sorting_strings" }, { - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "12 + '34'" ], "response": { - "message": "", "passed": true, "prediction": { - "answer": "Error", + "answer": "\u9519\u8bef", "choices": [ "46", "'46'", "1234", "'1234'", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6786,11 +6397,13 @@ }, "frames": [ { + "filename": "/my_program.py", + "lineno": 1, "lines": [ { - "content": "12 + '34'", "is_current": true, "lineno": 1, + "text": "12 + '34'", "type": "line" } ], @@ -6805,7 +6418,7 @@ ], "text": [ "Traceback (most recent call last):", - " File \"my_program.py\", line 1, in ", + " File \"/my_program.py\", line 1, in ", "--> 1 | 12 + '34'", " ^^^^^^^^^", "", @@ -6823,7 +6436,7 @@ " print('Starting... ' + str(i + 1))", "print('Go!')" ], - "page": "Types", + "page": "\u7c7b\u578b", "program": [ "number = '1'", "for i in range(int(number)):", @@ -6831,7 +6444,6 @@ "print('Go!')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6843,18 +6455,8 @@ "step": "fixing_type_errors_with_conversion" }, { - "get_solution": [ - "def format_board(board):", - " first_row = ' '", - " for i in range(len(board)):", - " first_row += str(i + 1)", - " joined_rows = [first_row]", - " for i in range(len(board)):", - " joined_row = str(i + 1) + ''.join(board[i])", - " joined_rows.append(joined_row)", - " return \"\\n\".join(joined_rows)" - ], - "page": "Types", + "get_solution": "program", + "page": "\u7c7b\u578b", "program": [ "def format_board(board):", " first_row = ' '", @@ -6867,21 +6469,20 @@ " return \"\\n\".join(joined_rows)" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "format_board_with_numbers" }, { - "page": "Interactive Programs with input()", + "get_solution": "program", + "page": "\u4f7f\u7528 input() \u7684\u4ea4\u4e92\u5f0f\u7a0b\u5e8f", "program": [ "print('Type your name, then press Enter:')", "name = input()", "print(f'Hello {name}!')" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6906,7 +6507,6 @@ }, { "get_solution": [ - "super_secret_number = 7", "print(\"What number am I thinking of?\")", "guess = input()", "if int(guess) == super_secret_number:", @@ -6914,7 +6514,7 @@ "else:", " print(\"Nope!\")" ], - "page": "Interactive Programs with input()", + "page": "\u4f7f\u7528 input() \u7684\u4ea4\u4e92\u5f0f\u7a0b\u5e8f", "program": [ "super_secret_number = 7", "print(\"What number am I thinking of?\")", @@ -6925,7 +6525,6 @@ " print(\"Nope!\")" ], "response": { - "message": "", "passed": true, "result": [ { @@ -6949,7 +6548,8 @@ "step": "convert_input_to_int" }, { - "page": "Nested List Assignment: Playing Moves on the Board", + "get_solution": "program", + "page": "\u5d4c\u5957\u5217\u8868\u4f5c\u4e1a\uff1a\u5728\u68cb\u76d8\u4e0a\u8fdb\u884c\u79fb\u52a8", "program": [ "def play_move(board, player):", " board[1] = player", @@ -6962,7 +6562,6 @@ "play_game()" ], "response": { - "message": "", "passed": true, "prediction": { "answer": "[' ', 'X', ' ']", @@ -6976,7 +6575,7 @@ "['X', ' ', ' ']", "[' ', 'X', ' ']", "[' ', ' ', 'X']", - "Error" + "\u9519\u8bef" ] }, "result": [ @@ -6989,7 +6588,8 @@ "step": "modify_list_in_function" }, { - "page": "Nested List Assignment: Playing Moves on the Board", + "get_solution": "program", + "page": "\u5d4c\u5957\u5217\u8868\u4f5c\u4e1a\uff1a\u5728\u68cb\u76d8\u4e0a\u8fdb\u884c\u79fb\u52a8", "program": [ "def play_move(board, player):", " row = board[1]", @@ -7007,7 +6607,6 @@ "play_game()" ], "response": { - "message": "", "passed": true, "result": [ { @@ -7019,13 +6618,8 @@ "step": "nested_assignment_two_lines" }, { - "get_solution": [ - "def play_move(board, player):", - " row = int(input()) - 1", - " col = int(input()) - 1", - " board[row][col] = player" - ], - "page": "Nested List Assignment: Playing Moves on the Board", + "get_solution": "program", + "page": "\u5d4c\u5957\u5217\u8868\u4f5c\u4e1a\uff1a\u5728\u68cb\u76d8\u4e0a\u8fdb\u884c\u79fb\u52a8", "program": [ "def play_move(board, player):", " row = int(input()) - 1", @@ -7033,14 +6627,14 @@ " board[row][col] = player" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "nested_assignment_input" }, { - "page": "Making the Board", + "get_solution": "program", + "page": "\u5236\u4f5c\u68cb\u76d8", "program": [ "def make_board(size):", " row = []", @@ -7068,7 +6662,6 @@ "test()" ], "response": { - "message": "", "passed": true, "result": [ { @@ -7080,30 +6673,21 @@ "step": "naive_make_board" }, { - "page": "Making the Board", + "page": "\u5236\u4f5c\u68cb\u76d8", "program": [ "pass", "def make_board(size): return [[' '] * size] * size" ], "response": { - "message": "

The sublists in the result are not all separate objects.

", + "message": "

\u7ed9\u5b9a\u8fd9\u4e9b\u503c\uff1a

\n
size = 2\n
\n

\u4f60\u7684\u4ee3\u7801\u8f93\u51fa\uff1a

\n
[[' ', ' '], [' ', ' ']]\n
\n

\u54ea\u4e00\u4e2a\u662f\u6b63\u786e\u7684\uff01

\n

\u7136\u800c\uff0c\u7ed3\u679c\u4e2d\u7684\u5b50\u5217\u8868\u5e76\u4e0d\u662f\u6240\u6709\u72ec\u7acb\u7684\u5bf9\u8c61\u3002

", "passed": false, "result": [] }, "step": "fix_make_board" }, { - "get_solution": [ - "def make_board(size):", - " board = []", - " for _ in range(size):", - " row = []", - " for _ in range(size):", - " row.append(' ')", - " board.append(row)", - " return board" - ], - "page": "Making the Board", + "get_solution": "program", + "page": "\u5236\u4f5c\u68cb\u76d8", "program": [ "def make_board(size):", " board = []", @@ -7115,7 +6699,6 @@ " return board" ], "response": { - "message": "", "passed": true, "result": [] }, @@ -7142,7 +6725,7 @@ "", " print_draw()" ], - "page": "The Full Tic-Tac-Toe Game", + "page": "\u5b8c\u6574\u7684\u4e95\u5b57\u68cb\u6e38\u620f", "program": [ "def winning_line(strings):", " strings = set(strings)", @@ -7205,10 +6788,445 @@ " print_draw()" ], "response": { - "message": "", "passed": true, "result": [] }, "step": "the_full_game" + }, + { + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french = {'apple': 'pomme', 'box': 'boite'}" + ], + "response": { + "passed": true, + "result": [] + }, + "step": "first_dict" + }, + { + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french = {}" + ], + "response": { + "message": "

\u54ce\u5440\uff0c\u4f60\u9700\u8981\u5148\u8bbe\u7f6e french = {'apple': 'pomme', 'box': 'boite'}\uff0c\u624d\u80fd\u7ee7\u7eed\u3002

", + "passed": false, + "result": [] + }, + "step": "dict_access" + }, + { + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french[0]" + ], + "response": { + "passed": true, + "result": [ + { + "data": [ + { + "didyoumean": [], + "exception": { + "message": "0", + "type": "KeyError" + }, + "frames": [ + { + "filename": "/my_program.py", + "lineno": 1, + "lines": [ + { + "is_current": true, + "lineno": 1, + "text": "french[0]", + "type": "line" + } + ], + "name": "", + "type": "frame", + "variables": [ + { + "name": "french\n", + "value": "{'apple': 'pomme', 'box': 'boite'}\n" + } + ] + } + ], + "friendly": "

A KeyError is raised when a value is not found as a\nkey in a Python dict or in a similar object.

\n

The key 0 cannot be found in the dict french.

", + "tail": "" + } + ], + "text": [ + "Traceback (most recent call last):", + " File \"/my_program.py\", line 1, in ", + "--> 1 | french[0]", + " ^^^^^^^^^", + "french = {'apple': 'pomme', 'box': 'boite'}", + "", + "KeyError: 0" + ], + "type": "traceback" + } + ] + }, + "step": "dict_access" + }, + { + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french = {}" + ], + "response": { + "message": "

\u54ce\u5440\uff0c\u4f60\u9700\u8981\u5148\u8bbe\u7f6e french = {'apple': 'pomme', 'box': 'boite'}\uff0c\u624d\u80fd\u7ee7\u7eed\u3002

", + "passed": false, + "result": [] + }, + "step": "dict_access2" + }, + { + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french['apple']" + ], + "response": { + "passed": true, + "result": [ + { + "text": "'pomme'\n", + "type": "stdout" + } + ] + }, + "step": "dict_access2" + }, + { + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french = {}" + ], + "response": { + "message": "

\u54ce\u5440\uff0c\u4f60\u9700\u8981\u5148\u8bbe\u7f6e french = {'apple': 'pomme', 'box': 'boite'}\uff0c\u624d\u80fd\u7ee7\u7eed\u3002

", + "passed": false, + "result": [] + }, + "step": "dict_access3" + }, + { + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french['box']" + ], + "response": { + "passed": true, + "result": [ + { + "text": "'boite'\n", + "type": "stdout" + } + ] + }, + "step": "dict_access3" + }, + { + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french = {}" + ], + "response": { + "message": "

\u54ce\u5440\uff0c\u4f60\u9700\u8981\u5148\u8bbe\u7f6e french = {'apple': 'pomme', 'box': 'boite'}\uff0c\u624d\u80fd\u7ee7\u7eed\u3002

", + "passed": false, + "result": [] + }, + "step": "dict_access4" + }, + { + "get_solution": "program", + "page": "\u4ecb\u7ecd\u5b57\u5178", + "program": [ + "french['pomme']" + ], + "response": { + "passed": true, + "result": [ + { + "data": [ + { + "didyoumean": [], + "exception": { + "message": "'pomme'", + "type": "KeyError" + }, + "frames": [ + { + "filename": "/my_program.py", + "lineno": 1, + "lines": [ + { + "is_current": true, + "lineno": 1, + "text": "french['pomme']", + "type": "line" + } + ], + "name": "", + "type": "frame", + "variables": [ + { + "name": "french\n", + "value": "{'apple': 'pomme', 'box': 'boite'}\n" + } + ] + } + ], + "friendly": "

A KeyError is raised when a value is not found as a\nkey in a Python dict or in a similar object.

\n

The key 'pomme' cannot be found in the dict french.

", + "tail": "" + } + ], + "text": [ + "Traceback (most recent call last):", + " File \"/my_program.py\", line 1, in ", + "--> 1 | french['pomme']", + " ^^^^^^^^^^^^^^^", + "french = {'apple': 'pomme', 'box': 'boite'}", + "", + "KeyError: 'pomme'" + ], + "type": "traceback" + } + ] + }, + "step": "dict_access4" + }, + { + "get_solution": "program", + "page": "\u5728\u5b9e\u8df5\u4e2d\u4f7f\u7528\u5b57\u5178", + "program": [ + "def total_cost(cart, prices):", + " result = 0", + " for item in cart:", + " price = prices[item]", + " result += price", + " return result" + ], + "response": { + "passed": true, + "result": [] + }, + "step": "shopping_cart1" + }, + { + "get_solution": "program", + "page": "\u5728\u5b9e\u8df5\u4e2d\u4f7f\u7528\u5b57\u5178", + "program": [ + "def total_cost(cart, quantities, prices):", + " result = 0", + " for item in cart:", + " price = prices[item]", + " quantity = quantities[item]", + " result += price * quantity", + " return result" + ], + "response": { + "passed": true, + "result": [] + }, + "step": "shopping_cart4" + }, + { + "get_solution": "program", + "page": "\u5728\u5b9e\u8df5\u4e2d\u4f7f\u7528\u5b57\u5178", + "program": [ + "def substitute(string):", + " result = ''", + " for char in string:", + " if char == 'A':", + " char = 'T'", + " elif char == 'T':", + " char = 'A'", + " elif char == 'G':", + " char = 'C'", + " elif char == 'C':", + " char = 'G'", + " result += char", + " return result", + "", + "original = 'AGTAGCGTCCTTAGTTACAGGATGGCTTAT'", + "expected = 'TCATCGCAGGAATCAATGTCCTACCGAATA'", + "assert_equal(substitute(original), expected)" + ], + "response": { + "passed": true, + "result": [ + { + "text": "OK\n", + "type": "stdout" + } + ] + }, + "step": "dna_part1" + }, + { + "get_solution": "program", + "page": "\u5728\u5b9e\u8df5\u4e2d\u4f7f\u7528\u5b57\u5178", + "program": [ + "def substitute(string, d):", + " result = \"\"", + " for letter in string:", + " result += d[letter]", + " return result" + ], + "response": { + "passed": true, + "result": [] + }, + "step": "dna_part2" + }, + { + "get_solution": "program", + "page": "\u904d\u5386\u5b57\u5178\u952e", + "program": [ + "quantities = {'apple': 1, 'cat': 10}", + "print(quantities.keys())" + ], + "response": { + "passed": true, + "result": [ + { + "text": "dict_keys(['apple', 'cat'])\n", + "type": "stdout" + } + ] + }, + "step": "introducing_keys" + }, + { + "get_solution": "program", + "page": "\u904d\u5386\u5b57\u5178\u952e", + "program": [ + "quantities = {'apple': 1, 'cat': 10}", + "for key in quantities.keys():", + " print(key)" + ], + "response": { + "passed": true, + "result": [ + { + "text": "apple\ncat\n", + "type": "stdout" + } + ] + }, + "step": "keys_are_iterable" + }, + { + "get_solution": "program", + "page": "\u904d\u5386\u5b57\u5178\u952e", + "program": [ + "quantities = {'apple': 1, 'cat': 10}", + "for key in quantities:", + " print(key)" + ], + "response": { + "passed": true, + "result": [ + { + "text": "apple\ncat\n", + "type": "stdout" + } + ] + }, + "step": "keys_are_iterable2" + }, + { + "get_solution": "program", + "page": "\u904d\u5386\u5b57\u5178\u952e", + "program": [ + "def total_cost(quantities, prices):", + " result = 0", + " for item in quantities:", + " price = prices[item]", + " quantity = quantities[item]", + " result += price * quantity", + " return result" + ], + "response": { + "passed": true, + "result": [] + }, + "step": "cleanup_shopping_cart" + }, + { + "get_solution": "program", + "page": "\u904d\u5386\u5b57\u5178\u952e", + "program": [ + "def print_words(french):", + " for word in french:", + " print(\"English: \" + word)", + " print(\"French: \" + french[word])", + " print(\"---\")" + ], + "response": { + "passed": true, + "result": [] + }, + "step": "english_to_french" + }, + { + "get_solution": "program", + "page": "\u904d\u5386\u5b57\u5178\u952e", + "program": [ + "def print_words(french, german):", + " for word in french:", + " print(\"English: \" + word)", + " print(\"French: \" + french[word])", + " print(\"German: \" + german[word])", + " print(\"---\")" + ], + "response": { + "passed": true, + "result": [] + }, + "step": "english_to_german" + }, + { + "get_solution": "program", + "page": "\u904d\u5386\u5b57\u5178\u952e", + "program": [ + "def print_words(words):", + " for word in words:", + " translations = words[word]", + "", + " print(f\"English: {word}\")", + " for language in translations:", + " print(f\"{language}: {translations[language]}\")", + " print(f\"---\")", + "", + "print_words({", + " 'apple': {", + " 'French': 'pomme',", + " 'German': 'apfel',", + " },", + " 'box': {", + " 'French': 'boite',", + " 'German': 'kasten',", + " },", + "})" + ], + "response": { + "passed": true, + "result": [ + { + "text": "English: apple\nFrench: pomme\nGerman: apfel\n---\nEnglish: box\nFrench: boite\nGerman: kasten\n---\n", + "type": "stdout" + } + ] + }, + "step": "nested_dictionaries" } ] \ No newline at end of file diff --git a/translations/locales/zh/LC_MESSAGES/futurecoder.mo b/translations/locales/zh/LC_MESSAGES/futurecoder.mo index e79c1c48..658f0ce5 100644 Binary files a/translations/locales/zh/LC_MESSAGES/futurecoder.mo and b/translations/locales/zh/LC_MESSAGES/futurecoder.mo differ diff --git a/translations/translate_futurecoder.py b/translations/translate_futurecoder.py new file mode 100644 index 00000000..382914ae --- /dev/null +++ b/translations/translate_futurecoder.py @@ -0,0 +1,983 @@ +import polib +import json +import os +from pathlib import Path +from typing import List, Dict, Any, Optional +import requests +from dataclasses import dataclass +import time +from concurrent.futures import ThreadPoolExecutor +import re +import argparse +from enum import Enum +from collections import defaultdict +import logging +import sys +import glob +from concurrent.futures import as_completed +import subprocess +import shutil + +# Configure logging +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.StreamHandler(sys.stdout) + ] +) +logger = logging.getLogger(__name__) + +@dataclass +class TranslationRules: + """Translation rules for a specific language""" + language_name: str + language_code: str + plural_forms: str + context: str + +@dataclass +class TranslationConfig: + """Configuration for translation service""" + api_key: str + base_url: str + model: str + +# Base teaching context that applies to all languages +BASE_TEACHING_CONTEXT = """You are a friendly and patient high school Python teacher translating from English to {language_name}. +Your goal is to make Python programming accessible and engaging for young students. + +Follow these translation rules: +1. Use clear, simple language that high school students can understand +2. Keep explanations friendly and encouraging +3. Maintain a teaching tone that guides students through concepts +4. Keep all code, variables, and technical terms in English +5. Preserve all markdown formatting, code blocks, and special markers +6. Maintain all HTML tags and their attributes +7. Keep all placeholders (e.g., {{variable}}, %s, etc.) unchanged +8. Preserve all newlines and spacing +9. Keep all numbers and units unchanged +10. Maintain all punctuation style +11. Keep all URLs and email addresses unchanged +12. Preserve all special characters and emojis +13. IMPORTANT: Never translate or modify special strings that start with double underscores (e.g., __copyable__, __code0__, __program__) +14. Keep all special strings exactly as they appear in the original text + +Remember to: +- Use age-appropriate analogies and examples +- Break down complex concepts into simpler terms +- Maintain an encouraging and supportive tone +- Keep technical explanations clear but not oversimplified""" + +class Language(Enum): + """Supported languages and their translation rules""" + ZH = TranslationRules( + language_name="Chinese", + language_code="zh", + plural_forms="nplurals=1; plural=0;", + context=BASE_TEACHING_CONTEXT.format(language_name="Chinese (Simplified)") + ) + FR = TranslationRules( + language_name="French", + language_code="fr", + plural_forms="nplurals=2; plural=(n > 1);", + context=BASE_TEACHING_CONTEXT.format(language_name="French") + ) + # Example of how to add a new language: + # ES = TranslationRules( + # language_name="Spanish", + # language_code="es", + # plural_forms="nplurals=2; plural=(n != 1);", + # context=BASE_TEACHING_CONTEXT.format(language_name="Spanish") + # ) + + @classmethod + def get_rules(cls, language_code: str) -> TranslationRules: + """Get translation rules for a language code.""" + try: + return cls[language_code.upper()].value + except KeyError: + raise ValueError(f"Unsupported language code: {language_code}") + + @classmethod + def add_language(cls, language_code: str, language_name: str, plural_forms: str) -> None: + """Add a new language to the supported languages. + + Args: + language_code: Two-letter language code (e.g., 'es' for Spanish) + language_name: Full name of the language + plural_forms: Plural forms expression for the language + """ + # Create new TranslationRules instance + rules = TranslationRules( + language_name=language_name, + language_code=language_code, + plural_forms=plural_forms, + context=BASE_TEACHING_CONTEXT.format(language_name=language_name) + ) + + # Add to enum + cls._member_map_[language_code.upper()] = rules + cls._member_names_.append(language_code.upper()) + cls._value2member_map_[rules] = rules + +class POFileSplitter: + def __init__(self, input_file: str, base_output_dir: str, chunk_size: int): + """Initialize the PO file splitter. + + Args: + input_file: Path to the input PO file + base_output_dir: Base directory for chunks (will create chunks/en/ subdirectory) + chunk_size: Maximum number of entries per chunk (for large chapters) + """ + self.input_file = input_file + self.base_output_dir = Path(base_output_dir) + self.en_chunks_dir = self.base_output_dir / "chunks" / "en" + self.chunk_size = chunk_size + self.logger = logging.getLogger(__name__) + self.logger.info(f"Initializing POFileSplitter with input: {input_file}, output: {self.en_chunks_dir}, max chunk size: {chunk_size}") + + def extract_chapter_name(self, msgid: str) -> str: + """Extract chapter name from msgid.""" + # Handle different msgid patterns + if msgid.startswith('pages.'): + # Extract chapter from patterns like: pages.IntroducingNestedLoops.steps.crack_password_exercise.hints.0.text + parts = msgid.split('.') + if len(parts) >= 2: + return parts[1] # IntroducingNestedLoops + elif msgid.startswith('frontend.'): + # Frontend UI elements + return 'frontend_ui' + elif 'hint' in msgid.lower(): + # Hint-related entries + return 'hints' + elif 'code_bits.' in msgid: + # Code bits + return 'code_bits' + elif any(marker in msgid for marker in ['__program__', '__code', '__copyable__']): + # Special markers + return 'special_markers' + else: + # General/uncategorized entries + return 'general' + + def split_entries(self) -> None: + """Split the PO file into chunks organized by chapters and save to chunks/en/.""" + try: + # Load the PO file + self.logger.info(f"Loading PO file: {self.input_file}") + po_file = polib.pofile(self.input_file) + total_entries = len(po_file) + self.logger.info(f"Found {total_entries} entries to split") + + # Group entries by chapter + chapters = defaultdict(list) + for entry in po_file: + chapter_name = self.extract_chapter_name(entry.msgid) + chapters[chapter_name].append(entry) + + self.logger.info(f"Organized entries into {len(chapters)} chapters:") + for chapter, entries in chapters.items(): + self.logger.info(f" - {chapter}: {len(entries)} entries") + + # Create English chunks directory + self.en_chunks_dir.mkdir(parents=True, exist_ok=True) + self.logger.info(f"Created English chunks directory: {self.en_chunks_dir}") + + # Save each chapter as one or more chunks to en/ directory + chunk_count = 0 + for chapter_name, entries in chapters.items(): + # If chapter has too many entries, split into multiple chunks + if len(entries) <= self.chunk_size: + # Single chunk for this chapter + chunk_count += 1 + chunk_file = self.en_chunks_dir / f"{chapter_name}.po" + self.save_chunk(entries, chunk_file, po_file.metadata) + self.logger.info(f"Saved chapter '{chapter_name}' to {chunk_file}") + else: + # Split large chapter into multiple chunks + num_chunks = (len(entries) + self.chunk_size - 1) // self.chunk_size + for i in range(num_chunks): + start_idx = i * self.chunk_size + end_idx = min((i + 1) * self.chunk_size, len(entries)) + chunk_entries = entries[start_idx:end_idx] + + chunk_count += 1 + chunk_file = self.en_chunks_dir / f"{chapter_name}_part{i+1:02d}.po" + self.save_chunk(chunk_entries, chunk_file, po_file.metadata) + self.logger.info(f"Saved chapter '{chapter_name}' part {i+1}/{num_chunks} to {chunk_file}") + + self.logger.info(f"Successfully split PO file into {chunk_count} chapter-based chunks in {self.en_chunks_dir}") + + except Exception as e: + self.logger.error(f"Error splitting PO file: {str(e)}") + raise + + def save_chunk(self, entries: List[polib.POEntry], chunk_file: Path, metadata: dict): + """Save a chunk of entries to a PO file.""" + chunk_po = polib.POFile() + chunk_po.metadata = metadata.copy() + chunk_po.extend(entries) + chunk_po.save(str(chunk_file)) + +class Translator: + def __init__(self, config: TranslationConfig, language_code: str): + """Initialize the translator. + + Args: + config: Translation configuration + language_code: Language code to translate to + """ + self.config = config + self.language_code = language_code + self.rules = Language.get_rules(language_code) + self.logger = logging.getLogger(__name__) + self.logger.info(f"Initialized translator for {self.rules.language_name}") + + # Initialize HTTP session + self.session = requests.Session() + self.session.headers.update({ + "Authorization": f"Bearer {config.api_key}", + "Content-Type": "application/json" + }) + + def is_chunk_translated(self, chunk_file: str) -> bool: + """Check if a chunk file is already translated.""" + try: + chunk = polib.pofile(chunk_file) + total_entries = len(chunk) + translated_entries = 0 + + for entry in chunk: + if entry.msgstr and entry.msgstr.strip(): + translated_entries += 1 + elif entry.msgstr_plural: + # Check if any plural form is translated + if any(msgstr.strip() for msgstr in entry.msgstr_plural.values()): + translated_entries += 1 + + # Consider chunk translated if at least 80% of entries are translated + translation_percentage = (translated_entries / total_entries) * 100 if total_entries > 0 else 0 + is_translated = translation_percentage >= 80 + + self.logger.debug(f"Chunk {os.path.basename(chunk_file)}: {translated_entries}/{total_entries} entries translated ({translation_percentage:.1f}%)") + + return is_translated + + except Exception as e: + self.logger.error(f"Error checking translation status of {chunk_file}: {str(e)}") + return False + + def prepare_translation_request(self, chunk: polib.POFile) -> Dict: + """Prepare the translation request payload.""" + # Convert PO entries to text format + entries_text = [] + for entry in chunk: + # Add special markers to help preserve special strings and formatting + entry_text = f"msgid: {entry.msgid}\n" + if entry.msgstr: + # Mark special strings with a unique marker + msgstr = entry.msgstr + + # Special handling for misc terms + if entry.msgid.startswith("misc_terms."): + # For misc terms, preserve all newlines and formatting exactly + msgstr = msgstr.replace('\n', '') + # Also preserve markdown formatting + msgstr = msgstr.replace('*', '') + msgstr = msgstr.replace('**', '') + msgstr = msgstr.replace('`', '') + msgstr = msgstr.replace('[', '') + msgstr = msgstr.replace(']', '') + msgstr = msgstr.replace('(', '') + msgstr = msgstr.replace(')', '') + else: + # For regular entries, just preserve newlines + msgstr = msgstr.replace('\n', '') + + # Mark special strings + msgstr = re.sub(r'(__\w+__)', r'\1', msgstr) + entry_text += f"msgstr: {msgstr}\n" + if entry.msgid_plural: + entry_text += f"msgid_plural: {entry.msgid_plural}\n" + for i, msgstr in enumerate(entry.msgstr_plural.values()): + # Mark newlines and special strings in plural forms too + msgstr = msgstr.replace('\n', '') + msgstr = re.sub(r'(__\w+__)', r'\1', msgstr) + entry_text += f"msgstr[{i}]: {msgstr}\n" + entries_text.append(entry_text) + + # Combine all entries + text_to_translate = "\n\n".join(entries_text) + self.logger.debug(f"Prepared text to translate (first 100 chars): {text_to_translate[:100]}") + + # Prepare the system message with rules + system_message = f"""You are a professional translator. Your task is to translate the following PO file entries from English to {self.rules.language_name}. + +{self.rules.context} + +The input will be in PO file format with msgid (English) and msgstr (translation) pairs. +Only translate the text in the msgstr fields, keeping all other formatting, markers, and structure exactly as is. + +Special formatting rules: +1. Special strings marked with tags must be preserved exactly as they are +2. Newlines marked with must be preserved exactly as they are, including their position and count +3. For misc_terms entries (starting with "misc_terms."): + - Preserve all newlines exactly as they appear in the original + - Preserve all markdown formatting: + * for italics (*) + * for bold (**) + * for code blocks (`) + * and for links ([ and ]) + * and for URLs (( and )) + - Keep all URLs and links unchanged + - Maintain the exact same number of newlines as the original + +Return the translated text in the same PO file format.""" + + return { + "model": self.config.model, + "messages": [ + {"role": "system", "content": system_message}, + {"role": "user", "content": text_to_translate} + ], + "temperature": 0.3 + } + + def translate_chunk(self, en_chunk_file: str, target_chunk_file: str) -> Optional[polib.POFile]: + """Translate a chunk from English to target language.""" + try: + # Check if target chunk already exists and is translated + if os.path.exists(target_chunk_file) and self.is_chunk_translated(target_chunk_file): + self.logger.info(f"Skipping already translated chunk: {os.path.basename(target_chunk_file)}") + return polib.pofile(target_chunk_file) + + self.logger.info(f"Translating {os.path.basename(en_chunk_file)} → {os.path.basename(target_chunk_file)}") + + # Load the English chunk + self.logger.debug(f"Loading English chunk: {en_chunk_file}") + chunk = polib.pofile(en_chunk_file) + self.logger.debug(f"Loaded chunk with {len(chunk)} entries") + + # Prepare translation request + payload = self.prepare_translation_request(chunk) + self.logger.debug("Prepared translation request") + + # Make API call + self.logger.debug(f"Making API call to {self.config.base_url}") + response = self.session.post(self.config.base_url, json=payload) + + if response.status_code == 200: + result = response.json() + translated_text = result['choices'][0]['message']['content'] + self.logger.debug(f"Received translation (first 100 chars): {translated_text[:100]}") + + # Parse the translated text and update the chunk + updated_chunk = self.parse_translated_text(translated_text, chunk) + + if updated_chunk: + # Ensure target directory exists + os.makedirs(os.path.dirname(target_chunk_file), exist_ok=True) + # Save to target language directory + updated_chunk.save(target_chunk_file) + self.logger.info(f"Successfully translated and saved: {os.path.basename(target_chunk_file)}") + return updated_chunk + else: + self.logger.error(f"Failed to parse translated text for {en_chunk_file}") + return None + else: + self.logger.error(f"API error {response.status_code}: {response.text}") + return None + + except Exception as e: + self.logger.error(f"Error translating chunk {en_chunk_file}: {str(e)}") + return None + + def parse_translated_text(self, translated_text: str, original_chunk: polib.POFile) -> Optional[polib.POFile]: + """Parse the translated text and update the original chunk with translations.""" + try: + self.logger.debug("Parsing translated text") + translated_lines = translated_text.split('\n') + current_entry = None + current_key = None + + for line in translated_lines: + line = line.strip() + if not line: + continue + + if line.startswith('msgid: '): + current_key = 'msgid' + msgid = line[7:].strip() + # Find the entry in the original chunk + current_entry = original_chunk.find(msgid) + if current_entry: + self.logger.debug(f"Found entry for msgid: {msgid[:50]}...") + else: + self.logger.warning(f"Could not find entry for msgid: {msgid[:50]}...") + + elif line.startswith('msgid_plural: '): + current_key = 'msgid_plural' + msgid_plural = line[14:].strip() + if current_entry and current_entry.msgid_plural == msgid_plural: + self.logger.debug(f"Found entry for msgid_plural: {msgid_plural[:50]}...") + + elif line.startswith('msgstr: '): + if current_entry and current_key == 'msgid': + translation = line[8:].strip() + + # Special handling for misc terms + if current_entry.msgid.startswith("misc_terms."): + # Restore markdown formatting + translation = translation.replace('', '*') + translation = translation.replace('', '**') + translation = translation.replace('', '`') + translation = translation.replace('', '[') + translation = translation.replace('', ']') + translation = translation.replace('', '(') + translation = translation.replace('', ')') + + # Restore newlines and special strings + translation = translation.replace('', '\n') + translation = re.sub(r'(__\w+__)', r'\1', translation) + + if translation and translation != current_entry.msgid: # Only update if actually translated + current_entry.msgstr = translation + self.logger.debug(f"Updated msgstr for entry: {current_entry.msgid[:50]}...") + + elif line.startswith('msgstr[0]: '): + if current_entry and current_key == 'msgid_plural': + translation = line[11:].strip() + # Restore newlines and special strings + translation = translation.replace('', '\n') + translation = re.sub(r'(__\w+__)', r'\1', translation) + if translation: + current_entry.msgstr_plural[0] = translation + self.logger.debug(f"Updated msgstr[0] for plural entry") + + elif line.startswith('msgstr[1]: '): + if current_entry and current_key == 'msgid_plural': + translation = line[11:].strip() + # Restore newlines and special strings + translation = translation.replace('', '\n') + translation = re.sub(r'(__\w+__)', r'\1', translation) + if translation: + current_entry.msgstr_plural[1] = translation + self.logger.debug(f"Updated msgstr[1] for plural entry") + + self.logger.debug(f"Successfully parsed translated text") + return original_chunk + + except Exception as e: + self.logger.error(f"Error parsing translated text: {str(e)}") + return None + +class TranslationMerger: + def __init__(self, chunks_dir: str, output_file: str, language_code: str): + self.chunks_dir = chunks_dir + self.output_file = output_file + self.language_code = language_code + self.logger = logging.getLogger(__name__) + # Define the locales directory path + self.locales_dir = Path(os.path.dirname(output_file)) / "locales" / language_code / "LC_MESSAGES" + self.locales_dir.mkdir(parents=True, exist_ok=True) + # Load English PO file for reference + self.english_po = polib.pofile(os.path.join(os.path.dirname(output_file), "english.po")) + + def validate_special_strings(self, entry: polib.POEntry, original_entry: polib.POEntry) -> bool: + """Validate that all special strings from the original entry are preserved in the translation.""" + try: + # Extract special strings from original entry with their positions + original_special_strings = [] + for match in re.finditer(r'(__\w+__)', original_entry.msgstr): + original_special_strings.append((match.group(1), match.start())) + + # Extract special strings from translated entry with their positions + translated_special_strings = [] + for match in re.finditer(r'(__\w+__)', entry.msgstr): + translated_special_strings.append((match.group(1), match.start())) + + # Check if we have the same number of special strings + if len(original_special_strings) != len(translated_special_strings): + self.logger.warning(f"Mismatched special strings count for {entry.msgid[:50]}...") + self.logger.warning(f"Original has {len(original_special_strings)} special strings: {[s[0] for s in original_special_strings]}") + self.logger.warning(f"Translation has {len(translated_special_strings)} special strings: {[s[0] for s in translated_special_strings]}") + return False + + # Check if each special string matches and is in a similar relative position + for (orig_str, orig_pos), (trans_str, trans_pos) in zip(original_special_strings, translated_special_strings): + if orig_str != trans_str: + self.logger.warning(f"Mismatched special string in {entry.msgid[:50]}...") + self.logger.warning(f"Original: {orig_str} at position {orig_pos}") + self.logger.warning(f"Translation: {trans_str} at position {trans_pos}") + return False + + # Calculate relative positions (as percentage of total length) + orig_rel_pos = orig_pos / len(original_entry.msgstr) + trans_rel_pos = trans_pos / len(entry.msgstr) + + # Allow some flexibility in position (within 20% of the text length) + if abs(orig_rel_pos - trans_rel_pos) > 0.2: + self.logger.warning(f"Special string {orig_str} is too far from expected position in {entry.msgid[:50]}...") + self.logger.warning(f"Original relative position: {orig_rel_pos:.2f}") + self.logger.warning(f"Translation relative position: {trans_rel_pos:.2f}") + return False + + return True + + except Exception as e: + self.logger.error(f"Error validating special strings: {str(e)}") + return False + + def fix_special_strings(self, entry: polib.POEntry, original_entry: polib.POEntry) -> polib.POEntry: + """Fix missing or misplaced special strings in the translation by copying them from the original entry.""" + try: + # Extract special strings from original entry with their context + original_special_strings = [] + original_text = original_entry.msgstr + translated_text = entry.msgstr + + # Find all special strings and their surrounding context + for match in re.finditer(r'(__\w+__)', original_text): + special_str = match.group(1) + start = max(0, match.start() - 20) # Get 20 chars before + end = min(len(original_text), match.end() + 20) # Get 20 chars after + context = original_text[start:end] + original_special_strings.append((special_str, context, match.start())) + + # For each special string in the original, ensure it exists in the translation + fixed_text = translated_text + for special_str, context, orig_pos in original_special_strings: + # Check if the special string exists in the translation + if special_str not in fixed_text: + # Find a good position to insert the special string + # Try to find similar context in the translation + context_words = set(re.findall(r'\w+', context)) + best_pos = 0 + best_match = 0 + + # Look for similar context in the translation + for i in range(len(fixed_text) - len(context) + 1): + trans_context = fixed_text[i:i + len(context)] + trans_words = set(re.findall(r'\w+', trans_context)) + common_words = len(context_words & trans_words) + if common_words > best_match: + best_match = common_words + best_pos = i + + # Insert the special string at the best position + if best_match > 0: + fixed_text = fixed_text[:best_pos] + special_str + fixed_text[best_pos:] + else: + # If no good context found, append at the end + fixed_text += f"\n{special_str}\n" + + self.logger.info(f"Added missing special string {special_str} to translation") + + # Create a new entry with the fixed translation + fixed_entry = polib.POEntry( + msgid=entry.msgid, + msgstr=fixed_text, + msgid_plural=entry.msgid_plural, + msgstr_plural=entry.msgstr_plural, + comment=entry.comment, + tcomment=entry.tcomment, + occurrences=entry.occurrences, + flags=entry.flags, + previous_msgid=entry.previous_msgid, + previous_msgid_plural=entry.previous_msgid_plural, + encoding=entry.encoding + ) + + # Validate the fix + if not self.validate_special_strings(fixed_entry, original_entry): + self.logger.warning(f"Failed to fix special strings for {entry.msgid[:50]}...") + # If validation fails, return the original entry's msgstr + fixed_entry.msgstr = original_entry.msgstr + self.logger.info(f"Using original English text for {entry.msgid[:50]}...") + + return fixed_entry + + except Exception as e: + self.logger.error(f"Error fixing special strings: {str(e)}") + return entry + + def normalize_po_file(self, po_file: str) -> bool: + """Normalize line endings and validate PO file format.""" + try: + # Read the PO file + with open(po_file, 'r', encoding='utf-8') as f: + content = f.read() + + # Normalize line endings to \n + content = content.replace('\r\n', '\n').replace('\r', '\n') + + # Parse the PO file using polib first to get proper structure + po = polib.pofile(po_file) + + # Create a new PO file with normalized entries + normalized_po = polib.POFile() + normalized_po.metadata = po.metadata.copy() + + for entry in po: + # Create a new entry + new_entry = polib.POEntry( + msgid=entry.msgid.strip(), + msgstr=entry.msgstr.strip(), + msgid_plural=entry.msgid_plural.strip() if entry.msgid_plural else None, + msgstr_plural=entry.msgstr_plural if entry.msgstr_plural else None, + comment=entry.comment, + tcomment=entry.tcomment, + occurrences=entry.occurrences, + flags=entry.flags, + previous_msgid=entry.previous_msgid, + previous_msgid_plural=entry.previous_msgid_plural, + encoding=entry.encoding + ) + normalized_po.append(new_entry) + + # Write back the normalized content + normalized_po.save(po_file) + + # Validate the normalized file + try: + polib.pofile(po_file) + self.logger.info(f"Successfully normalized and validated {po_file}") + return True + except Exception as e: + self.logger.error(f"PO file validation failed: {str(e)}") + return False + + except Exception as e: + self.logger.error(f"Error normalizing PO file: {str(e)}") + return False + + def compile_mo_file(self, po_file: str) -> bool: + """Compile PO file to MO file using msgfmt.""" + try: + # First normalize the PO file + if not self.normalize_po_file(po_file): + self.logger.error(f"Failed to normalize {po_file}") + return False + + # Always use futurecoder.mo as the output filename + mo_file = self.locales_dir / "futurecoder.mo" + + # Use msgfmt to compile the PO file to MO + result = subprocess.run( + ["msgfmt", "-o", str(mo_file), po_file], + capture_output=True, + text=True + ) + + if result.returncode == 0: + self.logger.info(f"Successfully compiled {po_file} to {mo_file}") + return True + else: + self.logger.error(f"Failed to compile {po_file}: {result.stderr}") + return False + + except Exception as e: + self.logger.error(f"Error compiling MO file: {str(e)}") + return False + + def merge_translations(self) -> None: + """Merge translated chunks into a single PO file and compile to MO.""" + self.logger.info(f"Starting merge process for {self.language_code}") + self.logger.info(f"Looking for chunks in: {self.chunks_dir}") + + # Create output directory if it doesn't exist + os.makedirs(os.path.dirname(os.path.abspath(self.output_file)), exist_ok=True) + + # Get all chunk files + chunk_files = sorted(glob.glob(os.path.join(self.chunks_dir, "*.po"))) + if not chunk_files: + self.logger.error(f"No chunk files found in {self.chunks_dir}") + all_files = glob.glob(os.path.join(self.chunks_dir, "*")) + self.logger.info(f"All files found: {all_files}") + raise FileNotFoundError(f"No chunk files found in {self.chunks_dir}") + + self.logger.info(f"Found {len(chunk_files)} chapter-based chunk files to merge") + for f in chunk_files: + self.logger.info(f" - {os.path.basename(f)}") + + # Create new PO file with metadata from english.po + try: + merged_po = polib.POFile() + merged_po.metadata = self.english_po.metadata.copy() + merged_po.metadata['Language'] = self.language_code + + # Get language rules for plural forms + language_rules = Language.get_rules(self.language_code) + merged_po.metadata['Plural-Forms'] = language_rules.plural_forms + except Exception as e: + self.logger.error(f"Failed to read english.po: {str(e)}") + raise + + # Track statistics + total_entries = 0 + translated_entries = 0 + skipped_entries = 0 + code_bits_kept = 0 + program_entries_kept = 0 + prediction_choices_kept = 0 + + # Process each translated chunk + for chunk_file in chunk_files: + self.logger.info(f"Processing translated chunk: {chunk_file}") + try: + chunk_po = polib.pofile(chunk_file) + total_entries += len(chunk_po) + + for entry in chunk_po: + if entry.msgstr and entry.msgstr.strip(): # Only include translated entries + # Find the corresponding English entry + original_entry = self.english_po.find(entry.msgid) + if original_entry: + # For Chinese and Tamil, keep certain entries in English + if self.language_code in ["zh", "ta"] and ( + entry.msgid.startswith("code_bits.") or + entry.msgid.endswith(".program") or + "output_prediction_choices" in entry.msgid + ): + entry.msgstr = original_entry.msgstr # Use English version + if entry.msgid.startswith("code_bits."): + code_bits_kept += 1 + self.logger.info(f"Keeping English code bit: {entry.msgid[:50]}...") + elif entry.msgid.endswith(".program"): + program_entries_kept += 1 + self.logger.info(f"Keeping English program entry: {entry.msgid[:50]}...") + else: + prediction_choices_kept += 1 + self.logger.info(f"Keeping English prediction choice: {entry.msgid[:50]}...") + + # Create a new entry with stripped content + new_entry = polib.POEntry( + msgid=entry.msgid.strip(), + msgstr=entry.msgstr.strip(), + msgid_plural=entry.msgid_plural.strip() if entry.msgid_plural else None, + msgstr_plural=entry.msgstr_plural if entry.msgstr_plural else None, + comment=entry.comment, + tcomment=entry.tcomment, + occurrences=entry.occurrences, + flags=entry.flags, + previous_msgid=entry.previous_msgid, + previous_msgid_plural=entry.previous_msgid_plural, + encoding=entry.encoding + ) + merged_po.append(new_entry) + translated_entries += 1 + else: + self.logger.warning(f"Skipping untranslated entry in {chunk_file}: {entry.msgid[:50]}...") + skipped_entries += 1 + + except Exception as e: + self.logger.error(f"Error processing chunk {chunk_file}: {str(e)}") + raise + + # Save the merged PO file + try: + # Save with proper formatting + merged_po.save(self.output_file) + self.logger.info(f"Successfully merged translations to {self.output_file}") + + # Normalize and compile the merged PO file to MO + if self.compile_mo_file(self.output_file): + self.logger.info(f"Successfully compiled MO file to {self.locales_dir}") + else: + self.logger.error("Failed to compile MO file") + + self.logger.info(f"Statistics:") + self.logger.info(f" Total entries: {total_entries}") + self.logger.info(f" Translated entries: {translated_entries}") + if self.language_code in ["zh", "ta"]: + self.logger.info(f" Code bits kept in English: {code_bits_kept}") + self.logger.info(f" Program entries kept in English: {program_entries_kept}") + self.logger.info(f" Prediction choices kept in English: {prediction_choices_kept}") + self.logger.info(f" Skipped entries: {skipped_entries}") + if total_entries > 0: + self.logger.info(f" Translation coverage: {(translated_entries/total_entries)*100:.1f}%") + except Exception as e: + self.logger.error(f"Failed to save merged file: {str(e)}") + raise + +def validate_language_code(language_code: str) -> bool: + """Validate if the language code is supported""" + try: + Language(language_code) + return True + except ValueError: + return False + +def translate_all_chunks(translator: Translator, base_chunks_dir: str, language_code: str, max_workers: int = 3): + """Translate all chunks from chunks/en/ to chunks/{lang}/, skipping already translated ones.""" + logger = logging.getLogger(__name__) + + en_chunks_dir = os.path.join(base_chunks_dir, "chunks", "en") + target_chunks_dir = os.path.join(base_chunks_dir, "chunks", language_code) + + # Get all English chunk files + en_chunk_files = sorted(glob.glob(os.path.join(en_chunks_dir, "*.po"))) + if not en_chunk_files: + logger.error(f"No English chunk files found in {en_chunks_dir}") + return + + logger.info(f"Found {len(en_chunk_files)} English chunk files in {en_chunks_dir}") + logger.info(f"Will translate to {target_chunks_dir}") + + # Create target directory + os.makedirs(target_chunks_dir, exist_ok=True) + + # Check which chunks need translation + chunks_to_translate = [] + already_translated = [] + + for en_chunk_file in en_chunk_files: + chunk_name = os.path.basename(en_chunk_file) + target_chunk_file = os.path.join(target_chunks_dir, chunk_name) + + if os.path.exists(target_chunk_file) and translator.is_chunk_translated(target_chunk_file): + already_translated.append((en_chunk_file, target_chunk_file)) + else: + chunks_to_translate.append((en_chunk_file, target_chunk_file)) + + logger.info(f"Translation status:") + logger.info(f" - Already translated: {len(already_translated)} chunks") + logger.info(f" - Need translation: {len(chunks_to_translate)} chunks") + + if already_translated: + logger.info("Already translated chunks:") + for en_file, target_file in already_translated: + logger.info(f" ✓ {os.path.basename(target_file)}") + + if not chunks_to_translate: + logger.info("All chunks are already translated! Skipping translation phase.") + return + + logger.info("Chunks to translate:") + for en_file, target_file in chunks_to_translate: + logger.info(f" → {os.path.basename(en_file)} → {os.path.basename(target_file)}") + + # Translate only the chunks that need translation + logger.info(f"Starting translation of {len(chunks_to_translate)} chunks using {max_workers} workers...") + + with ThreadPoolExecutor(max_workers=max_workers) as executor: + # Submit translation tasks + future_to_chunk = { + executor.submit(translator.translate_chunk, en_file, target_file): (en_file, target_file) + for en_file, target_file in chunks_to_translate + } + + completed = 0 + failed = 0 + + # Process completed tasks + for future in as_completed(future_to_chunk): + en_file, target_file = future_to_chunk[future] + try: + result = future.result() + if result is not None: + completed += 1 + logger.info(f"Progress: {completed + failed}/{len(chunks_to_translate)} - Completed: {os.path.basename(target_file)}") + else: + failed += 1 + logger.error(f"Progress: {completed + failed}/{len(chunks_to_translate)} - Failed: {os.path.basename(target_file)}") + except Exception as e: + failed += 1 + logger.error(f"Progress: {completed + failed}/{len(chunks_to_translate)} - Error: {str(e)}") + + logger.info(f"Translation completed: {completed} successful, {failed} failed") + +def main(): + parser = argparse.ArgumentParser(description="Translate Futurecoder PO files") + parser.add_argument("-l", "--language", required=True, help="Language code (e.g., zh, fr, es)") + parser.add_argument("-k", "--api-key", required=True, help="API key for translation service") + parser.add_argument("--base-url", default="https://api.openai.com/v1/chat/completions", + help="Base URL for the translation API") + parser.add_argument("-m", "--model", default="gpt-3.5-turbo", + help="Model to use for translation") + parser.add_argument("-i", "--input", default="./english.po", + help="Input PO file to translate") + parser.add_argument("-o", "--output-dir", default="./", + help="Output directory for translated files") + parser.add_argument("--max-workers", type=int, default=5, + help="Maximum number of parallel workers") + parser.add_argument("--chunk-size", type=int, default=50, + help="Number of entries per chunk") + parser.add_argument("--skip-mo", action="store_true", + help="Skip MO file compilation") + args = parser.parse_args() + + # Configure logging + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(f"translation_{args.language}.log"), + logging.StreamHandler(sys.stdout) + ] + ) + logger = logging.getLogger(__name__) + + try: + # Validate language code + try: + rules = Language.get_rules(args.language) + logger.info(f"Using translation rules for {rules.language_name}") + except ValueError as e: + logger.error(str(e)) + logger.info("Supported languages: " + ", ".join(lang.value.language_name for lang in Language)) + sys.exit(1) + + # Create base chunks directory structure + base_chunks_dir = args.output_dir + en_chunks_dir = os.path.join(base_chunks_dir, "chunks", "en") + target_chunks_dir = os.path.join(base_chunks_dir, "chunks", args.language) + + logger.info(f"Directory structure:") + logger.info(f" - English chunks: {en_chunks_dir}") + logger.info(f" - Target chunks: {target_chunks_dir}") + + # Initialize components + config = TranslationConfig( + api_key=args.api_key, + base_url=args.base_url, + model=args.model + ) + + # Initialize splitter with base output directory + splitter = POFileSplitter( + input_file=args.input, + base_output_dir=base_chunks_dir, + chunk_size=args.chunk_size + ) + translator = Translator(config, args.language) + merger = TranslationMerger( + chunks_dir=target_chunks_dir, + output_file=os.path.join(args.output_dir, f"{args.language}.po"), + language_code=args.language + ) + + # Split the file into English chunks + logger.info(f"Splitting PO file from: {args.input}") + splitter.split_entries() + + # Translate chunks from en/ to {lang}/ + logger.info("Starting translation of chunks...") + translate_all_chunks(translator, base_chunks_dir, args.language, args.max_workers) + + # Merge translations and compile MO file + logger.info(f"Merging translated chunks to: {merger.output_file}") + merger.merge_translations() + + logger.info("Translation process completed successfully!") + logger.info(f"Final output files:") + logger.info(f" - PO file: {os.path.abspath(merger.output_file)}") + logger.info(f" - MO file: {os.path.abspath(merger.locales_dir / 'futurecoder.mo')}") + + except Exception as e: + logger.error(f"Translation process failed: {str(e)}", exc_info=True) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file