[Commits] typing-turtle branch master updated.

Wade Brainerd wadetb at gmail.com
Thu Nov 20 22:16:34 EST 2008


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "/home/olpc-code/git/activities/typing-turtle".

The branch, master has been updated
       via  35a5d25da9947c4a6e0ef4deeef3a82f5ec92077 (commit)
       via  3613818bd495525defe448789efe73e6438ee775 (commit)
      from  e3e3449a83546a579e9a77b906ee6ffc7015bca0 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

 TODO                               |   62 ++++--
 lessons/en_US/dummy.lesson         |   13 +-
 lessons/en_US/homerow_intro.lesson |   65 +++--
 lessons/en_US/long.lesson          |   15 +-
 typingturtle.py                    |  497 ++++++++++++++++++++----------------
 5 files changed, 366 insertions(+), 286 deletions(-)

- Log -----------------------------------------------------------------
commit 35a5d25da9947c4a6e0ef4deeef3a82f5ec92077
Author: Wade Brainerd <wadetb at gmail.com>
Date:   Fri Nov 21 03:16:26 2008 +0000

    Lesson prerequisites.

diff --git a/TODO b/TODO
index ac4fcc7..e9346ce 100644
--- a/TODO
+++ b/TODO
@@ -1,35 +1,32 @@
 Typing Turtle
 
 First Release
-- Write tLoser, Wes about developing artwork.
-+ Blinking key hilite in 'key' mode.
-+ WPM meter updated in 1sec timer.
+- Write to Loser, Wes about developing artwork.
 - Draw incorrect characters in red.
 - Support backspace, backspace to previous lines.
-+ Scrolling TextView in lesson.
-+ Missing spaces at the end of some lines thanks to dodgy word wrap.
+- Scrolling TextView in lesson.
+- Missing spaces at the end of some lines thanks to dodgy word wrap.
 - Handle ends of line in a sane manner.
 - Implement two step types: key learning and text copying.
 - Split text into lines for long lessons.
 - Try out an insensitive gtk.Entry instead of the gtk.Label.
-+ Write to authors of prior typing activity to inquire about layered artwork.  Need to make a .xo file to demo.
-+ Better flow at the end of a level.  Report the result on the Lesson screen: Need more work, Medal received, etc.
+- Better flow at the end of a level.  Report the result on the Lesson screen: Need more work, Medal received, etc.
+- Ability of lessons to list medals in other lessons as prerequisites.  Disable unavailable lessons.
+- Some sort of lesson sorting criteria.
++ Scroll lessons list to the first non-medaled lesson.
 + Status message on the main screen.  "You unlocked a new lesson!" for example.  Eventually have the turtle 'say' it.
-+ Scroll lessons list to the first non-medaled lesson?
-+ Implement a long text copying lesson and fix bugs in the scrolling and typing.
-+ Some sort of lesson sorting criteria.
-+ Ability of lessons to list medals in other lessons as prerequisites.
+- Implement a long text copying lesson and fix bugs in the scrolling and typing.
 + Graphical WPM and accuracy meters.
++ WPM meter updated in 1sec timer in addition to on keypress.
 - Working medals assignment: "You got a medal!" popup, display next to lesson.
 - Nice looking keyboard.
 - Highlighted keyboard keys when pressed.
 - Support for displaying modifier keys in Keyboard.
 - Change key shown when modified is held.
 - Indicate next key to press on keyboard.
++ Make medal WPM adjustable somehow?  Perhaps a settable Goal WPM?
 + Highlight regions of keyboard, color by finger.
 + Allow lessons to choose between forcing correct keypresses and allowing incorrect ones (with support for Backspace).
-+ History screen: List of lessons completed with statistics.
-+ Progress screen: Line graphs of Accuracy, WPM over time.
 + Artwork and animations.
   + Background picture in main screen.
   + Speed meter picture?
@@ -41,13 +38,45 @@ First Release
 + Sound effects.
   + Welcome to the activity sound.
   + Speed up / slow down sounds when WPM crosses threshold: Slow, Medium, Fast.
-  + Medal award sounds for each medal type: Bronze, Silver, Gold.
-  + Incorrect key pressed sound. 
+  + Medal award sounds for each medal type: None, Bronze, Silver, Gold.  Applause sound.
+  + Incorrect key pressed tick sound. 
 + Develop lessons.
   + Continue to develop lessons for all keys on the keyboard.
-  + Develop 'focus' lessons e.g. fj.
-  + Mark some lessons as "locational" versus "textual" and translate from the English keyboard to native.  Ex: Home row, Left hand, Numbers, etc.
-  + Give each lesson criteria for each medal type based on Accuracy, WPM.
+  - Develop 'focus' lessons e.g. fj.
+  - Mark some lessons as "locational" versus "textual" and translate from the English keyboard to native.  Ex: Home row, Left hand, Numbers, etc.
+  - Give each lesson criteria for each medal type based on Accuracy, WPM.
 
 Future Release
 + Goal support with progress reporting.  WPM, Accuracy, Entire keyboard learned, etc.
++ Keymap showing skill per key.  Star icons on keys that are mastered.
++ History screen: List of lessons completed with statistics.
++ Progress screen: Line graphs of Accuracy, WPM over time.
++ Lesson editor activity or mode.
++ Automatically generate lessons similar to 'home row' based on a list of keys.
+
+Balloon Game
+
++ Create BalloonGameScreen class (use gtk.Layout?).
++ Generate a list of random words, or read from lesson dictionary.
++ Score display.
++ Floating balloons with words on them.  Random velocities, "floaty" look.
++ Balloon letters disappear when typed.
++ Balloon pops when word typed, score increased.
++ Rate of balloons increases over time.
++ Game finished popup, displays score and medal text.
+
+Paper Airplane Game
+
++ Create AirplaneGameScreen class (use gtk.Layout?).
++ Implement bottom-scrolling text display.
++ Airplane toss animation (or maybe takeoff from runway?).  Starts with first letter.
++ Floating airplane momentum based on words per minute.
++ Scrolling background graphics.
++ Finish line graphics.
++ Game finished popup, displays score and medal text.
+
+Race Game
+
++ Create RaceGameScreen class (use gtk.Layout?).
++ Copy whatever Prakhar's got, finish it up.
++ Game finished popup, displays score and medal text.
diff --git a/lessons/en_US/dummy.lesson b/lessons/en_US/dummy.lesson
index f3049ea..ec780ef 100644
--- a/lessons/en_US/dummy.lesson
+++ b/lessons/en_US/dummy.lesson
@@ -1,16 +1,13 @@
   {
-    "name": "Dummy",
-    "description": "Empty lesson for testing.",
-    "medals": {
-      "bronze": { "wpm": 0, "accuracy": 60 },
-      "silver": { "wpm": 0, "accuracy": 60 },
-      "gold":   { "wpm": 0, "accuracy": 90 }
-    },
+    "name": "Cheat Lesson",
+    "description": "Empty lesson for testing, automatically gives level 100.",
+    "level": 100,
+    "requiredlevel": 0,
     "steps": [
       {
         "type": "key",
         "instructions": "Type an x.",
-        "text": "x"
+        "text": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
       }
     ]
   }
