[Commits] typing-turtle branch master updated.

Wade Brainerd wadetb at gmail.com
Thu Jan 29 00:02:34 EST 2009


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  27fbcc80d5435f943dcd63bbf375529fe39af3b3 (commit)
      from  6fb4ec696e195691cc6611b981fc50fbfb5a4f76 (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.

 balloongame.py                    |   72 +++++++++++++++++-------------------
 lessonbuilder                     |   66 +++++++++++++++------------------
 lessons/en_US/bottomrow.lesson    |   11 ++----
 lessons/en_US/homerow.lesson      |    2 +-
 lessons/en_US/leftcapital.lesson  |   11 ++----
 lessons/en_US/rightcapital.lesson |   11 ++----
 lessons/en_US/toprow.lesson       |   11 ++----
 mainscreen.py                     |   34 ++++++++++++------
 8 files changed, 100 insertions(+), 118 deletions(-)

- Log -----------------------------------------------------------------
commit 27fbcc80d5435f943dcd63bbf375529fe39af3b3
Author: Wade Brainerd <wadetb at gmail.com>
Date:   Thu Jan 29 05:01:04 2009 +0000

    Optimize lesson builder, add colors to balloongame (busts performance atm), fallback to en_US when no lessons found, remove req_keys pairs from lessons - seemed dumb and pointless.

diff --git a/balloongame.py b/balloongame.py
index 3e4b947..5caaef9 100644
--- a/balloongame.py
+++ b/balloongame.py
@@ -19,18 +19,11 @@ from gettext import gettext as _
 
 import gobject, pygtk, gtk, pango
 
-# Each 'stage' contains a certain number of balloons as well as
-# parameters about them.
-BALLOON_STAGES = [
-    { 'count': 10, 'delay': 80 },
-    #{ 'count': 20, 'delay': 60 },
-    #{ 'count': 70, 'delay': 40 },
-]
-
-DEFAULT_MEDALS = [
-    { 'name': 'bronze', 'score': 4000 },
-    { 'name': 'silver', 'score': 6000 },
-    { 'name': 'gold',   'score': 10000 }
+BALLOON_COLORS = [
+    (65535, 0, 0),
+    (0, 0, 65535),
+    (65535, 32768, 0),
+    (0, 32768, 65535),
 ]
 
 class Balloon:
@@ -40,7 +33,8 @@ class Balloon:
         self.vx = vx
         self.vy = vy
         self.word = word
-        self.size = 100
+        self.size = max(100, 50 + len(word) * 20) 
+        self.color = random.choice(BALLOON_COLORS)
 
 class BalloonGame(gtk.VBox):
     def __init__(self, lesson, activity):
@@ -65,7 +59,7 @@ class BalloonGame(gtk.VBox):
         
         # Build the game drawing area.
         self.area = gtk.DrawingArea()
-        self.area.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffffff'))
+        self.area.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#c0c0ff'))
         self.area.connect("expose-event", self.expose_cb)
 
         # Connect keyboard grabbing and releasing callbacks.        
@@ -83,9 +77,8 @@ class BalloonGame(gtk.VBox):
         self.score = 0
         self.spawn_delay = 10
 
-        self.stage_idx = 0
-        self.stage = BALLOON_STAGES[self.stage_idx]
-        self.count_left = self.stage['count']
+        self.count = 0
+        self.count_left = self.lesson.get('length', 60)
 
         self.medal = None
         self.finished = False
@@ -169,31 +162,29 @@ class BalloonGame(gtk.VBox):
 
         self.spawn_delay -= 1
         if self.spawn_delay <= 0:
+            self.count += 1
             self.count_left -= 1
 
-            if self.count_left <= 0:
-                self.stage_idx += 1
-
-                if self.stage_idx < len(BALLOON_STAGES):
-                    self.stage = BALLOON_STAGES[self.stage_idx]
-                    self.count_left = self.stage['count']
-
-            else:
-                word = random.choice(self.lesson['words'])
+            word = random.choice(self.lesson['words'])
 
-                x = random.randint(100, self.bounds.width - 100)
-                y = self.bounds.height + 100
+            x = random.randint(100, self.bounds.width - 100)
+            y = self.bounds.height + 100
 
-                vx = random.uniform(-2, 2)
-                vy = -3 #random.uniform(-5, -3)
+            vx = random.uniform(-2, 2)
+            vy = -3 #random.uniform(-5, -3)
 
-                b = Balloon(x, y, vx, vy, word)
-                self.balloons.append(b)
+            b = Balloon(x, y, vx, vy, word)
+            self.balloons.append(b)
 
-                delay = self.stage['delay']
-                self.spawn_delay = random.randint(delay-20, delay+20)
+            if self.count < 10:
+                delay = 80
+            elif self.count < 20:
+                delay = 60
+            else:
+                delay = 40
+            self.spawn_delay = random.randint(delay-20, delay+20)
 
-        if len(self.balloons) == 0 and self.stage_idx >= len(BALLOON_STAGES):
+        if len(self.balloons) == 0 and self.count_left <= 0:
             self.finish_game()
  
         return True
@@ -248,7 +239,7 @@ class BalloonGame(gtk.VBox):
         # Show the medal screen, if one should be given.
         got_medal = None
         
-        medals = self.lesson.get('medals', DEFAULT_MEDALS)
+        medals = self.lesson['medals']
         for medal in medals:
             if self.score >= medal['score']:
                 got_medal = medal['name']
@@ -293,20 +284,25 @@ class BalloonGame(gtk.VBox):
         x1 = int(b.x - b.size/2)
         y1 = int(b.y - b.size/2)
         x2 = int(b.x + b.size/2)
-        y2 = int(b.y + b.size/2)
+        y2 = int(b.y + b.size/2 - b.size*b.vy)
         self.queue_draw_area(x1, y1, x2, y2)
 
     def draw_balloon(self, gc, b):
         x = int(b.x)
         y = int(b.y)
         
+        # Draw the string.
+        gc.foreground = self.area.get_colormap().alloc_color(0,0,0)
+        self.area.window.draw_line(gc, b.x, b.y+b.size/2, b.x-int(b.size/2*b.vx), b.y+b.size/2-int(b.size/2*b.vy))
+        
         # Draw the balloon.
-        gc.foreground = self.area.get_colormap().alloc_color(65535,0,0)
+        gc.foreground = self.area.get_colormap().alloc_color(b.color[0],b.color[1],b.color[2])
         self.area.window.draw_arc(gc, True, x-b.size/2, y-b.size/2, b.size, b.size, 0, 360*64)
     
         # Draw the text.
         gc.foreground = self.area.get_colormap().alloc_color(0,0,0)
         layout = self.area.create_pango_layout(b.word)
+        layout.set_font_description(pango.FontDescription('Sans 12'))    
         size = layout.get_size()
         tx = x-(size[0]/pango.SCALE)/2
         ty = y-(size[1]/pango.SCALE)/2
diff --git a/lessonbuilder b/lessonbuilder
index ec7f255..73a232a 100755
--- a/lessonbuilder
+++ b/lessonbuilder
@@ -16,7 +16,7 @@
 # You should have received a copy of the GNU General Public License
 # along with Typing Turtle.  If not, see <http://www.gnu.org/licenses/>.
 
-import os, sys, random, simplejson, locale, re
+import os, sys, random, simplejson, locale, re, optparse
 from gettext import gettext as _
 
 # For modifier constants.
@@ -172,13 +172,13 @@ def get_pairs_from_wordlist(words):
             pairs.append((c0+c1, c1_value))
 
     # Sort by frequency.
-    pairs.sort(cmp=lambda x,y: x[1] - y[1])
+    #pairs.sort(cmp=lambda x,y: x[1] - y[1])
     
     # Normalize the weights.
-    total = 0.0
-    for p in pairs:
-        total += p[1]
-    pairs = [(p[0], p[1]/total) for p in pairs]
+    #total = 0.0
+    #for p in pairs:
+    #    total += p[1]
+    #pairs = [(p[0], p[1]/total) for p in pairs]
 
     return pairs
 
@@ -236,35 +236,26 @@ def filter_wordlist(words, all_keys, req_keys, minlen, maxlen, bad_words):
     # Filter word list based on variety of contraints.
     good_words = []
 
+    unknown_re = re.compile('[^'+re.escape(all_keys)+']')
+    req_re = re.compile('['+re.escape(req_keys)+']')
+
     for word in words:
         if len(word) < minlen or len(word) > maxlen:
             continue
         
-        good = True
-        
         # Check for letters that are not supported.
-        for c in word:
-            if all_keys.find(c) == -1:
-                good = False
-                break
-        
-        # Make sure required letters are present.
-        any_req = False
-        for c in req_keys:
-            if word.find(c) >= 0:
-                any_req = True
-                break
-        if not any_req:
-            good = False
+        if unknown_re.search(word):
+            continue 
         
+        # Make sure at least one required letter is present.
+        if not req_re.search(word):
+            continue
+
         # Remove bad words.
-        for bad in bad_words:
-            if word == bad:
-                good = False
-                break
+        if word in bad_words:
+            continue
         
-        if good:
-            good_words.append(word)
+        good_words.append(word)
 
     return good_words
 
@@ -307,7 +298,7 @@ def build_key_steps(
         minlen=2, maxlen=8, 
         bad_words=bad_words)
     
-    pairs = get_pairs_from_wordlist(words)
+    pairs = get_pairs_from_wordlist(good_words)
         
     steps = []
 
@@ -365,13 +356,8 @@ def build_key_steps(
     
     steps.append(make_step(
         get_congrats() + _('Now put the keys together into pairs.'),
-        'text', make_weighted_wordlist_pairs(pairs, new_keys, new_keys, count)))
+        'text', make_weighted_wordlist_pairs(pairs, new_keys, all_keys, count)))
     
-    if base_keys != '':
-        steps.append(make_step(
-            get_congrats() + _('Now practice all the keys you know.'),
-            'text', make_weighted_wordlist_pairs(pairs, new_keys, all_keys, count)))
-
     if len(good_words) == 0:
         steps.append(make_step(
             get_congrats() + _('Time to type jumbles.'),
@@ -433,8 +419,7 @@ def build_text_step(path):
 
     return steps
 
-if __name__ == "__main__":
-    import optparse
+def main():
     parser = optparse.OptionParser("usage: %prog [options]")
 
     parser.add_option("--title", dest="name", default="Generated",
@@ -555,6 +540,7 @@ if __name__ == "__main__":
             parser.error('no wordlist file given')
         
         lesson['type'] = options.game
+        lesson['length'] = options.length
         lesson['words'] = build_game_words(
             new_keys=options.keys, base_keys=options.base_keys, 
             words=words, bad_words=bad_words)
@@ -562,3 +548,11 @@ if __name__ == "__main__":
     text = simplejson.dumps(lesson, sort_keys=True, indent=4)
 
     open(options.output, 'w').write(text)
+
+if __name__ == "__main__":
+    try:
+        main()
+    except KeyboardInterrupt:
+        print "Ctrl-C detected, aborting."
+        sys.exit(1)
+
diff --git a/lessons/en_US/bottomrow.lesson b/lessons/en_US/bottomrow.lesson
index 830e5f9..0dd489e 100644
--- a/lessons/en_US/bottomrow.lesson
+++ b/lessons/en_US/bottomrow.lesson
@@ -71,17 +71,12 @@
         {
             "instructions": "Nice work. Now put the keys together into pairs.", 
             "mode": "text", 
-            "text": "nb bc cm nn mv nb mc nn cb nn bb zz mm vv bv zv cb xv nz zz nn nn cb vv vn nv bz cn bc zb mv cm nm nn zc cz xb nx mn mn zm zn nv zm zb xm nm cc xc zb vn vn bc nc xn nb nm xv zc xm"
+            "text": "tn nn gb zi km tn lb zd an zl vy rz xo md ob em ac cq pm rz zd zl ax mj cr vy ci mq nl hb mc dn wn zp fb mw bo ne ub ub ib ec ym ix kb bf wn xc sm hm cz cr nq zw bs tn wm cm gz bc"
         }, 
         {
-            "instructions": "Nice work. Now practice all the keys you know.", 
+            "instructions": "Nice work. Time to type real words.", 
             "mode": "text", 
-            "text": "mj va cu zl ms xq pn yb bp vd mn ym bw kc cm pn ax ns cd xd ng mg vi ov rv cu zc bv rc zm ez lm ox ux ev tb ic im wn cl am zq tb yv sv xi xa wc em mu lb ov nx mn hn lb ox cl xo md"
-        }, 
-        {
-            "instructions": "Wonderful! Time to type real words.", 
-            "mode": "text", 
-            "text": "sinewed czardom uneven smasher schooner masked prone syllable oxidant protect genuine cooties slovenly oxidize garcon fogeydom bluesy ultimo fauces coot overstep realign fusion retrace ulna mauler rhetoric bible remade gruesome shelling pinwheel chyme phonemic cinerary indict lighting vasa quean resource heretic slagging ambition cope zygotic deprive canner chide courtly marked former illness baseball peatbog dogmatic cryer slavish grovel culprit em"
+            "text": "enroll joiner clamper bespeak impugn baseness backhoe bland yclept moniker tubercle begonia guidable spacer albacore evitable mastic dominate arable swanky uncap bobwhite resound weaken quatrain cedi fuck phylum quantity sterling martini scripted outrun tween reverie numerate passably joint peony maturely bat homogeny mitosis measly unfroze unground fauve cliched lambkin plainly headpin archival skim betta blueness sizing publish subunit serve carcass"
         }, 
         {
             "instructions": "$report", 
diff --git a/lessons/en_US/homerow.lesson b/lessons/en_US/homerow.lesson
index da3c28a..4001302 100644
--- a/lessons/en_US/homerow.lesson
+++ b/lessons/en_US/homerow.lesson
@@ -81,7 +81,7 @@
         {
             "instructions": "Nice work. Now put the keys together into pairs.", 
             "mode": "text", 
-            "text": "dl kl fs ll kf dl kh ll jd la sl af ad sd lg sj fj hj ds af ll la jd sg hj gl jj hf kl aa hs hk gh la gf aj gk lf af af hd fh da kk kf lj gh ag ka aa fg hj ja ss kg dl gh fd hh fg"
+            "text": "lf ja da sk gl lf ha sk ad sk ls ld ss fa hh as ad af la ld sk sk ad fl af ls ag fl ka ga fa da ll sl dd ff aj hl lg lg ga al sa ga gl ak ll sa la gl ah af ja sl aj lf ll af dd ah"
         }, 
         {
             "instructions": "Nice work. Time to type real words.", 
diff --git a/lessons/en_US/leftcapital.lesson b/lessons/en_US/leftcapital.lesson
index 8f0e504..eed7334 100644
--- a/lessons/en_US/leftcapital.lesson
+++ b/lessons/en_US/leftcapital.lesson
@@ -111,17 +111,12 @@
         {
             "instructions": "Nice work. Now put the keys together into pairs.", 
             "mode": "text", 
-            "text": "RV TT DD WA FA RV SW AS AQ WA BC SA CA SE RB EQ AQ CS AW SA AS WA AR SD CS BC CC SG TR DV FT ET VC AB DS SR EC TD SC SC DW EG DT DV GB BD VC ST CR GB CT CS TT AT EB RV VC CE DQ BB"
+            "text": "TD Sa Br Ze Er TD Gd Za AR Zi TS Ro Vo Ez Gu CR AC Ak Su Ro Za Zh AW Di Am Wa Ae Dn ST En Ey Bh Ts Zs Ec Dy Ce Fo RT RT Eg Cy We Em Es Av Ts Wy Sv En Aq Am Sa Zw Ch TD To Af By Ap"
         }, 
         {
-            "instructions": "Nice work. Now practice all the keys you know.", 
+            "instructions": "Nice work. Time to type real words.", 
             "mode": "text", 
-            "text": "Ax Am Cl DT AB DV CB Zh Eo CS AT Sw uB Tz FT CB Aq Si CR Cn An Ao Ve Va Rh Cl SG Qi Er Sq Av Ty By Vu Ab AS Be Tr Ev Ce To BD Zu Zs Gd Ea Cz Xu Wa Fo Ai Va Sv Ry Ec Ai By Ce Aa TS"
-        }, 
-        {
-            "instructions": "Wonderful! Time to type real words.", 
-            "mode": "text", 
-            "text": "Sikh Cathie Rollin Glenn Severn Bengal Eadwig Goddard Carib Scorpius Theban Wilton Boeotian Breton Catania Rosalie Torbay Tet Rowland Gretchen Salween Corday Tupian Aachen Bush Rushmore Alamo Cedric Shylock Walker Smollett Chinese Cochin Quechua Donne Wisdom Tver Beninese Bede Quebecer Amman Bromley Angles Satanism Annamese Silesia Daniel Chengdu Freda Thimbu Sporades Copland Eleanor Chretien Freyja Devonian Elinor Eskimoan Susanna Segovia"
+            "text": "Dniester Alpine Antalya Tartary Alvin Gordimer Trollope Triad Sphinx Spencer Brady Dalmatia Zeus Qiqihar Quintin Actium Gilman Craiova Blanch Dirk Titicaca Silvia Ruskin Andy Burgundy Cetus Egyptian Deneb Zola Ennius Cos Romblon Ronny Thaddeus Rupert Beryl Cobb Fatima Colbert Rome Bagdad Calvert Cummings Freyja Choctaw Eichmann Simon Bulgar Chauncey Bourbon Roland Tuesday Rumania Sextans Ella Wycliffe Ronny Born Docetist Guianan"
         }, 
         {
             "instructions": "$report", 
diff --git a/lessons/en_US/rightcapital.lesson b/lessons/en_US/rightcapital.lesson
index a160621..c098bc0 100644
--- a/lessons/en_US/rightcapital.lesson
+++ b/lessons/en_US/rightcapital.lesson
@@ -91,17 +91,12 @@
         {
             "instructions": "Nice work. Now put the keys together into pairs.", 
             "mode": "text", 
-            "text": "UI PI KP MI MH UI OK MI II MI KO PU YM LI OM HI II IN PO PU MI MI II LI IN KO IO LO PI MO MH KP IJ OL MP OK HM OU PU PU MP HI ML MO MH IU IJ LP PO MO IP IN PI OL HM UI IJ IO KP IP"
+            "text": "Pe Og HM Yi JA Pi Lh Ye AL Yo Ub PC TO MO LT Is AI DO Oz PC Ye Yo CO MC GI UD EM MT Oe Ku Js HD Pu Yu HU LA IW OL PD PD KG Iq UI Ko Ky II Pt Up Ne Ku FM GI Oi cJ Ib Pi Pr DI HT IA"
         }, 
         {
-            "instructions": "Nice work. Now practice all the keys you know.", 
+            "instructions": "Nice work. Time to type real words.", 
             "mode": "text", 
-            "text": "BI Mc Yo AN PE IW cJ Ms PM EN Uf Ia NT LO HD cJ Um Ki PD DH Ka BL Ly Py NA Yo HU UF Ut MP OS Im CO IV Ib LP He Is Ps Om Mu DP LP OP SO Ub OS OP Or It SI Py TK Uk ID Pt CO Om Iq Lj"
-        }, 
-        {
-            "instructions": "Wonderful! Time to type real words.", 
-            "mode": "text", 
-            "text": "Molucca Mbini Harrow Ipswich Kanpur Ophelia Parkman Yafo Isabel Lori Marconi Herschel Jacky Lome Long Nehemiah Knox Howrah Marcia Jere LVN Orville Hounslow Loafer Psalms Martian Poznan Lapland Plato Kenton Hutu Judean Lamb Izmir Kepler Hazel Mainz Mortimer Mozart Hopper Kremlin Kampala Laplace Mendel Humanism Iowan Judith Yoruban Kevlar Moravian Hassid Owens Kroc Islamic Libyan Macaulay Hellene Levis Karelian Ming"
+            "text": "MC Paula Pilipino Orlando Niles Manchu Johanna Juneau Harry Nagoya Uppsala Numidia Peking Jeanie Median Manet Plymouth Kosice Lydian Justine Kilauea Hurston Pilate Lister Lear Mohammed Merle Kiel Marlyn Heather Lett Lapp Planck Harold Mongolic Nauru Pavlov Jackie Klondike Patna Maranon Milne Nauru Laplace Parmesan Oder Papuan Kola Melville Oriental Lipetsk Iqaluit Kamet Herbert Magus Mongolia Planck Patna Lofoten Mizar"
         }, 
         {
             "instructions": "$report", 
diff --git a/lessons/en_US/toprow.lesson b/lessons/en_US/toprow.lesson
index 9804dd1..f6742a2 100644
--- a/lessons/en_US/toprow.lesson
+++ b/lessons/en_US/toprow.lesson
@@ -86,17 +86,12 @@
         {
             "instructions": "Nice work. Now put the keys together into pairs.", 
             "mode": "text", 
-            "text": "ie ui rw ri uo ie oi ri qw te pr tu ro iu eo tp qe qi ru tu ri te yy yp ii ur uw eu ue yr ye yi rt re wr ew wt pp ry ry py oq pe tw uo pw rt to ep yr wu qi ow ti wy ie ty ww yo iw"
+            "text": "uo se iq yf ku uq op yg aq yh to ua wp ju qe fw ae ej ri ua yg yk ar lr eq tp ee oe pw ke ji ik uy yo iw ok gi ph ud ud hi fu tu hu kr dp ta wd rl ke eu eo si yr gu uq uw ei ir ew"
         }, 
         {
-            "instructions": "Nice work. Now practice all the keys you know.", 
+            "instructions": "Nice work. Time to type real words.", 
             "mode": "text", 
-            "text": "qw ty po uo ht gp uf gy uk fp ud ip ej tg oj uf oi tr ws tj is qe pr ap qu po pw sq ai ij ps up lt iu ig ys te ol tl pi he wg ys aq ko fo ey gt lo gi fr ap yo ud we fr lt pi wh wf"
-        }, 
-        {
-            "instructions": "Wonderful! Time to type real words.", 
-            "mode": "text", 
-            "text": "pulse leapfrog sis skewed keel darkly whereas resell egad thirty regrew are lari shower dislodge outplay teepee seed papule pot trooper fatwa flowery dogleg duty agist tatter rakishly dodder sweetish retrod glider diarist awe realty pied ratlike radish sheets filler ghetto rookie pleased plight rustily dessert stupor drily fiat fleshpot herald edged sparely daffy sweat julep withe raid testes rage"
+            "text": "parole oppress replete lawyer seafarer soursop effort toga hoary shyer rep peewee wisely towel appetite purr austere whites roguery waft paralyse outage fleer ratter aspire sledge idolatry jiff awol groper toe lager sawfish regulate tapster starless seller twirler ashes loaf gayety wort rewrote topple polite seeder outwit pipe deforest spider doily yuletide giggler talus outlast pathless sawfish patty adore drowsy"
         }, 
         {
             "instructions": "$report", 
diff --git a/mainscreen.py b/mainscreen.py
index 49641bc..fef569e 100644
--- a/mainscreen.py
+++ b/mainscreen.py
@@ -111,20 +111,20 @@ class MainScreen(gtk.VBox):
         self.prevlessonbtn.add(previcon)
         self.prevlessonbtn.connect('clicked', self.prev_lesson_clicked_cb)
         
+        # Load lessons for this language.
         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.
-        self.lessons = []
-        for f in glob.iglob(path + '/*.lesson'):
-            fd = open(f, 'r')
-            try:
-                lesson = json.read(fd.read())
-                self.lessons.append(lesson)
-            finally:
-                fd.close()
-        
+        self.load_lessons(path)
+
+        # Fallback to en_US lessons if none found.
+        if not len(self.lessons):
+            self.load_lessons(bundle_path + '/lessons/en_US')
+
+        # We cannot run without lessons/
+        if not len(self.lessons):
+            sys.exit(1)
+
         # Sort by the 'order' field.
         self.lessons.sort(lambda x, y: x.get('order', 0) - y.get('order', 0))
         
@@ -139,6 +139,18 @@ class MainScreen(gtk.VBox):
         
         self.show_next_lesson()
 
+    def load_lessons(self, path):
+        # Find all .lesson files in ./lessons/en_US/ for example.
+        self.lessons = []
+        for f in glob.iglob(path + '/*.lesson'):
+            fd = open(f, 'r')
+            try:
+                lesson = json.read(fd.read())
+                self.lessons.append(lesson)
+            finally:
+                fd.close()
+
+
     def get_next_lesson(self):
         """Returns the index of the first lesson without a medal."""
         index = len(self.lessons)-1
-----------------------------------------------------------------------


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


More information about the Commits mailing list