Package hm :: Package db :: Module log
[hide private]
[frames] | no frames]

Source Code for Module hm.db.log

  1  from django.db import models  
  2   
  3  from django.contrib.contenttypes.models import ContentType 
  4  from django.contrib.contenttypes import generic 
  5  from django.dispatch import dispatcher 
  6   
  7  import graph, citation, user, tag, rating 
  8  from user import User 
  9  from utils import current_user, cached_property, mixin_property, mixin_method, mixin 
 10   
 11  from lib.threadlocals_middleware import get_current_request 
 12   
 13  """ 
 14  @summary: 
 15  The L{log} module includes several classes tasked with tracking use 
 16  of and changes to the database. 
 17   
 18  @author: Daniel Ring 
 19  @organization: ThoughtAndMemory.org 
 20  @copyright: ThoughtAndMemoyr.org 
 21  @contact: lucy@thoughtandmemory.org, dfring@gmail.com 
 22   
 23  @version: 0.22a 
 24  """ 
 25   
 26   
27 -class Visit(models.Model):
28 """ 29 @summary: 30 A B{Visit} is a record of a single HTTP request. One is recorded 31 everytime Django serves up a page, so there will be lots of these. 32 When the site is public, we'll have to trim this down or regularly 33 flush the database, but for as long as we have the storage space, 34 this will help us evaluate traffic and analyze how users are moving 35 through the site. 36 37 @ivar path: The path (not including domain) for this page request. 38 @ivar user: The user, if logged in. 39 @ivar time: The time at the start of the request. 40 @ivar duration: The amount of time it took to process the request. 41 @ivar sql_duration: The cumulative time spent querying the database 42 during this request. 43 @ivar ip: The visitor's IP address. 44 @ivar remote_host: The HTTP_REMOTE_HOST header passed by the visitor. 45 @ivar user_agent: The HTTP_USER_AGENT header passed by the visitor. 46 This should describe their browser. 47 @ivar referer: The HTTP_REFERER header passed by the visitor. This 48 should be the page from which they linked to this one. 49 """ 50 path = models.CharField(max_length=255) 51 52 user = models.ForeignKey(User) 53 time = models.DateTimeField(auto_now_add=True) 54 duration = models.FloatField() 55 sql_duration = models.FloatField() 56 57 ip = models.IPAddressField() 58 remote_host = models.CharField(max_length=255) 59 user_agent = models.CharField(max_length=255) 60 referer = models.CharField(max_length=255) 61 62 @classmethod
63 - def make(klass, path, user, time, duration, sql_duration, ip, remote_host, user_agent, referer):
73
74 - class Admin:
75 pass
76 77
78 -class Action(models.Model):
79 """ 80 @summary: 81 An B{Action} is a record of a database-changing event. All database 82 changes are logged, including creation of: 83 1. Node 84 2. UPC 85 3. Edge 86 4. Dimension 87 5. Behavior 88 6. Argument 89 7. ArgumentLink 90 8. Citation 91 9. Source 92 10. Article 93 11. Author 94 12. Tag 95 13. Tagging 96 The information stored when an action is logged includes: 97 1. User 98 2. IP 99 3. Time 100 4. Item - the affected database row 101 102 @ivar user: The User associated with this activity. 103 @type user: User 104 @ivar ip: The IP address used when the activity occurred. 105 @type ip: string 106 @ivar time: The time the activity occurred. 107 @type time: datetime 108 @ivar item: This is a polymorphic link to the affected item. 109 @type item: Any 110 @ivar item_type: This is an index into the django_content_types 111 table, which stores a row for each model in the application. 112 @type item_type: int (an id in the ContentTypes table) 113 @ivar item_id: This is the primary key of the affected item. 114 @type item_id: int 115 """ 116 117 ACTING_MODELS = [ 118 graph.Node, graph.NodeType, graph.NodeTypeCast, 119 graph.Edge, graph.EdgeType, graph.EdgeTypeCast, 120 graph.Argument, graph.Dimension, graph.Behavior, graph.UPC, 121 citation.Source, citation.Author, citation.Article, citation.Citation, 122 tag.Tag, tag.Tagging, 123 rating.Rating, 124 user.UserDimensionWeighting] 125 126 @classmethod
127 - def Initialize(klass):
128 """ 129 @summary: B{_apply} takes a list of models and to each model it: 130 1. adds B{ActionTarget} as a base class (in I{__bases__}) to 131 provide a common interface to the models; 132 2. assigns an instance of ActionTargetManager to I{actions} 133 which limits the scope to within a single model 134 3. registers listeners to call methods in L{Log} regarding events 135 on subclassed models; specifically: 136 1. L{Log.log_save} is called before a row is saved 137 2. L{Log.log_create} is called after a row is created 138 3. L{Log.log_delete} is called before a row is deleted 139 (this should not happen.) 140 141 @return: None 142 @rtype: NoneType 143 """ 144 mixin(ActionMixin, Action.ACTING_MODELS) 145 146 for model_class in Action.ACTING_MODELS: 147 #dispatcher.connect(Action.on_save, signal=models.signals.pre_save, sender=model_class) 148 dispatcher.connect(Action.on_create, signal=models.signals.post_save, sender=model_class)
149 #dispatcher.connect(Action.on_delete, signal=models.signals.pre_delete, sender=model_class) 150 #dispatcher.connect(Action.on_post_delete, signal=signals.post_save, sender=model_class) 151 152 153 #These are the specified types of actions 154 CREATE = 1 155 156 157 type = models.PositiveIntegerField(default=0) 158 label = models.CharField(max_length=255) 159 description = models.TextField() 160 161 user = models.ForeignKey(User) 162 ip = models.IPAddressField() 163 time = models.DateTimeField(auto_now_add=True) 164 165 item_type = models.ForeignKey(ContentType) 166 item_id = models.PositiveIntegerField() 167 item = generic.GenericForeignKey('item_type', 'item_id') 168 169 @classmethod
170 - def on_save(klass, signal, sender, instance):
171 """ 172 @summary: 173 This method called anytime an object is about to be saved. This is where 174 we can check the I{instance.original_values} dict to determine whether 175 the object has been changed. 176 177 From here, we can raise an exception to stop the object from being saved. 178 This is currently not done, but will eventually check for the logged-in 179 status of the user. 180 181 @return: None 182 @rtype: NoneType 183 """ 184 #if get_current_request().user is None: 185 # raise NotLoggedIn("You must log in before contributing.") 186 pass
187 188 @classmethod
189 - def on_create(klass, signal, sender, instance, created):
190 """ 191 @summary: 192 This method is called every time an object has been saved. This is where 193 L{Action} objects are created for new objects. 194 195 @return: None 196 @rtype: NoneType 197 """ 198 if created: 199 label = "Added: %s, id=%s, str=%s" % (instance.__class__.__name__, instance.id, unicode(instance)) 200 a = Action.add(type=Action.CREATE, item=instance, label=label, description="Hooray!") 201 #print repr(a) 202 return
203 204 @classmethod
205 - def on_delete(klass, signal, sender, instance):
206 """ 207 @summary: 208 This method is called every time an object is deleted. This should not 209 happen with the current database setup. 210 211 @return: None 212 @rtype: NoneType 213 """ 214 pass
215 216 @classmethod
217 - def by_user(klass, user):
218 """ 219 @summary: Returns a QuerySet containing all Actions for which 220 the specific User is responsible. 221 @rtype: QuerySet 222 """ 223 return Action.objects.filter(user=user)
224 225 @classmethod
226 - def make(klass, item, type=1, label="<no label>", description="", ip="", user=None):
227 """ 228 @summary: Instantiates, but does not save, a new Action object. 229 230 @param item: The object this action concerns. 231 @type item: Any 232 @param type: The type of Action. See the list of types in L{Action}. 233 @type type: int 234 @param label: A brief description of the activity being recorded. 235 @type label: string 236 @param description: The description of the action being created. 237 This defaults to the empty string if none is provided. 238 @type description: string 239 240 """ 241 request = get_current_request() 242 if request: 243 ip = request.META.get('REMOTE_ADDR') 244 user = request.user 245 if not isinstance(user, User): 246 user = User(id=1) 247 return Action(type=1, 248 label=label, 249 description=description, 250 item_type=ContentType.objects.get_for_model(item), 251 item_id=item.id, 252 user=user, 253 ip=ip)
254
255 - def items(self):
256 """ 257 @summary: 258 259 @return: 260 @rtype: 261 """ 262 return Action.by_action(self)
263 264 @classmethod
265 - def by_item(klass, item):
266 """ 267 @summary: 268 269 @return: 270 @rtype: 271 """ 272 return Action.objects.filter(item_type=ContentType.objects.get_for_model(item), 273 item_id=item.id)
274 275 @classmethod
276 - def by_action(klass, action):
277 """ 278 @summary: 279 280 @return: 281 @rtype: 282 """ 283 return Action.objects.filter(action=action)
284
285 - def __unicode__(self):
286 return self.label
287 - class Admin:
288 pass
289 290 291 @mixin_property([user.UserExtended])
292 -def actions(self):
293 """ 294 @summary: Returns a QuerySet containing all actions by this user, 295 limited to the specified type, if provided. 296 @rtype: QuerySet 297 """ 298 return Action.by_user(self)
299 300 #def ActionTargetManager(model_class): 301 # """ 302 # @summary: 303 # Generates an ActionTargetManager for a specific model class. 304 # """ 305 # class ActionTargetManager(models.Manager): 306 # """ 307 # @summary: 308 # 309 # """ 310 # def get_query_set(self): 311 # return super(ActionTargetManager, self).get_query_set(item_type=ContentType.objects.get_for_model(item)) 312 # def by_user(self, user): 313 # return self.filter(user=user) 314 # 315 # return ActionTargetManager 316 317
318 -class ActionMixin(object):
319 """ 320 @summary: 321 B{ActionMixin} is a mix-in used to provide common methods, attributes, 322 and hooks to all the models whose changes are logged by L{log.Action}. 323 324 At initialization, ActionTarget.B{_apply} is applied to all the models 325 listed in L{log.Action}. 326 327 @ivar actions: 328 @type actions: ActionTargetManager 329 @ivar log: 330 @type log: LogManager 331 """ 332 333 @classmethod
334 - def by_user(klass, user):
335 """ 336 @summary: Returns a QuerySet containing the appropriate models, 337 depending which Model class this method was called on. 338 @rtype: QuerySet 339 """ 340 db_table = klass._meta.db_table 341 item_type_id = ContentType.objects.get_for_model(klass).id 342 return klass.objects.extra( 343 tables=['db_action AS action'], 344 where =['%s.id = action.item_id' % db_table, 345 'action.item_type_id = %s' % item_type_id, 346 'action.user_id=%s' % user.id])
347 348 349 @cached_property
350 - def action(self):
351 """ 352 @summary: Returns the Action object logged at the time of this 353 object's creation. 354 @rtype: Action 355 """ 356 if not hasattr(self, 'cached_action'): 357 action = Action.by_item(self).select_related()[:1] 358 if len(action) == 0: 359 raise RuntimeError("No action object for '%s'" % self) 360 self.cached_action = action[0] 361 return self.cached_action
362 363 @property
364 - def user(self):
365 """ 366 @summary: Returns the User responsible for creating this object. 367 @rtype: User 368 """ 369 return self.action.user
370 #return self.action and self.action.user or None 371 372 @property
373 - def time(self):
374 """ 375 @summary: Returns the time at which this object was created. 376 @rtype: datetime 377 """ 378 return self.action.time
379 #return self.action and self.action.time or None 380