--- a/app/soc/logic/models/base.py Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/base.py Sat Nov 15 03:12:33 2008 +0000
@@ -45,6 +45,23 @@
on the the child-classes to implement _model, _name and _key_name
"""
+ def __init__(self, model, base_model=None,
+ name=None, skip_properties=None):
+ """Defines the name, key_name and model for this entity.
+ """
+ self._model = model
+ self._base_model = base_model
+
+ if name:
+ self._name = name
+ else:
+ self._name = self.getModelClassName()
+
+ if skip_properties:
+ self._skip_properties = skip_properties
+ else:
+ self._skip_properties = []
+
def _updateField(self, model, name, value):
"""Hook called when a field is updated.
@@ -85,6 +102,17 @@
# construct the KeyName in the appropriate format
return ":".join([self._name] + keyvalues)
+ def getModelClassName(self):
+ """Returns model class name string.
+ """
+ return self._model.__name__
+
+ def getFullModelClassName(self):
+ """Returns fully-qualified model module.class name string.
+ """
+ return '%s.%s' % (self._model.__module__,
+ self.getModelClassName())
+
def getKeyValues(self, entity):
"""Exctracts the key values from entity and returns them
@@ -266,6 +294,135 @@
return query.fetch(limit, offset)
+ def buildTypedQueryString(self):
+ """Returns a GQL query string compatible with PolyModel.
+ """
+ return ''.join(self._buildTypedQueryParts())
+
+ def _buildTypedQueryParts(self):
+ if self._base_model:
+ return [
+ 'SELECT * FROM ', self._base_model.__name__,
+ " WHERE inheritance_line = '", self.getFullModelClassName(), "'"]
+
+ return ['SELECT * FROM ', self._model.__name__]
+
+ def buildOrderedQueryString(self, order_by=None):
+ """Returns a an ordered GQL query string compatible with PolyModel.
+
+ Args:
+ order_by: optional field name by which to order the query results;
+ default is None, in which case no ORDER BY clause is placed in
+ the query string
+ """
+ return ''.join(self._buildOrderedQueryParts(order_by=order_by))
+
+ def _buildOrderedQueryParts(self, order_by=None):
+ query_str_parts = self._buildTypedQueryParts()
+
+ if order_by:
+ query_str_parts.extend([' ORDER BY ', order_by])
+
+ return query_str_parts
+
+ def getEntitiesForLimitAndOffset(self, limit, offset=0, order_by=None):
+ """Returns entities for given offset and limit or None if not found.
+
+ Args:
+ limit: max amount of entities to return
+ offset: optional offset in entities list which defines first entity to
+ return; default is zero (first entity)
+ """
+ query_string = self.buildOrderedQueryString(order_by=order_by)
+ query = db.GqlQuery(query_string)
+
+ return query.fetch(limit, offset)
+
+ def getNearestEntities(self, fields_to_try):
+ """Get entities just before and just after the described entity.
+
+ Args:
+ fields_to_try: ordered list of key/value pairs that "describe"
+ the desired entity (which may not necessarily exist), where key is
+ the name of the field, and value is an instance of that field
+ used in the comparison; if value is None, that field is skipped
+
+ Returns:
+ a two-tuple: ([nearest_entities], 'field_name')
+
+ nearest_entities: list of entities being those just before and just
+ after the (possibly non-existent) entity identified by the first
+ of the supplied (non-None) fields
+ OR
+ possibly None if query had no results for the supplied field
+ that was used.
+ """
+ # SELECT * FROM base_class WHERE inheritance_line = 'derived_class'
+ typed_query_parts = self._buildTypedQueryParts()
+
+ if self._base_model:
+ typed_query_parts.append(' AND %s > :1')
+ else:
+ typed_query_parts.append(' WHERE %s > :1')
+
+ typed_query_fmt = ''.join(typed_query_parts)
+
+ for field, value in fields_to_try:
+ if value is None:
+ # skip this not-supplied field
+ continue
+
+ query = db.GqlQuery(typed_query_fmt % field, value)
+ return query.fetch(1), field
+
+ # all fields exhausted, and all had None values
+ return (None, None)
+
+ def findNearestEntitiesOffset(width, fields_to_try):
+ """Finds offset of beginning of a range of entities around the nearest.
+
+ Args:
+ width: the width of the "found" window around the nearest User found
+ fields_to_try: see getNearestEntities()
+
+ Returns:
+ an offset into the list of entities that is width/2 less than the
+ offset of the first entity returned by getNearestEntities(), or zero
+ if that offset would be less than zero
+ OR
+ None if there are no nearest entities or the offset of the beginning of
+ the range cannot be found for some reason
+ """
+ # find entity "nearest" to supplied fields
+ nearest_entities, field = self.getNearestEntities(fields_to_try)
+
+ if not nearest_entities:
+ # no "nearest" entity, so indicate that with None
+ return None
+
+ nearest_entity = nearest_entities[0]
+
+ # start search for beginning of nearest Users range at offset zero
+ offset = 0
+ entities = self.getEntitiesForLimitAndOffset(width, offset=offset)
+
+ while True:
+ for entity in entities:
+ if getattr(nearest_entity, field) == getattr(entity, field):
+ # nearest User found in current search range, so return a range start
+ return max(0, (offset - (width/2)))
+
+ offset = offset + 1
+
+ # nearest User was not in the current search range, so fetch the next set
+ entities = self.getEntitiesForLimitAndOffset(width, offset=offset)
+
+ if not entities:
+ # nearest User never found, so indicate that with None
+ break
+
+ return None
+
def updateModelProperties(self, model, model_properties):
"""Update existing model entity using supplied model properties.