app/soc/logic/models/base.py
changeset 481 94834a1e6c01
parent 459 2cfcedaf7c16
child 487 1a7591ff0051
equal deleted inserted replaced
480:9b07ddeb1412 481:94834a1e6c01
    43 
    43 
    44   The BaseLogic class functions specific to Entity classes by relying
    44   The BaseLogic class functions specific to Entity classes by relying
    45   on the the child-classes to implement _model, _name and _key_name
    45   on the the child-classes to implement _model, _name and _key_name
    46   """
    46   """
    47 
    47 
       
    48   def __init__(self, model, base_model=None,
       
    49                name=None, skip_properties=None):
       
    50     """Defines the name, key_name and model for this entity.
       
    51     """
       
    52     self._model = model
       
    53     self._base_model = base_model
       
    54     
       
    55     if name:
       
    56       self._name = name
       
    57     else:
       
    58       self._name = self.getModelClassName()
       
    59     
       
    60     if skip_properties:
       
    61       self._skip_properties = skip_properties
       
    62     else:
       
    63       self._skip_properties = []
       
    64 
    48   def _updateField(self, model, name, value):
    65   def _updateField(self, model, name, value):
    49     """Hook called when a field is updated.
    66     """Hook called when a field is updated.
    50 
    67 
    51     Base classes should override if any special actions need to be
    68     Base classes should override if any special actions need to be
    52     taken when a field is updated. The field is not updated if the
    69     taken when a field is updated. The field is not updated if the
    82     for key_field_name in key_field_names:
    99     for key_field_name in key_field_names:
    83       keyvalues.append(kwargs[key_field_name])
   100       keyvalues.append(kwargs[key_field_name])
    84 
   101 
    85     # construct the KeyName in the appropriate format
   102     # construct the KeyName in the appropriate format
    86     return ":".join([self._name] + keyvalues)
   103     return ":".join([self._name] + keyvalues)
       
   104 
       
   105   def getModelClassName(self):
       
   106     """Returns model class name string.
       
   107     """ 
       
   108     return self._model.__name__ 
       
   109 
       
   110   def getFullModelClassName(self):
       
   111     """Returns fully-qualified model module.class name string.
       
   112     """ 
       
   113     return '%s.%s' % (self._model.__module__,
       
   114                       self.getModelClassName()) 
    87 
   115 
    88   def getKeyValues(self, entity):
   116   def getKeyValues(self, entity):
    89     """Exctracts the key values from entity and returns them
   117     """Exctracts the key values from entity and returns them
    90 
   118 
    91     Args:
   119     Args:
   264     if unique:
   292     if unique:
   265       return query.get()
   293       return query.get()
   266 
   294 
   267     return query.fetch(limit, offset)
   295     return query.fetch(limit, offset)
   268 
   296 
       
   297   def buildTypedQueryString(self):
       
   298     """Returns a GQL query string compatible with PolyModel.
       
   299     """
       
   300     return ''.join(self._buildTypedQueryParts())
       
   301 
       
   302   def _buildTypedQueryParts(self):
       
   303     if self._base_model:
       
   304       return [
       
   305           'SELECT * FROM ', self._base_model.__name__,
       
   306           " WHERE inheritance_line = '", self.getFullModelClassName(), "'"]
       
   307     
       
   308     return ['SELECT * FROM ', self._model.__name__]
       
   309 
       
   310   def buildOrderedQueryString(self, order_by=None):
       
   311     """Returns a an ordered GQL query string compatible with PolyModel.
       
   312   
       
   313     Args:
       
   314       order_by: optional field name by which to order the query results;
       
   315         default is None, in which case no ORDER BY clause is placed in
       
   316         the query string
       
   317     """
       
   318     return ''.join(self._buildOrderedQueryParts(order_by=order_by))
       
   319 
       
   320   def _buildOrderedQueryParts(self, order_by=None):
       
   321     query_str_parts = self._buildTypedQueryParts()
       
   322 
       
   323     if order_by:
       
   324       query_str_parts.extend([' ORDER BY ', order_by])
       
   325 
       
   326     return query_str_parts
       
   327 
       
   328   def getEntitiesForLimitAndOffset(self, limit, offset=0, order_by=None):
       
   329     """Returns entities for given offset and limit or None if not found.
       
   330     
       
   331     Args:
       
   332       limit: max amount of entities to return
       
   333       offset: optional offset in entities list which defines first entity to
       
   334         return; default is zero (first entity)
       
   335     """
       
   336     query_string = self.buildOrderedQueryString(order_by=order_by)
       
   337     query = db.GqlQuery(query_string)
       
   338 
       
   339     return query.fetch(limit, offset)
       
   340 
       
   341   def getNearestEntities(self, fields_to_try):
       
   342     """Get entities just before and just after the described entity.
       
   343     
       
   344     Args:
       
   345       fields_to_try: ordered list of key/value pairs that "describe"
       
   346         the desired entity (which may not necessarily exist), where key is
       
   347         the name of the field, and value is an instance of that field
       
   348         used in the comparison; if value is None, that field is skipped
       
   349 
       
   350     Returns:
       
   351       a two-tuple: ([nearest_entities], 'field_name')
       
   352     
       
   353       nearest_entities: list of entities being those just before and just
       
   354         after the (possibly non-existent) entity identified by the first
       
   355         of the supplied (non-None) fields
       
   356           OR
       
   357         possibly None if query had no results for the supplied field
       
   358         that was used.
       
   359     """
       
   360     # SELECT * FROM base_class WHERE inheritance_line = 'derived_class'
       
   361     typed_query_parts = self._buildTypedQueryParts()
       
   362   
       
   363     if self._base_model:
       
   364       typed_query_parts.append(' AND %s > :1')
       
   365     else:
       
   366       typed_query_parts.append(' WHERE %s > :1')
       
   367 
       
   368     typed_query_fmt = ''.join(typed_query_parts)
       
   369 
       
   370     for field, value in fields_to_try:
       
   371       if value is None:
       
   372         # skip this not-supplied field
       
   373         continue
       
   374 
       
   375       query = db.GqlQuery(typed_query_fmt % field, value)
       
   376       return query.fetch(1), field
       
   377 
       
   378     # all fields exhausted, and all had None values
       
   379     return (None, None)
       
   380 
       
   381   def findNearestEntitiesOffset(width, fields_to_try):
       
   382     """Finds offset of beginning of a range of entities around the nearest.
       
   383   
       
   384     Args:
       
   385       width: the width of the "found" window around the nearest User found
       
   386       fields_to_try:  see getNearestEntities()
       
   387     
       
   388     Returns:
       
   389       an offset into the list of entities that is width/2 less than the
       
   390       offset of the first entity returned by getNearestEntities(), or zero
       
   391       if that offset would be less than zero
       
   392         OR
       
   393       None if there are no nearest entities or the offset of the beginning of
       
   394       the range cannot be found for some reason 
       
   395     """
       
   396     # find entity "nearest" to supplied fields
       
   397     nearest_entities, field = self.getNearestEntities(fields_to_try)
       
   398   
       
   399     if not nearest_entities:
       
   400       # no "nearest" entity, so indicate that with None
       
   401       return None
       
   402 
       
   403     nearest_entity = nearest_entities[0]
       
   404 
       
   405     # start search for beginning of nearest Users range at offset zero
       
   406     offset = 0
       
   407     entities = self.getEntitiesForLimitAndOffset(width, offset=offset)
       
   408   
       
   409     while True:
       
   410       for entity in entities:
       
   411         if getattr(nearest_entity, field) == getattr(entity, field):
       
   412           # nearest User found in current search range, so return a range start
       
   413           return max(0, (offset - (width/2)))
       
   414 
       
   415         offset = offset + 1
       
   416 
       
   417       # nearest User was not in the current search range, so fetch the next set
       
   418       entities = self.getEntitiesForLimitAndOffset(width, offset=offset)
       
   419 
       
   420       if not entities:
       
   421         # nearest User never found, so indicate that with None
       
   422         break
       
   423 
       
   424     return None
       
   425 
   269   def updateModelProperties(self, model, model_properties):
   426   def updateModelProperties(self, model, model_properties):
   270     """Update existing model entity using supplied model properties.
   427     """Update existing model entity using supplied model properties.
   271 
   428 
   272     Args:
   429     Args:
   273       model: a model entity
   430       model: a model entity