Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/model.py
diff options
context:
space:
mode:
Diffstat (limited to 'model.py')
-rw-r--r--model.py381
1 files changed, 381 insertions, 0 deletions
diff --git a/model.py b/model.py
new file mode 100644
index 0000000..187100e
--- /dev/null
+++ b/model.py
@@ -0,0 +1,381 @@
+# Copyright (C) 2006, 2007, 2008 One Laptop Per Child
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+import libxml2
+from os import environ, makedirs, chmod
+from os.path import join, basename, isdir, split, normpath
+import logging
+import random
+import gobject
+import zipfile
+import tempfile
+
+from sugar import profile
+from sugar.datastore import datastore
+from sugar.activity.activity import get_bundle_path, get_activity_root
+
+_logger = logging.getLogger('model')
+
+class Pair(gobject.GObject):
+ __gproperties__ = {
+ 'aimg' : (str, None, None, None, gobject.PARAM_READWRITE),
+ 'asnd' : (str, None, None, None, gobject.PARAM_READWRITE),
+ 'achar': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'bimg' : (str, None, None, None, gobject.PARAM_READWRITE),
+ 'bsnd' : (str, None, None, None, gobject.PARAM_READWRITE),
+ 'bchar': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'aspeak': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'bspeak': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'asensor': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'bsensor': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'color' : (gobject.TYPE_INT, 'Base', 'Base', 0, 10, 0, \
+ gobject.PARAM_READWRITE)
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self._properties = {'aimg':None, 'asnd':None, 'achar':None, 'bimg':None,
+ 'bsnd':None, 'bchar':None, 'color':100,
+ 'aspeak':None, 'bspeak':None, 'asensor':None, 'bsensor':None}
+
+ def do_get_property(self, pspec):
+ """Retrieve a particular property from our property dictionary
+ """
+ if pspec.name == "aimg":
+ return self._properties["aimg"]
+ elif pspec.name == "asnd":
+ return self._properties["asnd"]
+ elif pspec.name == "achar":
+ return self._properties["achar"]
+ elif pspec.name == "bimg":
+ return self._properties["bimg"]
+ elif pspec.name == "bsnd":
+ return self._properties["bsnd"]
+ elif pspec.name == "bchar":
+ return self._properties["bchar"]
+ elif pspec.name == "color":
+ return self._properties["color"]
+ elif pspec.name == "aspeak":
+ return self._properties["aspeak"]
+ elif pspec.name == "bspeak":
+ return self._properties["bspeak"]
+ elif pspec.name == "asensor":
+ return self._properties["asensor"]
+ elif pspec.name == "bsensor":
+ return self._properties["bsensor"]
+
+ def set_property(self, name, value):
+ if name == 'aimg':
+ self._properties['aimg'] = value
+ elif name == "asnd":
+ self._properties["asnd"] = value
+ elif name == "achar":
+ self._properties["achar"] = value
+ elif name == "bimg":
+ self._properties["bimg"] = value
+ elif name == "bsnd":
+ self._properties["bsnd"] = value
+ elif name == "bchar":
+ self._properties["bchar"] = value
+ elif name == "color":
+ self._properties["color"] = value
+ elif name == "aspeak":
+ self._properties["aspeak"] = value
+ elif name == "bspeak":
+ self._properties["bspeak"] = value
+ elif name == "asensor":
+ self._properties["asensor"] = value
+ elif name == "bsensor":
+ self._properties["bsensor"] = value
+
+
+class Model(object):
+ ''' The model of the activity. Contains methods to read and write
+ the configuration for a game from xml. Stores the pairs and grid
+ information.
+ '''
+
+ def __init__(self, game_path=None):
+ self.data = {}
+
+ if game_path is None:
+ game_path = get_activity_root()
+
+ if isdir(game_path):
+ self.game_path = game_path
+ else:
+ _logger.error('Game_path not found in %s' % game_path)
+ return
+
+ self.data['face'] = ''
+ self.data['align'] = '1'
+
+ try:
+ self.dtd = libxml2.parseDTD(None, join(get_bundle_path(),
+ 'memorize.dtd'))
+ except libxml2.parserError, e:
+ _logger.error('Init: no memorize.dtd found ' +str(e))
+ self.dtd = None
+ self.ctxt = libxml2.newValidCtxt()
+
+ self.pairs = {}
+ self.grid = []
+
+ # used by the leader of the game to keep track of the game state
+ self.players = {}
+ self.player_active = 0
+ self.selected = 0
+ self.turn = 0
+ self.started = 0
+ self.count = 0
+
+ def read(self, game_file):
+ tmp_root = join(environ['SUGAR_ACTIVITY_ROOT'], 'instance')
+ temp_folder = tempfile.mkdtemp(dir=tmp_root)
+ chmod(temp_folder, 0777)
+ self.data['key'] = basename(game_file)
+ self.data['game_file'] = game_file
+ self.data['path'] = temp_folder
+ self.data['pathimg'] = join(self.data['path'], 'images')
+ self.data['pathsnd'] = join(self.data['path'], 'sounds')
+
+ ''' extracts files in the zip file '''
+ game_name = basename(game_file)[:-4]
+ zipFile = zipfile.ZipFile(game_file, "r")
+ for each in zipFile.namelist():
+ if not each.endswith('/'):
+ root, name = split(each)
+ directory = normpath(join(self.data['path'], root))
+ if not isdir(directory):
+ makedirs(directory)
+ file(join(directory, name), 'wb').write(zipFile.read(each))
+
+ self.pairs = {}
+
+ ''' reads the configuration from an xml file '''
+ try:
+ xml_file = join(environ['SUGAR_ACTIVITY_ROOT'],
+ self.data['path'], 'game.xml')
+ doc = libxml2.parseFile(xml_file)
+ if doc.validateDtd(self.ctxt, self.dtd):
+
+ # get the requested nodes
+ xpa = doc.xpathNewContext()
+ res = xpa.xpathEval("//*")
+
+ # write their content to the data structure
+ idpair = 0
+ for elem in res:
+ attributes = elem.get_properties()
+ pair = Pair()
+ if(elem.name == 'pair'):
+ for attribute in attributes:
+ if(attribute.name == 'text'):
+ pass
+ else:
+ pair.set_property(attribute.name,
+ attribute.content)
+ self.pairs[str(idpair)] = pair
+ idpair += 1
+ elif(elem.name == 'memorize'):
+ for attribute in attributes:
+ if(attribute.name == 'text'):
+ pass
+ elif(attribute.name == 'name'):
+ self.data['name'] = attribute.content
+ elif(attribute.name == 'scoresnd'):
+ self.data['scoresnd'] = attribute.content
+ elif(attribute.name == 'winsnd'):
+ self.data['winsnd'] = attribute.content
+ elif(attribute.name == 'divided'):
+ self.data['divided'] = attribute.content
+ elif(attribute.name == 'face'):
+ self.data['face'] = attribute.content
+ elif(attribute.name == 'face1'):
+ self.data['face1'] = attribute.content
+ elif(attribute.name == 'face2'):
+ self.data['face2'] = attribute.content
+ elif(attribute.name == 'align'):
+ self.data['align'] = attribute.content
+ elif(attribute.name == 'equal_pairs'):
+ self.data['equal_pairs'] = attribute.content
+ xpa.xpathFreeContext()
+ else:
+ _logger.error('Read: Error in validation of the file')
+ doc.freeDoc()
+ return 1
+ doc.freeDoc()
+ return 0
+ except libxml2.parserError, e:
+ _logger.error('Read: Error parsing file ' +str(e))
+ return 2
+
+ def write(self, equal_pairs, divided):
+ ''' writes the configuration to an xml file '''
+ doc = libxml2.newDoc("1.0")
+ root = doc.newChild(None, "memorize", None)
+
+ if(self.data.get('name', None) != None):
+ root.setProp("name", self.data['name'])
+
+ if divided:
+ root.setProp('divided', '1')
+ root.setProp('face1', '1')
+ root.setProp('face2', '2')
+ else:
+ root.setProp('divided', '0')
+
+ if equal_pairs:
+ root.setProp('equal_pairs', str(equal_pairs))
+
+ if(self.data.get('scoresnd', None) != None):
+ root.setProp("scoresnd", self.data['scoresnd'])
+ if(self.data.get('winsnd', None) != None):
+ root.setProp("winsnd", self.data['winsnd'])
+ if(self.data.get('divided', None) != None):
+ root.setProp("divided", self.data['divided'])
+ if(self.data.get('face', None) != None):
+ root.setProp("face", self.data['face'])
+ if(self.data.get('face1', None) != None):
+ root.setProp("face1", self.data['face1'])
+ if(self.data.get('face2', None) != None):
+ root.setProp("face2", self.data['face2'])
+ if(self.data.get('align', None) != None):
+ root.setProp("align", self.data['align'])
+
+ for key in self.pairs:
+ elem = root.newChild(None, "pair", None)
+ if self.pairs[key].props.aimg != None:
+ elem.setProp("aimg", self.pairs[key].props.aimg)
+ if self.pairs[key].props.asnd != None:
+ elem.setProp("asnd", self.pairs[key].props.asnd)
+ if self.pairs[key].props.achar != None:
+ elem.setProp("achar", self.pairs[key].props.achar)
+ if self.pairs[key].props.bimg != None:
+ elem.setProp("bimg", self.pairs[key].props.bimg)
+ if self.pairs[key].props.bsnd != None:
+ elem.setProp("bsnd", self.pairs[key].props.bsnd)
+ if self.pairs[key].props.bchar != None:
+ elem.setProp("bchar", self.pairs[key].props.bchar)
+ if self.pairs[key].props.aspeak != None:
+ elem.setProp("aspeak", self.pairs[key].props.aspeak)
+ if self.pairs[key].props.bspeak != None:
+ elem.setProp("bspeak", self.pairs[key].props.bspeak)
+ if self.pairs[key].props.asensor != None:
+ elem.setProp("asensor", self.pairs[key].props.asensor)
+ if self.pairs[key].props.bsensor != None:
+ elem.setProp("bsensor", self.pairs[key].props.bsensor)
+ # elem.setProp("color", str(self.pairs[key].props.color))
+
+ if doc.validateDtd(self.ctxt, self.dtd):
+ doc.saveFormatFile(join(self.game_path, 'game.xml'), 1)
+ else:
+ _logger.error('Write: Error in validation of the file')
+ doc.freeDoc()
+ return 2
+ doc.freeDoc()
+ return 0
+
+
+ def def_grid(self, size):
+ ''' create the grid for the play from the pairs information
+ and shuffles the grid so they always appear in a different
+ place
+ '''
+ psize = (size * size / 2)
+ _logger.debug('Size requested: %d', psize)
+ self.grid = []
+ temp1 = []
+ temp2 = []
+ i = 0
+
+ # shuffle the pairs first to avoid only taking the first ones
+ # when there are more pairs in the config file then the grid is using
+ keys = self.pairs.keys()
+ random.shuffle(keys)
+
+ for key in keys:
+ if i < psize:
+ elem = {}
+ elem['pairkey'] = key
+ elem['state'] = '0'
+ elem['ab'] = 'a'
+ if self.pairs[key].props.aimg != None:
+ elem['img'] = self.pairs[key].props.aimg
+ if self.pairs[key].props.asnd != None:
+ elem['snd'] = self.pairs[key].props.asnd
+ if self.pairs[key].props.achar != None:
+ elem['char'] = self.pairs[key].props.achar
+ if self.pairs[key].props.aspeak != None:
+ elem['speak'] = self.pairs[key].props.aspeak
+ if self.pairs[key].props.asensor != None:
+ elem['sensor'] = self.pairs[key].props.asensor
+ temp1.append(elem)
+
+ elem = {}
+ elem['pairkey'] = key
+ elem['state'] = '0'
+ elem['ab'] = 'b'
+ if self.pairs[key].props.bimg != None:
+ elem['img'] = self.pairs[key].props.bimg
+ if self.pairs[key].props.bsnd != None:
+ elem['snd'] = self.pairs[key].props.bsnd
+ if self.pairs[key].props.bchar != None:
+ elem['char'] = self.pairs[key].props.bchar
+ if self.pairs[key].props.bspeak != None:
+ elem['speak'] = self.pairs[key].props.bspeak
+ if self.pairs[key].props.bsensor != None:
+ elem['sensor'] = self.pairs[key].props.bsensor
+ temp2.append(elem)
+ i += 1
+ else:
+ break
+
+ numpairs = len(self.pairs)
+ if numpairs < psize:
+ _logger.debug('Defgrid: Not enough pairs, requested=%s had=%s'
+ % (psize, numpairs))
+ self.data['size'] = str(size)
+
+ if self.data['divided'] == '1':
+ random.shuffle(temp1)
+ random.shuffle(temp2)
+ temp1.extend(temp2)
+ else:
+ temp1.extend(temp2)
+ random.shuffle(temp1)
+ self.grid = temp1
+ _logger.debug('Defgrid: grid( size=%s ): %s'
+ % (self.data['size'], self.grid))
+ _logger.debug('Defgrid: data: %s', self.data)
+
+ def set_data_grid(self, data, grid):
+ self.data = data
+ self.grid = grid
+
+ def save_byte_array(self, path, title= None, color= None):
+ if color == None:
+ color = profile.get_color().to_string()
+ _logger.debug('Save new game in datastore')
+
+ # Saves the zip in datastore
+ gameObject = datastore.create()
+ gameObject.metadata['title'] = title
+ gameObject.metadata['mime_type'] = 'application/x-memorize-project'
+ gameObject.metadata['icon-color'] = color
+ gameObject.file_path = path
+ datastore.write(gameObject)