internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: hedyorg/hedy

Issue:
Change behavior of save
- Overwrite already saved programs when saving them again, instead of saving them with another name.
- Namespace is the name of the program, not its context (level plus mode (level mode vs adventure x).
- Auto-overwrite without confirmation.
- Remove the "Untitled" from titles and simply have the level + [optional] adventure name.


Discussion:

Resolving patch:
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -737,21 +737,19 @@ def save_program (user):
 
     name = body ['name']
 
-    # If name ends with (N) or (NN), we strip them since it's very likely these addenda were added by our server to avoid overwriting existing programs.
-    name = re.sub (' \(\d+\)$', '', name)
-    # We check if a program with a name `xyz` exists in the database for the username. If it does, we exist whether `xyz (1)` exists, until we find a program `xyz (NN)` that doesn't exist yet.
+    # We check if a program with a name `xyz` exists in the database for the username.
     # It'd be ideal to search by username & program name, but since DynamoDB doesn't allow searching for two indexes at the same time, this would require to create a special index to that effect, which is cumbersome.
     # For now, we bring all existing programs for the user and then search within them for repeated names.
-    existing = db_get_many ('programs', {'username': user ['username']}, True)
-    name_counter = 0
-    for program in existing:
-        if re.match ('^' + re.escape (name) + '( \(\d+\))*', program ['name']):
-            name_counter = name_counter + 1
-    if name_counter:
-        name = name + ' (' + str (name_counter) + ')'
+    programs = db_get_many ('programs', {'username': user ['username']}, True)
+    program = {}
+    overwrite = False
+    for program in programs:
+        if program ['name'] == name:
+            overwrite = True
+            break
 
     stored_program = {
-        'id': uuid.uuid4().hex,
+        'id': program.get ('id') if overwrite else uuid.uuid4().hex,
         'session': session_id(),
         'date': timems (),
         'lang': requested_lang(),
@@ -765,7 +763,10 @@ def save_program (user):
     if 'adventure_name' in body:
         stored_program ['adventure_name'] = body ['adventure_name']
 
-    db_create('programs', stored_program)
+    if overwrite:
+        db_update('programs', stored_program)
+    else:
+        db_create('programs', stored_program)
 
     program_count = 0
     if 'program_count' in user:


Test patch:
Repository: hedyorg/hedy

Issue:
Error message location in the web interface
This is more of a UX problem. 
On my screen, the error message appears below the code box, and thus sometimes not visible if there are to many 'Try this' boxes above. So that I didn't figure something was wrong directly without visual feedback. 
To reproduce, type garbage in lesson2 in english then hit 'run code'


Discussion:
Fixed by https://github.com/Felienne/hedy/commit/16d80f55cd7302c73e80c36cb74010fc846414ee


Resolving patch:
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -472,6 +472,7 @@ def get_similar_code(processed_code, language, level):
                     if distance < shortest_distance:
                         shortest_distance = distance
                         similar_code = lines[0]
+        similar_code = similar_code.replace("    ", "\t")
     except:
         similar_code = None
     return similar_code


Test patch:
Repository: hedyorg/hedy

Issue:
Error message location in the web interface
This is more of a UX problem. 
On my screen, the error message appears below the code box, and thus sometimes not visible if there are to many 'Try this' boxes above. So that I didn't figure something was wrong directly without visual feedback. 
To reproduce, type garbage in lesson2 in english then hit 'run code'


Discussion:
Fixed by https://github.com/Felienne/hedy/commit/16d80f55cd7302c73e80c36cb74010fc846414ee


Resolving patch:
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -263,6 +263,7 @@ def parse():
     # but we'll fall back to browser default if it's missing for whatever
     # reason.
     lang = body.get('lang', requested_lang())
+    session['lang'] = lang
     querylog.log_value(level=level, lang=lang)
 
     response = {}
@@ -399,7 +400,7 @@ def log_feedback():
         'level_usefulness': level_answer,
         'similar_code': similar_code,
         'collapse': collapse,
-        'GFM': True
+        'GFM': True if gfi_support(session['lang']) else False
     })
     debug = True
     if debug:


Test patch:
Repository: hedyorg/hedy

Issue:
Error message location in the web interface
This is more of a UX problem. 
On my screen, the error message appears below the code box, and thus sometimes not visible if there are to many 'Try this' boxes above. So that I didn't figure something was wrong directly without visual feedback. 
To reproduce, type garbage in lesson2 in english then hit 'run code'


Discussion:
Fixed by https://github.com/Felienne/hedy/commit/16d80f55cd7302c73e80c36cb74010fc846414ee


