Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/plugins/_flashcard.py
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/_flashcard.py')
-rwxr-xr-xplugins/_flashcard.py728
1 files changed, 728 insertions, 0 deletions
diff --git a/plugins/_flashcard.py b/plugins/_flashcard.py
new file mode 100755
index 0000000..e1869ac
--- /dev/null
+++ b/plugins/_flashcard.py
@@ -0,0 +1,728 @@
+# Modified to play flashcards from .xml decks in flashcard folder
+# Currently
+# * Displaying Categories in set language,
+# * Pick English Questions
+
+
+import pygame
+from pygame import *
+
+import random
+from layout import *
+from frontend import hex2rgb
+from datetime import datetime
+import time
+import threading
+
+from xmlio import Xmlio
+import xml.etree.ElementTree as ET
+from path import path
+
+import os
+
+__PLUGIN_NAME__ = 'flashcard'
+QUIZSOURCE = '/home/olpc/Activities/ImageQuizPlus.activity/flashcards/flashcards'
+ICONSOURCE = '/home/olpc/Activities/ImageQuizPlus.activity/images'
+
+class CurrentQuestion:
+ id = 0
+ imgfn = u''
+ map = u''
+ cat = 0
+ subcat = 0
+ text = u''
+ answer = u''
+ answer_link = ''
+ hint = ''
+ more = ''
+ sound = ''
+ image = ''
+
+class Answer:
+ display = True
+ display_line = False
+ sound_enabled = True
+
+ link = u''
+ text = u''
+
+ img_found_count = 0
+ img_notfound_count = 0
+# imgfn_found = ["Emotes/face-grin.png", "Emotes/face-devil-grin.png", "Emotes/face-glasses.png"]
+# imgfn_notfound = ["Emotes/face-monkey.png", "Emotes/face-crying.png"]
+ imgfn_found = ["Emotes/face-grin.png"]
+ imgfn_notfound = ["Emotes/face-devil-grin.png"]
+ random.shuffle(imgfn_found)
+ random.shuffle(imgfn_notfound)
+
+ found_straight = 0
+ straight_count = 0
+ icon_left = 0
+ bg_icons = pygame.Surface((500, 32))
+ bg_icons_straight = pygame.Surface((500, 32))
+
+ def __init__(self):
+ self.sound_found = sf.sound_load("accessed.wav")
+ self.sound_notfound = sf.sound_load("sorry.wav")
+
+ def reset_points(self):
+ self.found_straight = 0
+ self.straight_count = 0
+ self.icon_left = 0
+
+ self.bg_icons.fill(hex2rgb(Layout().Toolbar.background))
+ self.bg_icons_straight.fill(hex2rgb(Layout().Toolbar.background))
+
+ sf.display_surface(self.bg_icons, (540, 10), "toolbar")
+ sf.display_surface(self.bg_icons_straight, (538, 50), "toolbar")
+ sf.refresh()
+
+ def add_straight(self):
+ # 5 in a row -> special reward
+ self.straight_count += 1
+ im, im_rect = sf.image_load("images/%s" % "Icons/bulb.png")
+ self.bg_icons_straight.blit(im, ((self.straight_count-1) * (im_rect[2] + 4), 0))
+ sf.display_surface(self.bg_icons_straight, (538, 50), "toolbar")
+ sf.refresh()
+
+ def display_icon(self, icon_name):
+ if icon_name == "found":
+ self.found_straight += 1
+ if self.found_straight == 5:
+ # Found Straight 5!
+ self.bg_icons.fill(hex2rgb(Layout().Toolbar.background))
+ sf.display_surface(self.bg_icons, (540, 10), "toolbar")
+ self.add_straight()
+ self.icon_left = 0
+ self.found_straight = 1
+
+ fn = self.imgfn_found[self.img_found_count % len(self.imgfn_found)]
+ self.img_found_count += 1
+
+ elif icon_name == "not-found":
+ self.found_straight = 0
+ fn = self.imgfn_notfound[self.img_notfound_count % len(self.imgfn_notfound)]
+ self.img_notfound_count += 1
+
+ img, img_rect = sf.image_load("images/%s" % fn)
+ self.bg_icons.blit(img, (self.icon_left, 0))
+
+ sf.display_surface(self.bg_icons, (540, 10), "toolbar")
+ sf.refresh()
+
+ self.icon_left += img_rect[2] + 4
+
+ class PlaySoundThread (threading.Thread):
+ def set(self, sound, interval):
+ self.sound = sound
+ self.i = interval
+
+ def run(self):
+ time.sleep(self.i)
+ self.sound.play()
+
+ def play_sound(self, i):
+ if self.sound_enabled:
+ t = self.PlaySoundThread()
+ if i == 1: t.set(self.sound_found, 0)
+ else: t.set(self.sound_notfound, 0.5)
+ t.start()
+
+ def display_answer(self):
+ if self.display_line: sf.draw_lines()
+ if self.display == False: return False
+
+ # Get Top Right Spot of list
+ print "showing answer"
+ map_arr = Q.question.map.split(", ")
+
+ # Widths & Heights
+ bubble_width = 400
+ bubble_height = 300
+ textfield_width = 270
+ textfield_height = 200
+
+ # Extremas of the Polygone will be saved in here:
+ x_max = 0
+ y_max = 0
+ x_min = 1000
+ y_min = 1000
+
+ # Extract Extremas from the polygon
+ o = []
+ a = "x"
+ for s in map_arr:
+ if a == "x": a = s
+ else:
+ # Take care of border pixels
+ if int(a) > x_max: x_max = int(a)
+ if int(s) > y_max: y_max = int(s)
+ if int(a) < x_min: x_min = int(a)
+ if int(s) < y_min: y_min = int(s)
+ a = "x"
+
+ # Set x and y for the Answer Bubble
+ y_med = (y_min + y_max) / 2
+ x_max -= 5
+
+ y_med = 150
+ x_max = 400
+
+ x_max += Layout().Question.x + Layout().Question.Image.x + 2
+ y_med += Layout().Question.y + Layout().Question.Image.y + 2
+
+# sf.draw_polygon()
+
+ # Draw Answer Bubble Image & Text
+ im, im_rect = sf.image_load('images/bubble.gif')
+
+ text_arr = self.text.split(' ')
+ cur_line = ''
+
+ cur_x = 0
+ cur_y = 0
+
+ # 'textfield' contains the answer text
+ textfield = pygame.Surface((textfield_width, textfield_height))
+ textfield.fill((255, 255, 255))
+
+ # Make line breaks after reaching width of 'textfield'
+ for t in text_arr:
+ cur_line = "%s %s" % (cur_line, t)
+
+ font = pygame.font.Font(None, 38)
+ n_text = font.render(cur_line, 1, (0,0,0))
+ textpos = n_text.get_rect()
+# #print cur_line,
+
+ x,y,w,h = list(textpos)
+ if w > (textfield_width):
+ textfield.blit(text, (cur_x, cur_y))
+ cur_line = t
+ cur_y += 30
+ written = 1
+ else:
+ written = 0
+ text = n_text
+
+# #print textpos
+
+ # Draw leftovers on textfield
+ if written == 0:
+ textfield.blit(n_text, (cur_x, cur_y))
+ else:
+ font = pygame.font.Font(None, 38)
+ n_text = font.render(cur_line, 1, (0,0,0))
+ textfield.blit(n_text, (cur_x, cur_y))
+
+# #print "draw"
+
+ # Draw on Screen
+ sf.display_surface(im, (x_max, y_med))
+ sf.display_surface(textfield, (x_max+25, y_med+20))
+
+ pygame.display.update()
+
+
+class Questions:
+ global path
+ _cat_id = -1
+ lang_id = 0
+ lang_name = 0
+ in_question = False
+
+ played = 0
+ count_won = 0
+ count_lost = 0
+ questions = []
+
+ def load(self, lang_id, lang_name):
+ self.lang_id = lang_id
+ self.lang_name = lang_name
+ self.question = CurrentQuestion()
+ self.load_categories()
+
+ def load_categories(self):
+ #here we need to get the names of folders and files in the flashcard folder
+ #make a list 'self.categories' with each entry a list containing:
+ #cat_id, text (name of category), count, parent_id, base_parent_id
+ #sorted alphabetically by text (name of category)
+ decks = path(QUIZSOURCE)
+ self.categories = []
+ for f in decks.files('*.xml'):
+ tree = Xmlio(f)
+ root = tree.getroot()
+ count = len(root)
+ catid = 1
+ parent = len(self.categories)
+ self.categories.append([catid, f, count, parent, 0])
+ self.categories.sort()
+
+ def question_pick(self):
+ # Check if Questions are left to play
+ if self.played >= len(self.questions):
+ # Game / Cat Finished!
+ return False
+
+ # Okay, Next One!
+ #print 'next', self.questions[self.played]
+ self.question.id = self.questions[self.played][0]
+# self.question.imgfn = self.questions[self.played][1]
+ self.question.imgfn = 'bubble.gif'
+ self.question.map = self.questions[self.played][2]
+ self.question.cat = self.questions[self.played][3]
+ self.question.subcat = self.questions[self.played][4]
+ self.question.text = self.questions[self.played][5]
+ self.question.answer = self.questions[self.played][6]
+ self.question.answer_link = self.questions[self.played][7]
+ self.question.hint = self.questions[self.played][8]
+ self.question.more = self.questions[self.played][9]
+ self.question.sound = self.questions[self.played][10]
+ self.question.image = self.questions[self.played][11]
+
+ A.text = self.question.answer
+ A.link = self.question.answer_link
+
+
+ self.played += 1
+
+ return True
+
+ def load_questions(self, cat_id):
+ #print 'cat_id = ', cat_id
+ # 2. Load Questions
+ self._cat_id = cat_id[0]
+ self._subcat_id = cat_id[1]
+
+ self.played = 0
+ self.count_won = 0
+ self.count_lost = 0
+
+ #ImageQuiz allows user to select one deck, all decks in category, or all decks
+ #Assume self.categories is built correctly
+ files = []
+ for cat in self.categories:
+ if self._cat_id < 0: #all selected
+ files.append(cat)
+ elif self._cat_id == cat[3] and self._subcat_id == 0: #all subcategories selected
+ files.append(cat)
+ #print 'cat', cat[3], cat[0]
+ elif self._cat_id == cat[3] and self._subcat_id == cat[0]: #specific subcategory within category selected
+ files.append(cat)
+ #print 'subcat', cat[3], cat[0], cat
+ flashcards = []
+ for file in files:
+ #print 'file', file
+ pathname = file[1]
+ #print 'loading', pathname + '\n'
+ tree = Xmlio(pathname)
+ deck = tree.getroot()
+ self.deck = deck
+ for card in deck:
+ cardid = int(card.get('id'))
+ question = card.findtext('question')
+ question_node = card.find('question')
+ if question_node:
+ sound = question_node.findtext('sound')
+ hint = question_node.findtext('hint')
+ else:
+ sound = ''
+ hint = ''
+ image = ''
+ answer_node = card.find('answer')
+ answer = card.findtext('answer')
+ if answer_node:
+ temp = answer_node.findtext('image')
+ if temp:
+ image = temp[:-4] + '.png'
+ else:
+ image = ''
+ more = answer_node.findtext('more')
+ else:
+ more = ''
+ image = ''
+ cat = file[3]
+ subcat = file[0]
+ flashcard = [cardid,'','',cat, subcat, question, answer,
+'', hint, more, sound, image]
+ flashcards.append(flashcard)
+ random.shuffle(flashcards)
+ self.questions = flashcards
+ #print 'number of questions=', len(self.questions)
+
+def show_answer():
+ A.display_answer()
+
+def finished():
+ sf.refresh()
+ sf.add_text_item("You have finished this category!", (180, 80))
+ sf.add_text_item("Won: %i" % Q.count_won, (220, 120))
+ sf.add_text_item("Lost: %i" % Q.count_lost, (220, 160))
+ pygame.display.update()
+# ask_category(130)
+
+
+def startgame(db_cat_id):
+ #print "flashcard"
+ #print "* Starting Game, Category:", db_cat_id
+ #print "** Loading Questions"
+
+ Q.count_won = 0
+ Q.count_won = 0
+ Q.shown = datetime.now()
+ A.reset_points()
+
+ Q.load_questions(db_cat_id)
+ display_points()
+
+ sf.clear_text_items()
+ next_question()
+
+
+def ask_subcat(c):
+ y = 110
+ #print 'subcat=', c
+ sf.add_text_item("Subcategory:", (580,y))
+ y += 50
+
+ sf.add_text_item('All', (600,y), startgame, (c, 0), True)
+
+ i = 0
+ for q in Q.categories:
+ if q[3] == c:
+ y += 50
+ sf.add_text_item("%s (%s)" % (q[1][1], q[2]), (600,y), startgame, (q[3], q[0]), True)
+ i += 1
+
+ #print c
+
+def ask_category(offset_y = 0):
+ Q.load(__SERVICES__.locale.lang_id, __SERVICES__.locale.lang_name)
+
+# sf.clear_text_items()
+
+ i = 1
+ y = 110 + offset_y
+
+ sf.add_text_item("Next Category:", (280,y))
+
+ y += 50
+ sf.add_text_item('All', (300,y), startgame, (0, 0), True)
+
+ for q in Q.categories:
+ #print 'q', q
+ if q[0] == 1:
+ fname = q[1].name
+ sf.add_text_item("%s (%s)" % (fname, q[2]), (300,y + 50 * q[3]), startgame, (q[3], q[0]), True)
+ i += 1
+
+ display_points()
+
+def display_points():
+ format = TextFormat(None, 30)
+ max_q = len(Q.questions)
+
+# sf.display_tooltip("%i" % (Q.count_won), (480, 14), format)
+# sf.display_tooltip("%i" % Q.count_lost, (480, 42), format)
+
+ if max_q == 0:
+ sf.display_line(0, True)
+ sf.display_line(0, False)
+ else:
+ sf.display_line(100 * Q.count_won / max_q, True)
+ sf.display_line(100 * Q.count_lost / max_q, False)
+ pygame.display.update()
+
+def picture_tl():
+ global images
+ if Q.in_question:
+ #print images[0], Q.question.image
+ #print 'tl'
+ if images[0] == Q.question.image:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def picture_tr():
+ global images
+ if Q.in_question:
+ #print 'tr'
+ #print images[1], Q.question.image
+ if images[1] == Q.question.image:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def picture_ll():
+ global images
+ if Q.in_question:
+ #print 'll'
+ #print images[2], Q.question.image
+ if images[2] == Q.question.image:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def picture_lr():
+ global images
+ if Q.in_question:
+ #print 'lr'
+ #print images[3], Q.question.image
+ if images[3] == Q.question.image:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def delete_reacts(response):
+ if response:
+ correct_answer()
+ else:
+ wrong_answer()
+ return True
+
+def getpossibles(teststr):
+ #there may be multiple correct answers separated by a '/'
+ lst = []
+ test = ''
+ for i in range(len(teststr)):
+ if teststr[i] != '/':
+ test = test + teststr[i]
+ else:
+ lst.append(test)
+ test = ''
+ if len(test) > 0:
+ lst.append(test)
+ return lst
+
+def checkanswer(response):
+ possibles = getpossibles(Q.question.answer)
+ return response.strip() in possibles
+
+def self_test():
+ show_answer()
+ #show two clickable items: smiley face, sad face
+ image_right, xy = sf.image_load("images/Emotes/face-grin.png")
+ sf.display_surface(image_right, (750,100))
+ image_wrong, xy = sf.image_load("images/Emotes/face-devil-grin.png")
+ sf.display_surface(image_wrong, (750,150))
+ pygame.display.update()
+
+def play_query_again():
+ global query
+ if Q.in_question:
+ #print 'play_query'
+ query.play()
+ while pygame.mixer.get_busy():
+ clock.tick(30)
+
+def next_question():
+ #print "* Next Question"
+ global images
+ global query
+ global rtr, rtl, rll, rlr
+
+ # Select Category
+ if Q._cat_id == -1:
+ sf.clear_text_items()
+ sf.clear_question_frame(True)
+ ask_category()
+ #print 'ask_category'
+ return 1
+
+ # Pick only locale language's categories
+ if Q.question_pick():
+ sf.clear_text_items()
+ Renyi = False
+ #print 'image = ', Q.question.image
+ Q.in_question = True
+ Q.starttime = time.time()
+ if Q.question.image and len(Q.question.image) > 0:
+ Renyi = True
+ candidate_images = []
+ for question in Q.questions:
+ candidate_images.append(question[11])
+ random.shuffle(candidate_images)
+ #print 'candidate_images', candidate_images
+ if Q.question.image in candidate_images[:4]:
+ images = candidate_images[:4]
+ else:
+ images = candidate_images[:3]
+ images.append(Q.question.image)
+ random.shuffle(images)
+ #print 'images', images
+ #load image
+ imagepath = path(QUIZSOURCE).joinpath('image')
+ #print 'imagepath', imagepath
+ image_tl, xy = sf.image_load(images[0],path = imagepath)
+ if image_tl == None:
+ image_tl, xy = sf.image_load(error.png, path = imagepath)
+ image_tr, xy = sf.image_load(images[1],path = imagepath)
+ if image_tr == None:
+ image_tr, xy = sf.image_load(error.png, path = imagepath)
+ image_ll, xy = sf.image_load(images[2],path = imagepath)
+ if image_ll == None:
+ image_ll, xy = sf.image_load(error.png, imagepath)
+ image_lr, xy = sf.image_load(images[3],path = imagepath)
+ if image_lr == None:
+ image_lr, xy = sf.image_load(error.png, path = imagepath)
+ #display image
+ sf.display_surface(image_tl, (250, 150))
+ sf.display_surface(image_tr, (520,150))
+ sf.display_surface(image_ll, (250,420))
+ sf.display_surface(image_lr, (520, 420))
+ pygame.display.update()
+
+ #print 'sound = ', Q.question.sound
+ soundpath = path(QUIZSOURCE).joinpath('sound')
+ soundlst = getpossibles(soundpath)
+ random.shuffle(soundlst)
+ iconpath = path(ICONSOURCE)
+ if Q.question.sound and len(Q.question.sound) > 0:
+ temp = getpossibles(Q.question.sound)
+ random.shuffle(temp)
+ selected = temp[0]
+ print 'sounds', Q.question.sound, temp, selected
+ query = sf.sound_load(selected[:-4] + '.ogg',
+ path=soundpath)
+ query.play()
+ while pygame.mixer.get_busy():
+ clock.tick(30)
+ #image_play, xy = sf.image_load("images/play.svg")
+
+ image_play, xy = sf.image_load("play_button.png", path = iconpath)
+ sf.display_surface(image_play, (300,30))
+ pygame.display.update()
+
+ if not Renyi:
+ response = __SERVICES__.frontend.ask(Q.question.text)
+ #print 'response=', response
+
+ if len(response) == 0 or len(Q.question.answer) == 0:
+ self_test()
+ else:
+ correct = checkanswer(response)
+ y = 100
+ sf.add_text_item("ok", (750,y), next_question)
+ if correct:
+ correct_answer(False)
+ else:
+ wrong_answer(False)
+
+ pass
+ else:
+ # game / cat finished!
+ Q.in_question = False
+ #print "finished"
+ Q._cat_id = -1
+ finished()
+ pass
+
+def find_card(cardid):
+ for card in Q.deck:
+ if int(card.get('id')) == cardid:
+ return card
+
+def correct_answer(next=True):
+ correct_sounds = ['i-like-it.wav', 'ooh-yeah.wav', 'impressive.wav', 'dramatic_chord.wav', 'sweet.wav',
+ 'brain-yes.wav', 'mk-excellent.wav', 'that-was-cool.wav', 'bt-excellent.wav', 'groovy.wav',
+ 'yes-i-like-it.wav', 'burns-excellent.wav', 'oh-yeah.wav']
+ #print 'correct answer next =', next, Q.in_question
+ if Q.in_question:
+ A.display_icon("found")
+ #A.play_sound(1)
+ Q.in_question = False
+ Q.count_won += 1
+ display_points()
+ good = sf.sound_load(random.choice(correct_sounds))
+ good.play()
+ while pygame.mixer.get_busy():
+ clock.tick(30)
+ #update Leitner attributes
+ think = '%0.2f' % float( time.time() - Q.starttime)
+ print 'think',think
+ card = find_card(Q.question.id)
+ card.attrib['think'] = think
+ card.attrib['shown'] = Q.shown
+ count = int(card.attrib['count'])
+ count +=1
+ card.attrib['count'] = str(count)
+ bin = int(card.attrib['bin'])
+ bin += 1
+ card.attrib['bin'] = str(bin)
+ if next:
+ next_question()
+ else:
+ show_answer()
+
+def wrong_answer(next=True):
+ wrong_sounds = ['db_forgetaboutit.wav', 'alf_wrong.wav', 'doh.wav', 'sorry.wav', 'awh_man.wav', 'metal_clang_2.wav',
+ 'excuse_me.wav', 'negative.wav', 'bunny_awful.wav', 'gwarsh.wav', 'not.wav', 'haha.wav', 'oh_no.wav',
+ 'compute.wav', 'hoo-ah.wav']
+ #print 'wrong answer next =', next, Q.in_question
+ if Q.in_question:
+ #update Leitner attributes
+ card = find_card(Q.question.id)
+ card.attrib['think'] = \
+ '%0.2f' % float(time.time() - Q.starttime)
+ card.attrib['shown'] = Q.shown
+ card.attrib['bin'] = 1
+ count = int(card.attrib['count'])
+ count += 1
+ card.attrib['count'] = str(count)
+ A.display_icon("not-found")
+ #A.play_sound(0)
+ Q.in_question = False
+ Q.count_lost += 1
+ display_points()
+ bad = sf.sound_load(random.choice(wrong_sounds))
+ bad.play()
+ while pygame.mixer.get_busy():
+ clock.tick(30)
+ if next:
+ next_question()
+ else:
+ show_answer()
+
+def click_on_flashcards():
+ global selectedlanguage
+ #print 'flashcards selected'
+ selectedlanguage = 'flashcards'
+ Q.cat_id = -1
+ next_question()
+def load():
+ global sf
+ sf = __SERVICES__.frontend;
+
+ global Q
+ Q = Questions()
+
+ global A
+ A = Answer()
+
+ global clock
+ clock = pygame.time.Clock()
+
+ global rtl, rtr, rll, rlr, knew_it, forgot_it, play_again
+ rtl = sf.add_react(50, 100, 320, 240, picture_tl)
+ #print 'rtl=', rtl
+ rtr = sf.add_react(400, 100, 320, 240, picture_tr)
+ #print 'rtr=', rtr
+ rll = sf.add_react(50, 360, 320, 240, picture_ll)
+ #print 'rll=', rll
+ rlr = sf.add_react(400, 360, 320, 240, picture_lr)
+ #print 'rlr=', rlr
+ knew_it = sf.add_react(750,100,50,50, correct_answer)
+ #print 'knew_it=', knew_it
+ forgot_it = sf.add_react(750,150,50,50, wrong_answer)
+ #print 'forgot_it=', forgot_it
+ play_again = sf.add_react(300,0,100,100, play_query_again)
+ #print 'play_again=', play_again
+
+ sf.add_menu_item('/', 'Flashcards', click_on_flashcards)
+
+def close():
+ pass