diff --git a/lessons/en_US/homerow_intro.lesson b/lessons/en_US/homerow_intro.lesson
index 2320a46..8f674d8 100644
--- a/lessons/en_US/homerow_intro.lesson
+++ b/lessons/en_US/homerow_intro.lesson
@@ -1,6 +1,8 @@
 {
   "name": "Home Row",
   "description": "Teaches the middle row of keys on the keyboard.",
+  "level": 1,
+  "requiredlevel": 0,
   "steps": [
     {
       "instructions": "Welcome to the first Typing Turtle lesson!  Place your fingers on the keyboard as shown below.\n\nWhen you are ready to begin, press the f key with your left index finger.",
diff --git a/lessons/en_US/long.lesson b/lessons/en_US/long.lesson
index c4379f1..d05610e 100644
--- a/lessons/en_US/long.lesson
+++ b/lessons/en_US/long.lesson
@@ -1,6 +1,8 @@
 {
     "name": "Punctuation Practice!",
     "description": "Practice punctuation with a section of Pride and Prejudice.",
+    "level": 2,
+    "requiredlevel": 1,
     "steps": [
         {
           "type": "text",
diff --git a/typingturtle.py b/typingturtle.py
index 3459e7a..b24e298 100755
--- a/typingturtle.py
+++ b/typingturtle.py
@@ -17,7 +17,7 @@
 """Typing Turtle - Interactive typing tutor for the OLPC XO."""
 
 # Import standard Python modules.
-import logging, os, math, time, copy, json, locale, datetime, random
+import logging, os, math, time, copy, json, locale, datetime, random, re
 from gettext import gettext as _
 
 # Set up localization.
@@ -44,7 +44,7 @@ import keyboard
 PARAGRAPH_CODE = u'\xb6'
 
 # Maximium width of a text line in text lesson mode.
-LINE_LENGTH = 80
+LINE_WIDTH = 80
 
 # Requirements for earning medals.
 # Words per minute goals came from http://en.wikipedia.org/wiki/Words_per_minute.
@@ -214,27 +214,19 @@ class LessonScreen(gtk.VBox):
         self.advance_step()
 
     def wrap_line(self, line):
-        words = line.split(' ')
+        words = re.split('(\W+)', line)
+        
         new_lines = []
         cur_line = ''
         for w in words:
             # TODO: Handle single word longer than a line.
-            #if len(w) > LINE_LENGTH:
-            #    new_lines.append(cur_line)
-            #    while len(w):
-            #        new_lines.append(w[:LINE_LENGTH])
-            #        w = w[LINE_LENGTH:]
-            #    cur_line = ''
-            if len(cur_line) + len(w) + 1 > LINE_LENGTH:
+            if not w.isspace() and len(cur_line) + len(w) > LINE_WIDTH:
                 if len(cur_line):
                     new_lines.append(cur_line)
-                cur_line = ''
-            cur_line += w + ' '
+                    cur_line = ''
+            cur_line += w
         
         if len(cur_line):
-            # Remove trailing spaces from last line before adding.
-            while cur_line[-1] == ' ':
-                cur_line = cur_line[:-1]
             new_lines.append(cur_line)
         
         return new_lines
@@ -242,14 +234,11 @@ class LessonScreen(gtk.VBox):
     def advance_step(self):
         # Clear step related variables.
         self.step = None
-        self.step_type = None
         
         self.text = None
         self.line = None
         self.line_marks = None
         
-        self.key_expected = None
-        
         # End lesson if this is the last step.
         if self.next_step_idx >= len(self.lesson['steps']):
             self.lesson_finished = True
@@ -261,61 +250,46 @@ class LessonScreen(gtk.VBox):
         self.step = self.lesson['steps'][self.next_step_idx]
         self.next_step_idx = self.next_step_idx + 1
         
-        if len(self.step['text']) == 1:
-            self.step_type = 'key'
-        else:
-            self.step_type = 'text'
+        # Clear the text buffer and output the instructions.
+        self.lessonbuffer.insert_with_tags_by_name(
+            self.lessonbuffer.get_end_iter(), '\n\n' + self.step['instructions'] + '\n', 'instructions')
         
-        if self.step_type == 'text':
-            # Clear the text buffer and output the instructions.
-            self.lessonbuffer.set_text('')
-            self.lessonbuffer.insert_with_tags_by_name(
-                self.lessonbuffer.get_end_iter(), '\n' + self.step['instructions'] + '\n\n', 'instructions')
-            
-            self.text = unicode(self.step['text'])
-            
-            # Split text into lines.
-            self.lines = self.text.splitlines(True)
-            
-            # Substitute paragraph codes.
-            self.lines = [l.replace('\n', PARAGRAPH_CODE) for l in self.lines]
-            
-            # Split by line length in addition to by paragraphs.
-            for i in range(0, len(self.lines)):
-                line = self.lines[i]
-                if len(line) > 30:
-                    self.lines[i:i+1] = self.wrap_line(line)
+        self.text = unicode(self.step['text'])
+        
+        # Split text into lines.
+        self.lines = self.text.splitlines(True)
+        
+        # Substitute paragraph codes.
+        self.lines = [l.replace('\n', PARAGRAPH_CODE) for l in self.lines]
+        
+        # Split by line length in addition to by paragraphs.
+        for i in range(0, len(self.lines)):
+            line = self.lines[i]
+            if len(line) > LINE_WIDTH:
+                self.lines[i:i+1] = self.wrap_line(line)
+        
+        # Center single line steps.
+        indent = ''
+        #if len(self.lines) == 1:
+        #    indent = ' ' * ((LINE_LENGTH - len(self.lines[0]))/2)
             
-            # Fill text buffer with text lines, each followed by room for the user to type.
-            self.line_marks = {} 
-            line_idx = 0
-            for l in self.lines:
-                self.lessonbuffer.insert_with_tags_by_name(
-                    self.lessonbuffer.get_end_iter(), l.encode('utf-8') + '\n', 'text')
-                
-                self.line_marks[line_idx] = self.lessonbuffer.create_mark(None, self.lessonbuffer.get_end_iter(), True)
-                self.lessonbuffer.insert_at_cursor('\n')
+        # Fill text buffer with text lines, each followed by room for the user to type.
+        self.line_marks = {} 
+        line_idx = 0
+        for l in self.lines:
                 
-                line_idx += 1
-            
-            self.line_idx = 0
-            self.begin_line()
-            
-        elif self.step_type == 'key':
-            # Clear the text buffer and output the instructions.
-            self.lessonbuffer.set_text('')
+            # Add the text to copy.
             self.lessonbuffer.insert_with_tags_by_name(
-                self.lessonbuffer.get_end_iter(), '\n' + self.step['instructions'] + '\n\n', 'instructions')
+                self.lessonbuffer.get_end_iter(), '\n' + indent + l.encode('utf-8') + '\n' + indent, 'text')
             
-            # Prepare the key.            
-            self.key_expected = unicode(self.step['text'][0]).replace('\n', PARAGRAPH_CODE)
+            # Leave a marker where we will later insert text.
+            self.line_marks[line_idx] = self.lessonbuffer.create_mark(None, self.lessonbuffer.get_end_iter(), True)
+            
+            line_idx += 1
+        
+        self.line_idx = 0
+        self.begin_line()
             
-            # Hilite the key on the virtual keyboard.
-            self.keyboard.clear_hilite()
-            key = self.keyboard.find_key_by_letter(self.key_expected)
-            if key:
-                key.set_hilite(True)
-
     def begin_line(self):
         self.line = self.lines[self.line_idx]
         self.line_mark = self.line_marks[self.line_idx]
@@ -336,6 +310,7 @@ class LessonScreen(gtk.VBox):
         
         # Simply wait for a return keypress on the lesson finished screen.
         if self.lesson_finished:
+            # TODO: Wait a second first.
             if key_name == 'Return':
                 self.end_lesson()
             return
@@ -351,81 +326,64 @@ class LessonScreen(gtk.VBox):
             self.start_time = time.time()
         
         # Handle backspace by deleting text and optionally moving up lines.
-        if self.step_type == 'text':
-            if key_name == 'BackSpace':
-                # Move to previous line if at the end of the current one.
-                if self.char_idx == 0 and self.line_idx > 0:
-                    self.line_idx -= 1 
-                    self.begin_line()
-                    
-                    self.char_idx = len(self.line)
-                
-                # Then delete the current character.
-                if self.char_idx > 0:
-                    self.char_idx -= 1
-                    
-                    iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
-                    iter.forward_chars(self.char_idx)
-                    
-                    iter_end = iter.copy()
-                    iter_end.forward_char()
-                    
-                    self.lessonbuffer.delete(iter, iter_end)
-    
-            # Process normal key presses.
-            elif key != 0:
+        if key_name == 'BackSpace':
+            # Move to previous line if at the end of the current one.
+            if self.char_idx == 0 and self.line_idx > 0:
+                self.line_idx -= 1 
+                self.begin_line()
                 
-                # Check to see if they pressed the correct key.
-                if key == self.line[self.char_idx]:
-                    tag_name = 'correct-copy'
-                    self.correct_keys += 1
-                    self.total_keys += 1
-                    
-                else:
-                    # TODO - Play 'incorrect key' sound here.
-                    
-                    tag_name = 'incorrect-copy'
-                    self.incorrect_keys += 1
-                    self.total_keys += 1
+                self.char_idx = len(self.line)
+            
+            # Then delete the current character.
+            if self.char_idx > 0:
+                self.char_idx -= 1
                 
-                # Insert the key into the bufffer.
                 iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
                 iter.forward_chars(self.char_idx)
                 
-                self.lessonbuffer.insert_with_tags_by_name(iter, key, tag_name)
-                
-                # Advance to the next character (or else).
-                self.char_idx += 1
-                if self.char_idx >= len(self.line):
-                    self.line_idx += 1
-                    if self.line_idx >= len(self.lines):
-                        self.advance_step()
-                    else:
-                        self.begin_line()
+                iter_end = iter.copy()
+                iter_end.forward_char()
                 
-                self.update_stats()
-            
+                self.lessonbuffer.delete(iter, iter_end)
+
             self.hilite_next_key()
-        
-        elif self.step_type == 'key':
+
+        # Process normal key presses.
+        elif key != 0:
             
             # Check to see if they pressed the correct key.
-            if key == self.key_expected:
+            if key == self.line[self.char_idx]:
                 tag_name = 'correct-copy'
                 self.correct_keys += 1
                 self.total_keys += 1
                 
-                self.advance_step()
-
             else:
                 # TODO - Play 'incorrect key' sound here.
-
+                
                 tag_name = 'incorrect-copy'
                 self.incorrect_keys += 1
                 self.total_keys += 1
             
-            self.update_stats()
+            # Insert the key into the bufffer.
+            iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
+            iter.forward_chars(self.char_idx)
+            
+            self.lessonbuffer.insert_with_tags_by_name(iter, key, tag_name)
+            
+            # Advance to the next character (or else).
+            self.char_idx += 1
+            if self.char_idx >= len(self.line):
+                self.line_idx += 1
+                if self.line_idx >= len(self.lines):
+                    self.advance_step()
+                else:
+                    self.begin_line()
+                return
             
+            self.update_stats()
+        
+            self.hilite_next_key()
+        
         return False
 
     def hilite_next_key(self):
@@ -445,7 +403,7 @@ class LessonScreen(gtk.VBox):
         self.lessontext.grab_focus()
 
         # Scroll the TextView so the cursor is on screen.
-        self.lessontext.scroll_to_mark(self.lessonbuffer.get_insert(), 0.1)
+        self.lessontext.scroll_to_mark(self.lessonbuffer.get_insert(), 0)
 
     def show_lesson_report(self):
         self.update_stats()
@@ -499,7 +457,12 @@ class LessonScreen(gtk.VBox):
                         if medal['wpm'] < old_medal['wpm']:
                             add_medal = False
             
-            if add_medal:
+            if add_medal:       
+                # Upgrade the player's level if needed.
+                if self.lesson['level'] > self.activity.data['level']:
+                    self.activity.data['level'] = self.lesson['level']
+                    self.activity.data['motd'] = _('You unlocked a new lesson!')
+                
                 self.activity.data['medals'][lesson_name] = medal
                 self.activity.mainscreen.update_medals()
         
@@ -527,8 +490,8 @@ class LessonScreen(gtk.VBox):
             
         else:
             # Comment on what the user needs to do better.
-            need_wpm = report['wpm'] < self.lesson['medals']['bronze']['wpm']
-            need_accuracy = report['accuracy'] < self.lesson['medals']['bronze']['accuracy']
+            need_wpm = report['wpm'] < MEDALS[0]['wpm']
+            need_accuracy = report['accuracy'] < MEDALS[0]['accuracy']
             
             if need_accuracy and need_wpm:
                 text += _('You need to practice this lesson more before moving on.  If you are having a hard time, '
@@ -562,18 +525,18 @@ class LessonScreen(gtk.VBox):
 class MainScreen(gtk.VBox):
     def __init__(self, activity):
         gtk.VBox.__init__(self)
-
+        
         self.activity = activity
-
+        
         # Build background.
         title = gtk.Label()
         title.set_markup("<span size='40000'><b>" + _('Typing Turtle') + "</b></span>")
-
+        
         subtitle = gtk.Label()
         subtitle.set_markup(_('Welcome to Typing Turtle! To begin, select a lesson from the list below.'))
-
+        
         spacer = gtk.HBox()
-
+        
         # Lessons header.
         headerbox = gtk.VBox()
         label = gtk.Label()
@@ -581,15 +544,15 @@ class MainScreen(gtk.VBox):
         label.set_markup("<span size='large'><b>"+_('Available Lessons')+"</b></span>")
         headerbox.pack_start(label, False)
         headerbox.pack_start(gtk.HSeparator(), False)
-
+        
         # Build lessons list.
         self.lessonbox = gtk.VBox()
         self.lessonbox.set_spacing(10)
-
+        
         bundle_path = sugar.activity.activity.get_bundle_path() 
         code = locale.getlocale(locale.LC_ALL)[0]
         path = bundle_path + '/lessons/' + code + '/'
- 
+        
         # Find all .lesson files in ./lessons/en_US/ for example.
         lessons = []
         for f in os.listdir(path):
@@ -599,47 +562,57 @@ class MainScreen(gtk.VBox):
                 lessons.append(lesson)
             finally:
                 fd.close()
-
+        
+        lessons.sort(lambda x, y: x['level'] - y['level'])
+        
         for l in lessons:
             label = gtk.Label()
             label.set_alignment(0.0, 0.5)
             label.set_markup("<span size='large'>" + l['name'] + "</span>\n" + l['description'])
-
+            
             btn = gtk.Button()
             btn.lesson = l
             btn.add(label)
             btn.connect('clicked', self.lesson_clicked_cb)
-
+            
             medalimage = gtk.Image()
-
+            
             medalbtn = gtk.Button()
             medalbtn.lesson = l
             medalbtn.add(medalimage)
             medalbtn.connect('clicked', self.medal_clicked_cb)
-
+            
             hbox = gtk.HBox()
             hbox.pack_start(btn, True, True, 10)
             hbox.pack_end(medalbtn, False, False)            
-
+            
+            hbox.button = btn
+            hbox.medalbutton = medalbtn
             hbox.lesson = l
             hbox.medalimage = medalimage
-      
+            
             self.lessonbox.pack_start(hbox, False)
-
+        
         self.lessonscroll = gtk.ScrolledWindow()
         self.lessonscroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
         self.lessonscroll.add_with_viewport(self.lessonbox)
-
+        
         self.pack_start(title, False, True, 10)
         self.pack_start(subtitle, False)
         self.pack_start(spacer, False, False, 50)
         self.pack_start(headerbox, False)
         self.pack_start(self.lessonscroll, True)
-
+        
         self.update_medals()
 
     def update_medals(self):
         for l in self.lessonbox:
+            # Disable the lesson button unless available.
+            lesson_available = self.activity.data['level'] >= l.lesson['requiredlevel']
+            l.button.set_sensitive(lesson_available)
+            l.medalbutton.set_sensitive(lesson_available)
+            
+            # Update the medal image.
             medal_type = 'none'
             if self.activity.data['medals'].has_key(l.lesson['name']):
                 medal_type = self.activity.data['medals'][l.lesson['name']]['type']
@@ -652,10 +625,10 @@ class MainScreen(gtk.VBox):
                 'gold':   bundle+'/images/gold-medal.jpg'
             }
             l.medalimage.set_from_file(images[medal_type])
-
+    
     def lesson_clicked_cb(self, widget):
         self.activity.push_screen(LessonScreen(widget.lesson, self.activity))
-
+    
     def medal_clicked_cb(self, widget):
         if self.activity.data['medals'].has_key(widget.lesson['name']):
             medal = self.activity.data['medals'][widget.lesson['name']]
@@ -669,29 +642,31 @@ class TypingTurtle(sugar.activity.activity.Activity):
     def __init__ (self, handle):
         sugar.activity.activity.Activity.__init__(self, handle)
         self.set_title(_("Typing Turtle"))
-
+        
         self.build_toolbox()
-  
+        
         self.screens = []
         self.screenbox = gtk.VBox()
-
+        
         self.owner = presenceservice.get_instance().get_owner()
-
+        
         # All data which is saved in the Journal entry is placed in this dictionary.
         self.data = {
+            'motd': _('Welcome to Typing Turtle!'),
+            'level': 0,
             'history': [],
             'medals': {}
         }
-
+        
         # This has to happen last, because it calls the read_file method when restoring from the Journal.
         self.set_canvas(self.screenbox)
-
+        
         # Start with the main screen.
         self.mainscreen = MainScreen(self)
         self.push_screen(self.mainscreen)
-  
+        
         self.show_all()
-
+        
         # Hide the sharing button from the activity toolbar since we don't support sharing.
         activity_toolbar = self.tbox.get_activity_toolbar()
         activity_toolbar.share.props.visible = False
@@ -699,13 +674,13 @@ class TypingTurtle(sugar.activity.activity.Activity):
     def build_toolbox(self):
         self.tbox = sugar.activity.activity.ActivityToolbox(self)
         self.tbox.show_all()
-
+        
         self.set_toolbox(self.tbox)
 
     def push_screen(self, screen):
         if len(self.screens):
             self.screenbox.remove(self.screens[-1])
- 
+        
         self.screenbox.pack_start(screen, True, True)
         self.screens.append(screen)
 
@@ -721,7 +696,7 @@ class TypingTurtle(sugar.activity.activity.Activity):
     def read_file(self, file_path):
         if self.metadata['mime_type'] != 'text/plain':
             return
-
+        
         fd = open(file_path, 'r')
         try:
             text = fd.read()
@@ -733,7 +708,7 @@ class TypingTurtle(sugar.activity.activity.Activity):
     def write_file(self, file_path):
         if not self.metadata['mime_type']:
             self.metadata['mime_type'] = 'text/plain'
-
+        
         fd = open(file_path, 'w')
         try:
             text = json.write(self.data)

commit 3613818bd495525defe448789efe73e6438ee775
Author: Wade Brainerd <wadetb at gmail.com>
Date:   Thu Nov 20 23:16:10 2008 +0000

    Home row lesson started - it's a lot of work!  Some other small bug fixes, thinking about code reorganization & layout issues.

diff --git a/TODO b/TODO
index bc5d087..ac4fcc7 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,8 @@
 Typing Turtle
 
 First Release
-- Write to Loser, Wes about developing artwork.
+- Write tLoser, Wes about developing artwork.
++ Blinking key hilite in 'key' mode.
 + WPM meter updated in 1sec timer.
 - Draw incorrect characters in red.
 - Support backspace, backspace to previous lines.
diff --git a/lessons/en_US/homerow_intro.lesson b/lessons/en_US/homerow_intro.lesson
index 774a148..2320a46 100644
--- a/lessons/en_US/homerow_intro.lesson
+++ b/lessons/en_US/homerow_intro.lesson
@@ -1,81 +1,90 @@
 {
   "name": "Home Row",
   "description": "Teaches the middle row of keys on the keyboard.",
-  "medals": {
-    "bronze": { "wpm": 0, "accuracy": 60 },
-    "silver": { "wpm": 0, "accuracy": 60 },
-    "gold":   { "wpm": 0, "accuracy": 90 }
-  },
   "steps": [
     {
-      "type": "key",
-      "instructions": "Welcome to the first Typing Turtle lesson!  Please place your fingers on the keyboard.\n\nWhen you are ready to begin, press the f key with your left index finger.",
+      "instructions": "Welcome to the first Typing Turtle lesson!  Place your fingers on the keyboard as shown below.\n\nWhen you are ready to begin, press the f key with your left index finger.",
       "text": "f"
     },
     {
-      "type": "key",
       "instructions": "Good job!  Now press the d key with your left middle finger.",
       "text": "d"
     },
     {
-      "type": "key",
       "instructions": "Now the s key with your left ring finger.",
       "text": "s"
     },
     {
-      "type": "key",
       "instructions": "And the a key with your left pinky finger.",
       "text": "a"
     },
     {
-      "type": "key",
       "instructions": "Great work!  We will now move on to the right hand.  As shown in the picture below, press the j key with your right index finger.",
       "text": "j"
     },
     {
-      "type": "key",
       "instructions": "The k key with your right middle finger.",
       "text": "k"
     },
     {
-      "type": "key",
       "instructions": "The l key with your right ring finger.",
       "text": "l"
     },
     {
-      "type": "key",
       "instructions": "The ; (semicolon) key with your right pinky finger.",
       "text": ";"
     },
     {
-      "type": "key",
-      "instructions": "Excellent!  These keys are known as the home row.  Your fingers should always return to them after typing each letter.\n\nType all of the letters below.  Press down with your fingertips, but do not move your hands.",
+      "instructions": "Excellent!  These keys are known as the home row.  Your fingers should always return to rest lightly on them.\n\nType all of the letters below.  Press down quickly and lightly with your fingertips, but do not move your hands.",
       "text": "fdsajkl;"
     },
     {
-      "type": "key",
       "instructions": "Again, from left to right!",
       "text": "asdfjkl;"
     },
     {
-      "type": "key",
       "instructions": "Keep it up, you are doing great!  Now, from right to left!",
       "text": ";lkjfdsa"
     },
     {
-      "type": "key",
-      "instructions": "Okay, you are done with that.  Now to begin moving your fingers.  Without moving any other fingers, move your left index finger over and press the g key.",
-      "text": "g"
+      "instructions": "Okay, now to learn the Enter reach.  Without moving your hands, move your right pinky over and press the Enter key.",
+      "text": "\n"
     },
     {
-      "type": "key",
-      "instructions": "Now do the same with your right hand.  Move your right index finger only and press the h key.",
-      "text": "h"
+      "instructions": "Good.  Now type the next two lines, using the Enter key to move from one line to the next.",
+      "text": "asdf\njkl;"
     },
     {
-      "type": "key",
-      "instructions": "Now for the final two keys of the home row.  Move your right pinky finger over and press the ' key.",
-      "text": "'"
+      "instructions": "Time to practice Enter and ;!",
+      "text": ";\n;\n;\n"
+    },
+    {
+      "instructions": "Time to practice letters.  Now type in the following lines.",
+      "text": "asdf\njkl;\nasdfjkl;\njkl;asdf"
+    },
+    {
+      "instructions": "Now we will learn the Space bar.  The space bar is used to leave a space between words.  Use your right thumb to press it.",
+      "text": " "
+    },
+    {
+      "instructions": "Press it again!",
+      "text": " "
+    },
+    {
+      "instructions": "One more time!",
+      "text": " "
+    },
+    {
+      "instructions": "Good job.  Let's put it all together now with some practice lines.",
+      "text": "ff f jj j dd d kk k ss s ll l aa a ;; ;\nfdsa jkl;\nff f jj j dd d kk k ss s ll l aa a ;; ;\nfdsa jkl;\n; ;; a aa l ll s ss k kk d dd j jj f ff\nasdf jkl;\n; ;; a aa l ll s ss k kk d dd j jj f ff\nasdf jkl;\n"
+    },
+    {
+      "instructions": "It's very important to master the home row before learning to reach for the other keys.  Stick with it and you will be touch typing in no time at all!",
+      "text": "a; a; sl sl dk dk fj fj jf jf kd kd ls ls ;a ;a\na; a; sl sl dk dk fj fj jf jf kd kd ls ls ;a ;a\n;ajf ;ajf ;alskdjf ;alskdjf ;alskdjf ;alskdjf ;alskdjf\n\n;ajf ;ajf ;alskdjf ;alskdjf ;alskdjf ;alskdjf ;alskdjf\nfja; fjsd j;kl lk;j sldk kdls ajf;dkls ajf;dkls jdfksl;a\n"
+    },
+    {
+      "instructions": "Try and speed up your typing a little.\n\nWhen your fingers are on the home row, they should be curved so that your thumbs rest on the space bar.",
+      "text": "a;sldkfj a;sldkfj a;sldkfj a;sldkfj\njj ff kk dd ll ss ;; aa\na; sl dk fj a; sl dk fj asdf ;lkj\njf kd ls ;a fj dk sl a; fdsa jkl; jfkdls;a\n;; ll kk kj lk ;l aa ss dd df sd as jd fl jd fl jj dd ff ll\na a as as sad sad lad lad fad fad ask ask\nadd add ask ask lass lass as as falls falls jaff\nask lass fall as daff lass ads ask add ask\na lass; all lads; a fall; fall a lad; a dall a lad; fall dad fad; a jad falls a fad;\nsalad lad; lass sad; a fad a dall; salad dad; salad fad;"
     }
   ]
 }
diff --git a/lessons/en_US/long.lesson b/lessons/en_US/long.lesson
index 512b3c0..c4379f1 100644
--- a/lessons/en_US/long.lesson
+++ b/lessons/en_US/long.lesson
@@ -1,16 +1,11 @@
 {
-    "name": "Long Lesson",
-    "description": "Testing for lessons involving multiple pages of text.",
-    "medals": {
-        "bronze": { "wpm": 0, "accuracy": 60 },
-        "silver": { "wpm": 0, "accuracy": 60 },
-        "gold":   { "wpm": 0, "accuracy": 90 }
-    },
+    "name": "Punctuation Practice!",
+    "description": "Practice punctuation with a section of Pride and Prejudice.",
     "steps": [
         {
           "type": "text",
-          "instructions": "Please copy out the following text.",
-          "text": "It is a truth universally acknowledged, that a single man in posession of a good fortune, must be in want of a wife.\n\nHowever little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.\n\n\"My dear Mr. Bennet,\" said his lady to him one day, \"have you heard that\nNetherfield Park is let at last?\"\n\nMr. Bennet replied that he had not.\n\n\"But it is,\" returned she; \"for Mrs. Long has just been here, and she told me all about it.\"\n\nMr. Bennet made no answer.\n\n\"Do you not want to know who has taken it?\" cried his wife impatiently.\n\n\"You want to tell me, and I have no objection to hearing it.\"\n\nThis was invitation enough."
+          "instructions": "Pride and Prejudice\nby Jane Austen",
+          "text": "It is a truth universally acknowledged, that a single man in posession of a good fortune, must be in want of a wife.\n\nHowever little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.\n\n\"My dear Mr. Bennet,\" said his lady to him one day, \"have you heard that Netherfield Park is let at last?\"\n\nMr. Bennet replied that he had not.\n\n\"But it is,\" returned she; \"for Mrs. Long has just been here, and she told me all about it.\"\n\nMr. Bennet made no answer.\n\n\"Do you not want to know who has taken it?\" cried his wife impatiently.\n\n\"You want to tell me, and I have no objection to hearing it.\"\n\nThis was invitation enough."
       }
     ]
   }
diff --git a/typingturtle.py b/typingturtle.py
index 10c88f2..3459e7a 100755
--- a/typingturtle.py
+++ b/typingturtle.py
@@ -17,7 +17,7 @@
 """Typing Turtle - Interactive typing tutor for the OLPC XO."""
 
 # Import standard Python modules.
-import logging, os, math, time, copy, json, locale, datetime
+import logging, os, math, time, copy, json, locale, datetime, random
 from gettext import gettext as _
 
 # Set up localization.
@@ -43,53 +43,64 @@ import keyboard
 # Paragraph symbol unicode character.
 PARAGRAPH_CODE = u'\xb6'
 
+# Maximium width of a text line in text lesson mode.
+LINE_LENGTH = 80
+
+# Requirements for earning medals.
+# Words per minute goals came from http://en.wikipedia.org/wiki/Words_per_minute.
+MEDALS = [
+    { 'name': 'bronze', 'wpm': 25, 'accuracy': 75 },
+    { 'name': 'silver', 'wpm': 35, 'accuracy': 85 },
+    { 'name': 'gold',   'wpm': 45, 'accuracy': 95 }
+]
+
 class MedalScreen(gtk.EventBox):
     def __init__(self, medal, activity):
         gtk.EventBox.__init__(self)
-
+        
         self.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffffff'))
-
+        
         self.medal = medal
         self.activity = activity
-
+        
         cert0 = gtk.Label()
         cert0.set_markup("<span size='35000'><b><i>" + _('Certificate of\nAchievement') + "</i></b></span>")
-
+        
         cert1 = gtk.Label()
         cert1.set_markup("<span size='18000'>" + 
             (_('This certifies that on <i><b><u>%(date)s</u></b></i>,\n<i><b><u>%(nick)s</u></b></i> earned a %(type)s medal\nin Typing Turtle lesson <i><b><u>%(lesson)s</u></b></i>.') % medal) +
             "</span>")
-
+        
         wpmlabel = gtk.Label()
         wpmlabel.set_markup("<span size='18000'>" + (_('<b>Words Per Minute:</b> %(wpm)d') % medal) + "</span>" )
-
+        
         accuracylabel = gtk.Label()
         accuracylabel.set_markup("<span size='15000'>" + (_('<b>Accuracy:</b> %(accuracy)d%%') % medal) + "</span>" )
-
+        
         statbox = gtk.HBox()
         statbox.pack_start(wpmlabel, True)
         statbox.pack_start(accuracylabel, True)
-
+        
         oklabel = gtk.Label()
         oklabel.set_markup("<span size='10000'>" + _('Go Back') + '</span>')
         okbtn =  gtk.Button()
         okbtn.add(oklabel)
         okbtn.connect('clicked', self.ok_cb)
-
+        
         btnbox = gtk.HBox()
         btnbox.pack_start(okbtn, True, False)
-
+        
         vbox = gtk.VBox()
-
+        
         vbox.pack_start(cert0, True, False, 0)
         vbox.pack_start(cert1, False, False, 0)
         vbox.pack_start(gtk.HSeparator(), False, False, 20)
         vbox.pack_start(statbox, False, False, 0)
         vbox.pack_start(gtk.HSeparator(), False, False, 20)
         vbox.pack_start(btnbox, False, False, 40)
-
+        
         self.add(vbox)
-
+        
         self.show_all()
 
     def ok_cb(self, widget):
@@ -98,55 +109,56 @@ class MedalScreen(gtk.EventBox):
 class LessonScreen(gtk.VBox):
     def __init__(self, lesson, activity):
         gtk.VBox.__init__(self)
-
+        
         self.lesson = lesson
         self.activity = activity
-
+        
         # Build the user interface.
         title = gtk.Label()
         title.set_markup("<span size='20000'><b>" + lesson['name'] + "</b></span>")
         title.set_alignment(1.0, 0.0)
-
+        
         stoplabel = gtk.Label(_('Go Back'))
         stopbtn =  gtk.Button()
         stopbtn.add(stoplabel)
         stopbtn.connect('clicked', self.stop_cb)
-
+        
         # TODO- These will be replaced by graphical displays using gtk.DrawingArea.
         self.wpmlabel = gtk.Label()
         self.accuracylabel = gtk.Label()
-
+        
         #self.wpmarea = gtk.DrawingArea()
         #self.wpmarea.connect('expose-event', self.wpm_expose_cb)
         #self.accuracyarea = gtk.DrawingArea()
         #self.accuracyarea.connect('expose-event', self.accuracy_expose_cb)
-
+        
         hbox = gtk.HBox()
         hbox.pack_start(stopbtn, False, False, 10)
         hbox.pack_start(self.wpmlabel, True, False, 10)
         hbox.pack_start(self.accuracylabel, True, False, 10)
         hbox.pack_end(title, False, False, 10)
-
+        
         # Set up font styles.
         self.tagtable = gtk.TextTagTable()
         instructions_tag = gtk.TextTag('instructions')
-        instructions_tag.props.size = 10000
-
+        #instructions_tag.props.size = 10000
+        instructions_tag.props.justification = gtk.JUSTIFY_CENTER
+        
         self.tagtable.add(instructions_tag)
         text_tag = gtk.TextTag('text')
         text_tag.props.family = 'Monospace'
         self.tagtable.add(text_tag)
-
+        
         correct_copy_tag = gtk.TextTag('correct-copy')
         correct_copy_tag.props.family = 'Monospace'
         correct_copy_tag.props.foreground = '#0000ff'
         self.tagtable.add(correct_copy_tag)
-
+        
         incorrect_copy_tag = gtk.TextTag('incorrect-copy')
         incorrect_copy_tag.props.family = 'Monospace'
         incorrect_copy_tag.props.foreground = '#ff0000'
         self.tagtable.add(incorrect_copy_tag)
-
+        
         # Set up the scrolling lesson text view.
         self.lessonbuffer = gtk.TextBuffer(self.tagtable)
         self.lessontext = gtk.TextView(self.lessonbuffer)
@@ -158,48 +170,50 @@ class LessonScreen(gtk.VBox):
         self.lessonscroll = gtk.ScrolledWindow()
         self.lessonscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
         self.lessonscroll.add(self.lessontext)
-
+        
         frame = gtk.Frame()
         frame.add(self.lessonscroll)
-
+        
         self.keyboard = keyboard.Keyboard(self.activity)
         self.keyboard.set_layout(keyboard.DEFAULT_LAYOUT)
-
+        
         activity.add_events(gtk.gdk.KEY_PRESS_MASK)
         activity.connect('key-press-event', self.key_press_cb)
-
+        
         self.pack_start(hbox, False, False, 10)
         self.pack_start(frame, True, True)
         self.pack_start(self.keyboard, True)
-
+        
         self.show_all()
-
+        
         self.begin_lesson()
 
-    def begin_lesson(self):
-        self.total_keys = 0
-        self.correct_keys = 0
-        self.incorrect_keys = 0
-
-        self.start_time = None
-
-        self.next_step_idx = 0
-        self.advance_step()
-
     def update_stats(self):
         self.total_time = time.time() - self.start_time
         if self.total_time >= 1.0:
-            self.wpm = 60 * (self.correct_keys / 10) / self.total_time
+            self.wpm = 60 * (self.correct_keys / 5) / self.total_time
         else:
             self.wpm = 1.0
         self.accuracy = 100.0 * self.correct_keys / self.total_keys
-
+        
         self.accuracylabel.set_markup(_('<b>Accuracy:</b> %(accuracy)d%%') % { 'accuracy' : int(self.accuracy) } )
         self.wpmlabel.set_markup(_('<b>WPM:</b> %(wpm)d') % { 'wpm': int(self.wpm) } )
 
-    def wrap_line(self, line):
-        LINE_LENGTH = 80
+    def begin_lesson(self):
+        self.lesson_finished = False
+        
+        self.medal = None
+        
+        self.total_keys = 0
+        self.correct_keys = 0
+        self.incorrect_keys = 0
+        
+        self.start_time = None
+        
+        self.next_step_idx = 0
+        self.advance_step()
 
+    def wrap_line(self, line):
         words = line.split(' ')
         new_lines = []
         cur_line = ''
@@ -229,41 +243,44 @@ class LessonScreen(gtk.VBox):
         # Clear step related variables.
         self.step = None
         self.step_type = None
-
+        
         self.text = None
         self.line = None
         self.line_marks = None
-
+        
         self.key_expected = None
-
+        
         # End lesson if this is the last step.
         if self.next_step_idx >= len(self.lesson['steps']):
-            self.finish_lesson()
+            self.lesson_finished = True
+            self.show_lesson_report()
             return
         
         # TODO - Play 'step finished' sound here.
         
         self.step = self.lesson['steps'][self.next_step_idx]
         self.next_step_idx = self.next_step_idx + 1
-
-        self.step_type = self.step['type']
+        
+        if len(self.step['text']) == 1:
+            self.step_type = 'key'
+        else:
+            self.step_type = 'text'
         
         if self.step_type == 'text':
             # Clear the text buffer and output the instructions.
             self.lessonbuffer.set_text('')
             self.lessonbuffer.insert_with_tags_by_name(
-                self.lessonbuffer.get_end_iter(), self.step['instructions'] + '\n\n', 'instructions')
+                self.lessonbuffer.get_end_iter(), '\n' + self.step['instructions'] + '\n\n', 'instructions')
             
-            # Replace newlines with paragraph marks.
             self.text = unicode(self.step['text'])
             
             # Split text into lines.
-            self.lines = self.text.split('\n')
+            self.lines = self.text.splitlines(True)
             
-            # Append paragraph codes.
-            self.lines = [l + PARAGRAPH_CODE for l in self.lines]
+            # Substitute paragraph codes.
+            self.lines = [l.replace('\n', PARAGRAPH_CODE) for l in self.lines]
             
-            # TODO: Split by line length in addition to by paragraphs.
+            # Split by line length in addition to by paragraphs.
             for i in range(0, len(self.lines)):
                 line = self.lines[i]
                 if len(line) > 30:
@@ -288,11 +305,11 @@ class LessonScreen(gtk.VBox):
             # Clear the text buffer and output the instructions.
             self.lessonbuffer.set_text('')
             self.lessonbuffer.insert_with_tags_by_name(
-                self.lessonbuffer.get_end_iter(), self.step['instructions'] + '\n\n', 'instructions')
-
+                self.lessonbuffer.get_end_iter(), '\n' + self.step['instructions'] + '\n\n', 'instructions')
+            
             # Prepare the key.            
-            self.key_expected = unicode(self.step['text'][0])
-
+            self.key_expected = unicode(self.step['text'][0]).replace('\n', PARAGRAPH_CODE)
+            
             # Hilite the key on the virtual keyboard.
             self.keyboard.clear_hilite()
             key = self.keyboard.find_key_by_letter(self.key_expected)
@@ -302,7 +319,7 @@ class LessonScreen(gtk.VBox):
     def begin_line(self):
         self.line = self.lines[self.line_idx]
         self.line_mark = self.line_marks[self.line_idx]
-
+        
         self.char_idx = 0
         
         self.hilite_next_key()
@@ -311,22 +328,28 @@ class LessonScreen(gtk.VBox):
         # Ignore hotkeys.
         if event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK):
             return
-
+        
         # Extract information about the key pressed.
         key = gtk.gdk.keyval_to_unicode(event.keyval)
         if key != 0: key = chr(key)
         key_name = gtk.gdk.keyval_name(event.keyval)
-
+        
+        # Simply wait for a return keypress on the lesson finished screen.
+        if self.lesson_finished:
+            if key_name == 'Return':
+                self.end_lesson()
+            return
+        
         # Convert Return keys to paragraph symbols.
         if key_name == 'Return':
             key = PARAGRAPH_CODE
-
+        
         print "key_press_cb: key=%s key_name=%s event.keyval=%d" % (key, key_name, event.keyval)
-
+        
         # Timer starts with first keypress.
         if not self.start_time:
             self.start_time = time.time()
-
+        
         # Handle backspace by deleting text and optionally moving up lines.
         if self.step_type == 'text':
             if key_name == 'BackSpace':
@@ -334,43 +357,43 @@ class LessonScreen(gtk.VBox):
                 if self.char_idx == 0 and self.line_idx > 0:
                     self.line_idx -= 1 
                     self.begin_line()
-    
+                    
                     self.char_idx = len(self.line)
-    
+                
                 # Then delete the current character.
                 if self.char_idx > 0:
                     self.char_idx -= 1
-    
+                    
                     iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
                     iter.forward_chars(self.char_idx)
-    
+                    
                     iter_end = iter.copy()
                     iter_end.forward_char()
-    
+                    
                     self.lessonbuffer.delete(iter, iter_end)
     
             # Process normal key presses.
             elif key != 0:
-    
+                
                 # Check to see if they pressed the correct key.
                 if key == self.line[self.char_idx]:
                     tag_name = 'correct-copy'
                     self.correct_keys += 1
                     self.total_keys += 1
-    
+                    
                 else:
                     # TODO - Play 'incorrect key' sound here.
-    
+                    
                     tag_name = 'incorrect-copy'
                     self.incorrect_keys += 1
                     self.total_keys += 1
-    
+                
                 # Insert the key into the bufffer.
                 iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
                 iter.forward_chars(self.char_idx)
-    
+                
                 self.lessonbuffer.insert_with_tags_by_name(iter, key, tag_name)
-    
+                
                 # Advance to the next character (or else).
                 self.char_idx += 1
                 if self.char_idx >= len(self.line):
@@ -381,9 +404,9 @@ class LessonScreen(gtk.VBox):
                         self.begin_line()
                 
                 self.update_stats()
-
+            
             self.hilite_next_key()
-
+        
         elif self.step_type == 'key':
             
             # Check to see if they pressed the correct key.
@@ -422,16 +445,11 @@ class LessonScreen(gtk.VBox):
         self.lessontext.grab_focus()
 
         # Scroll the TextView so the cursor is on screen.
-        mark = self.lessonbuffer.get_insert()
-        self.lessontext.scroll_to_mark(mark, 0.1)
+        self.lessontext.scroll_to_mark(self.lessonbuffer.get_insert(), 0.1)
 
-    def finish_lesson(self):
-        self.activity.pop_screen()
-        
+    def show_lesson_report(self):
         self.update_stats()
-        #self.add_text(_('Congratulations!  You finished the lesson in %(time)d seconds.\n\n') % 
-        #    { 'time': int(self.total_time) } )
-        
+
         lesson_name = self.lesson['name']
         
         # Add to the lesson history.
@@ -445,28 +463,30 @@ class LessonScreen(gtk.VBox):
         
         # Show the medal screen, if one should be given.
         got_medal = None
-        for medal_name in ['bronze', 'silver', 'gold']:
-            medal = self.lesson['medals'][medal_name]
+        for medal in MEDALS:
             if self.wpm >= medal['wpm'] and self.accuracy >= medal['accuracy']:
-                got_medal = medal_name
+                got_medal = medal['name']
 
         if got_medal:
             # Award the medal.
             medal = {
                 'lesson': lesson_name,
-                'type': medal_name,
+                'type': got_medal,
                 'date': datetime.date.today().strftime('%B %d, %Y'),
                 'nick': self.activity.owner.props.nick,
                 'time': self.total_time,
                 'wpm': report['wpm'],
                 'accuracy': report['accuracy']
             }
+            self.medal = medal
 
+            # Compare this medal with any existing medals for this lesson.
+            # Only record the best one.
             add_medal = True
             if self.activity.data['medals'].has_key(lesson_name):
                 old_medal = self.activity.data['medals'][lesson_name]
 
-                order = 'bronze silver gold'
+                order = ' '.join([m['name'] for m in MEDALS])
                 add_idx = order.index(medal['type'])
                 old_idx = order.index(old_medal['type']) 
 
@@ -478,13 +498,63 @@ class LessonScreen(gtk.VBox):
                     elif medal['accuracy'] == old_medal['accuracy']:
                         if medal['wpm'] < old_medal['wpm']:
                             add_medal = False
-
+            
             if add_medal:
                 self.activity.data['medals'][lesson_name] = medal
                 self.activity.mainscreen.update_medals()
-
-            # Show the new medal (regardless of whether it was recorded).
-            self.activity.push_screen(MedalScreen(medal, self.activity))
+        
+        # Display results to the user.
+        text = '\n'
+        
+        congrats = [
+            _('Good job!'),
+            _('Well done!'),
+            _('Nice work!'),
+            _('Way to go!')
+        ]
+        text += random.choice(congrats) + '\n\n'
+        
+        text += _('You finished the lesson in %(time)d seconds, with %(errors)d errors.\n\n') % \
+            { 'time': int(self.total_time), 'errors': self.incorrect_keys }
+        text += _('Your words per minute (WPM) was %(wpm)d, and your accuracy was %(accuracy)d%%.\n\n') % \
+            report
+        
+        if self.medal:
+            # TODO: Play medal sound here.
+            
+            text += _('Congratulations!  You earned a %(type)s medal!\n\nPress Enter to see your certificate.') % \
+            medal
+            
+        else:
+            # Comment on what the user needs to do better.
+            need_wpm = report['wpm'] < self.lesson['medals']['bronze']['wpm']
+            need_accuracy = report['accuracy'] < self.lesson['medals']['bronze']['accuracy']
+            
+            if need_accuracy and need_wpm:
+                text += _('You need to practice this lesson more before moving on.  If you are having a hard time, '
+                          'repeat the earlier lessons until you have mastered them completely before trying this one '
+                          'again.\n\n')
+                
+            elif need_accuracy:
+                text += _('You almost got a medal!  Next time, try not to make as many errors!\n\n')
+                
+            elif need_wpm:
+                text += _('You almost got a medal!  Next time, try to type a little faster!\n\n')
+                
+            text += _('Press Enter to return to the main screen.')
+            
+        self.lessonbuffer.set_text('')
+        self.lessonbuffer.insert_with_tags_by_name(
+            self.lessonbuffer.get_end_iter(),
+            text,
+            'instructions')
+        
+    def end_lesson(self):
+        self.activity.pop_screen()
+        
+        # Show the new medal if there was one.
+        if self.medal:
+            self.activity.push_screen(MedalScreen(self.medal, self.activity))
 
     def stop_cb(self, widget):
         self.activity.pop_screen()
-----------------------------------------------------------------------


--
/home/olpc-code/git/activities/typing-turtle


More information about the Commits mailing list