Resolving patch:
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -327,7 +327,8 @@ def parse():
             if gfi_support(lang):
                 response.update(gradual_feedback_model(code, level, gradual_feedback, lang, E, hedy_exception=False))
         if gfi_support(lang):
-            session ['code'] = code
+            session['code'] = code
+            session['hedy_level'] = level
 
     querylog.log_value(server_error=response.get('Error'))
     parse_logger.log ({
@@ -398,6 +399,7 @@ def log_feedback():
         'session': session_id(),
         'date': str(datetime.datetime.now()),
         'is_test': 1 if os.getenv('IS_TEST_ENV') else None,
+        'level': session['hedy_level'],
         'feedback_level': feedback_level,
         'usefulness': general_answer,
         'level_usefulness': level_answer,
@@ -408,6 +410,7 @@ def log_feedback():
     debug = True
     if debug:
         print("TESTING FEEDBACK LOG INFORMATION")
+        print("Current level: " + str(session['hedy_level']))
         print("Model was useful: " + str(general_answer))
         print("Specific level was useful: " + str(level_answer))
         print("Feedback level of fix: " + str(feedback_level))


Test patch:
Repository: hedyorg/hedy

Issue:
Loop variable in level 8 doesn't work in if statements
See code. The if statement should be true when the counter is at 2, but it still goes to the else statement. It does print the right number however
![Counter error](https://user-images.githubusercontent.com/47422769/126526065-d4debeaf-f1ca-4b94-bc48-cfe879e53a34.png)



Discussion:

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -157,8 +157,10 @@ def command(self, args):
         return flatten(args)
 
     def for_loop(self, args):
-        commands = args[1:]
+        # for loop iterator is a var so should be added to the list of vars
+        commands = args
         return flatten(commands)
+
     def while_loop(self, args):
         commands = args[1:]
         return flatten(commands)


Test patch:
Repository: hedyorg/hedy

Issue:
Issue with if/else within repeat block in level 7
In the latest master (https://github.com/Felienne/hedy/commit/175ac3156c0fa3f5fbdf334fe26dea9f35ed6f80), the following program fails at level 7:

```
repeat 5 times
    getallen is 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    getal1 is getallen at random
    getal2 is getallen at random
    goedeantwoord is getal1 * getal2
    print 'Hoeveel is ' getal1 ' keer ' getal2 '?'
    antwoord is ask Vul hier het antwoord in:
    if antwoord is goedeantwoord
        print 'Goedzo'
    else
        print 'Helaas'
```

The error is reported to be in the `else` line, but nothing seems to be wrong there.

If the repeat is taken out and the rest of the program is dedented, it works well. Also, if the else branch of the above program is removed (while keeping the repeat), the program works also well.

This bug was found by Marleen.


Discussion:
Hmmmmm might be something with out latest indent-fix, I will look into this.

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -1230,7 +1230,7 @@ def transpile_inner(input_string, level, sub = 0):
 
     parser = get_parser(level, sub)
 
-    if level >= 8:
+    if level >= 7:
         input_string = preprocess_blocks(input_string)
         # print(input_string)
 


Test patch:
diff --git a/tests/tests_level_07.py b/tests/tests_level_07.py
--- a/tests/tests_level_07.py
+++ b/tests/tests_level_07.py
@@ -252,6 +252,25 @@ def test_issue_297(self):
     self.assertEqual(expected, result)
 
 
+  def test_issue_396(self):
+    code = textwrap.dedent("""\
+    repeat 5 times
+        if antwoord2 is 10
+            print 'Goedzo'
+        else
+            print 'lalala'""")
+
+    result = hedy.transpile(code, 7)
+
+    expected = textwrap.dedent("""\
+    for i in range(int(5)):
+      if str('antwoord2') == str('10'):
+        print('Goedzo')
+      else:
+        print('lalala')""")
+
+    self.assertEqual(expected, result)
+
     
 #programs with issues to see if we catch them properly
 # (so this should fail, for now)
Repository: hedyorg/hedy

Issue:
Mistake in timestamps in Mijn Programma's
![IMG_20210719_111724862](https://user-images.githubusercontent.com/23657961/126136696-cc219124-8387-4d50-9202-8fccc75a8bc4.jpg)

Error: timestamp for previous programs says 3 days ago while it was 1,5 hours ago
Error occurred for user: "Patrik de Panda" and "hoi"



Discussion:
This is not a translating issue, in English is occurs in the same way

Resolving patch:
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -404,15 +404,16 @@ def programs_page (request):
     programs = []
     now = timems ()
     for item in result:
-        measure = texts ['minutes']
-        date = round ((now - item ['date']) / 60000)
-        if date > 90:
+        program_age = now - item ['date']
+        if program_age < 1000 * 60 * 60:
+            measure = texts ['minutes']
+            date = round (program_age / (1000 * 60))
+        elif program_age < 1000 * 60 * 60 * 24:
             measure = texts ['hours']
-            date = round (date / 60)
-        if date > 36:
+            date = round (program_age / (1000 * 60 * 60))
+        else:
             measure = texts ['days']
-
-            date = round (date / 24)
+            date = round (program_age / (1000 * 60 * 60 * 24))
 
         programs.append ({'id': item ['id'], 'code': item ['code'], 'date': texts ['ago-1'] + ' ' + str (date) + ' ' + measure + ' ' + texts ['ago-2'], 'level': item ['level'], 'name': item ['name'], 'adventure_name': item.get ('adventure_name'), 'public': item.get ('public')})
 


Test patch:
Repository: hedyorg/hedy

Issue:
"..." in programs should give a special error
So kids know this is a program to be completed.


PS Note to self: the fact that a lang has an explicit "missing" construct for didactic purposes is quite generalizable to other languages!


Discussion:

Resolving patch:
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -308,6 +308,9 @@ def parse():
             elif E.args[0] == "Unquoted Text":
                 error_template = hedy_errors[E.error_code]
                 response["Error"] = error_template.format(**E.arguments)
+            elif E.args[0] == "Has Blanks":
+                error_template = hedy_errors[E.error_code]
+                response["Error"] = error_template.format(**E.arguments)
             else:
                 error_template = hedy_errors[E.error_code]
                 response["Error"] = error_template.format(**E.arguments)
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -1044,16 +1044,19 @@ def preprocess_blocks(code):
         processed_code.append('end-block')
     return "\n".join(processed_code)
 
+def contains_blanks(code):
+    return (" _ " in code) or (" _\n" in code)
 
-def transpile_inner(input_string, level, sub = 0):
+def transpile_inner(input_string, level, sub=0):
     punctuation_symbols = ['!', '?', '.']
     level = int(level)
-
     parser = get_parser(level, sub)
 
+    if contains_blanks(input_string):
+        raise HedyException('Has Blanks')
+
     if level >= 7:
         input_string = preprocess_blocks(input_string)
-        # print(input_string)
 
     try:
         program_root = parser.parse(input_string+ '\n').children[0]  # getting rid of the root could also be done in the transformer would be nicer


Test patch:
Repository: hedyorg/hedy

Issue:
Turn left/right level 2 not working
In level 2, the "turtle" exercise ("tekenen" in Dutch) you are asked to specify the turn direction. Whether you choose 'left' or 'right' doesn't matter, the direction of the arrow is always downwards. 


Discussion:
Great catch @OnnoEbbens! Will pick this up today.

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -351,11 +351,17 @@ def forward(self,args):
         except:
             parameter = 50
         return f"t.forward({parameter})"""
+
     def turn(self, args):
         if len(args) == 0:
             return "t.right(90)" #no arguments works, and means a right turn
 
-        if args[0] == 'left':
+        argument = args[0]
+        if argument in self.lookup:        #is the argument a variable? if so, use that
+            return f"t.right({argument})"
+        elif argument.isnumeric():         #numbers can also be passed through
+            return f"t.right({argument})"
+        elif argument == 'left':
             return "t.left(90)"
         else:
             return "t.right(90)" #something else also defaults to right turn


Test patch:
diff --git a/tests/tests_level_02.py b/tests/tests_level_02.py
--- a/tests/tests_level_02.py
+++ b/tests/tests_level_02.py
@@ -356,6 +356,52 @@ def test_print_asterisk(self):
     self.assertEqual(expected, result.code)
     self.assertEqual(False, result.has_turtle)
 
+  def test_turn_number(self):
+    code = textwrap.dedent("""\
+    print Turtle race
+    turn 90""")
+
+    result = hedy.transpile(code, self.level)
+
+    expected = textwrap.dedent("""\
+    print('Turtle'+' '+'race')
+    t.right(90)""")
+
+    self.assertEqual(expected, result.code)
+    self.assertEqual(True, result.has_turtle)
+
+  def test_turn_number_var(self):
+    code = textwrap.dedent("""\
+    print Turtle race
+    direction is 70
+    turn direction""")
+
+    result = hedy.transpile(code, self.level)
+
+    expected = textwrap.dedent("""\
+    print('Turtle'+' '+'race')
+    direction = '70'
+    t.right(direction)""")
+
+    self.assertEqual(expected, result.code)
+    self.assertEqual(True, result.has_turtle)
+
+  def test_turn_ask(self):
+    code = textwrap.dedent("""\
+    print Turtle race
+    direction is ask Where to turn?
+    turn direction""")
+
+    result = hedy.transpile(code, self.level)
+
+    expected = textwrap.dedent("""\
+    print('Turtle'+' '+'race')
+    direction = input('Where to turn'+'?')
+    t.right(direction)""")
+
+    self.assertEqual(expected, result.code)
+    self.assertEqual(True, result.has_turtle)
+
   # test for 297 (not easy to fix, not giving prio now)
   # def test_print_space_after_excl(self):
   #   code = "print hello world!koekie!"
Repository: hedyorg/hedy

Issue:
Link to latests shared program is empty
![image](https://user-images.githubusercontent.com/1003685/130991189-31b89162-19a0-4e12-8f31-0862fe7dcef4.png)

(see link at the bottom)


Discussion:

Resolving patch:
diff --git a/website/teacher.py b/website/teacher.py
--- a/website/teacher.py
+++ b/website/teacher.py
@@ -29,7 +29,11 @@ def get_class (user, class_id):
             programs = DATABASE.programs_for_user(student_username)
             highest_level = max(program['level'] for program in programs) if len(programs) else 0
             sorted_public_programs = list(sorted([program for program in programs if program.get ('public')], key=lambda p: p['date']))
-            latest_shared = sorted_public_programs[-1] if sorted_public_programs else None
+            if sorted_public_programs:
+                latest_shared = sorted_public_programs[-1]
+                latest_shared['link'] = os.getenv ('BASE_URL') + f"hedy/{latest_shared['id']}/view"
+            else:
+                latest_shared = None
             students.append ({'username': student_username, 'last_login': utils.mstoisostring (student ['last_login']), 'programs': len (programs), 'highest_level': highest_level, 'latest_shared': latest_shared})
 
         if utils.is_testing_request (request):


Test patch:
Repository: hedyorg/hedy

Issue:
incomplete code shows command in list in error message
f.e. when forgetting argument of `print`

![image](https://user-images.githubusercontent.com/1003685/129219004-c84faef0-5ece-4ec2-8560-4bae09edb4fe.png)



Discussion:

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -273,7 +273,7 @@ def NAME(self, args):
 class IsValid(Filter):
     # all rules are valid except for the "Invalid" production rule
     # this function is used to generate more informative error messages
-    # tree is transformed to a node of [Bool, args, linenumber]
+    # tree is transformed to a node of [Bool, args, command number]
 
     def invalid_space(self, args):
         # return space to indicate that line starts in a space
@@ -1111,7 +1111,7 @@ def transpile_inner(input_string, level, sub=0):
 
     is_complete = IsComplete().transform(program_root)
     if not is_complete[0]:
-        incomplete_command = is_complete[1]
+        incomplete_command = is_complete[1][0]
         line = is_complete[2]
         raise HedyException('Incomplete', incomplete_command=incomplete_command, level=level, line_number=line)
 


Test patch:
diff --git a/tests/tests_level_01.py b/tests/tests_level_01.py
--- a/tests/tests_level_01.py
+++ b/tests/tests_level_01.py
@@ -37,6 +37,13 @@ def test_transpile_incomplete(self):
     with self.assertRaises(Exception) as context:
       result = hedy.transpile("print", self.level)
     self.assertEqual('Incomplete', str(context.exception))
+    self.assertEqual('print', str(context.exception.arguments['incomplete_command']))
+
+  def test_transpile_incomplete_on_line_2(self):
+    with self.assertRaises(Exception) as context:
+      result = hedy.transpile("print lalalala\nprint", self.level)
+    self.assertEqual('Incomplete', str(context.exception))
+    self.assertEqual('print', str(context.exception.arguments['incomplete_command']))
 
   def test_transpile_incomplete_with_multiple_lines(self):
     with self.assertRaises(Exception) as context:
Repository: hedyorg/hedy

Issue:
Issue in new string error message
First string is shown in error message instead of the wrong one:

![image](https://user-images.githubusercontent.com/1003685/133053737-d97d3e06-4448-4a5e-ad6e-2137aca00e29.png)



Discussion:

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -570,7 +570,8 @@ def print(self, args):
             return "print(" + '+'.join(args) + ')'
         else:
             # I would like to raise normally but that is caught by the transformer :(
-            return f"HedyException:{args[0]}"
+            first_unquoted_var = unquoted_args[0]
+            return f"HedyException:{first_unquoted_var}"
             #raise HedyException('Var Undefined', name=args[0])
 
     def print_nq(self, args):


Test patch:
diff --git a/tests/tests_level_03.py b/tests/tests_level_03.py
--- a/tests/tests_level_03.py
+++ b/tests/tests_level_03.py
@@ -289,6 +289,17 @@ def test_transpile_missing_all_quotes(self):
 
       print(f'{self.test_name()} level {level}')
 
+  def test_var_undefined_error_message(self):
+
+    code = textwrap.dedent("""\
+      naam is Hedy
+      print 'ik heet ' name""")
+
+    with self.assertRaises(Exception) as context:
+      result = hedy.transpile(code, self.level)
+
+    self.assertEqual('Var Undefined', context.exception.args[0])
+    self.assertEqual('name', context.exception.arguments['name'])
 
 
   def test_transpile_issue_375(self):
Repository: hedyorg/hedy

Issue:
Invalid Space Exception fails
Invalid Space Exception fails hard: ` print hallo world`

![image](https://user-images.githubusercontent.com/1003685/134167162-9991a7df-1348-4aef-9425-d50c6f7f89d4.png)





Discussion:

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -1266,7 +1266,7 @@ def transpile_inner(input_string, level, sub=0):
             fixed_code = repair(input_string)
             if fixed_code != input_string: #only if we have made a successful fix
                 result = transpile_inner(fixed_code, level, sub)
-            raise HedyException('Invalid Space', level=level, line_number=line, fixed_code = result)
+            raise HedyException('Invalid Space', level=level, line_number=line, fixed_code = result.code)
         elif args == 'print without quotes':
             # grammar rule is ignostic of line number so we can't easily return that here
             raise HedyException('Unquoted Text', level=level)


Test patch:
Repository: hedyorg/hedy

Issue:
i cannot be used in levels with repeat
Before we introduce a loop var that kids can set, we generate code with `i` based on conventions but if a kid used `i` that goes awry:

```
i is 5
repeat 3 times print 'Hedy is leuk!'
print i
```

We should check if i is in the list of vars and then use something else in the repeat/for.
i cannot be used in levels with repeat
Before we introduce a loop var that kids can set, we generate code with `i` based on conventions but if a kid used `i` that goes awry:

```
i is 5
repeat 3 times print 'Hedy is leuk!'
print i
```

We should check if i is in the list of vars and then use something else in the repeat/for.


Discussion:

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -506,6 +506,11 @@ def __init__(self, punctuation_symbols, lookup):
         self.punctuation_symbols = punctuation_symbols
         self.lookup = lookup
 
+    def get_fresh_var(self, name):
+        while name in self.lookup:
+            name = '_' + name
+        return name
+
     def program(self, args):
         return '\n'.join([str(c) for c in args])
     def command(self, args):
@@ -779,9 +784,10 @@ def number(self, args):
         return ''.join(args)
 
     def repeat(self, args):
+        var_name = self.get_fresh_var('i')
         times = process_variable(args[0], self.lookup)
         command = args[1]
-        return f"""for i in range(int({str(times)})):
+        return f"""for {var_name} in range(int({str(times)})):
 {indent(command)}"""
 
 @hedy_transpiler(level=7)


Test patch:
diff --git a/tests/tests_level_06.py b/tests/tests_level_06.py
--- a/tests/tests_level_06.py
+++ b/tests/tests_level_06.py
@@ -203,4 +203,31 @@ def test_repeat_over_9_times(self):
     me wants a cookie!
     me wants a cookie!""")
 
-    self.assertEqual(expected_output, run_code(result))
\ No newline at end of file
+    self.assertEqual(expected_output, run_code(result))
+
+  def test_repeat_with_collision(self):
+      code = textwrap.dedent("""\
+      i is hallo!
+      repeat 5 times print 'me wants a cookie!'
+      print i""")
+
+      result = hedy.transpile(code, self.level)
+
+      expected = textwrap.dedent("""\
+      i = 'hallo!'
+      for _i in range(int('5')):
+        print('me wants a cookie!')
+      print(str(i))""")
+
+      self.assertEqual(expected, result.code)
+      self.assertEqual(False, result.has_turtle)
+
+      expected_output = textwrap.dedent("""\
+      me wants a cookie!
+      me wants a cookie!
+      me wants a cookie!
+      me wants a cookie!
+      me wants a cookie!
+      hallo!""")
+
+      self.assertEqual(expected_output, run_code(result))
\ No newline at end of file