Package expedient :: Package clearinghouse :: Package aggregate :: Module models
[hide private]
[frames] | no frames]

Source Code for Module expedient.clearinghouse.aggregate.models

  1  ''' 
  2  @author: jnaous 
  3  ''' 
  4   
  5  import logging 
  6  from django.db import models 
  7  from django.contrib.auth.models import User 
  8  from django.conf import settings 
  9  from django.core.urlresolvers import reverse, NoReverseMatch 
 10  from django.contrib.contenttypes.models import ContentType 
 11  from expedient.common.extendable.models import Extendable 
 12  from expedient.common.permissions.shortcuts import \ 
 13      give_permission_to, delete_permission, must_have_permission, has_permission,\ 
 14      get_permittee_from_threadlocals 
 15  from expedient.common.permissions.models import Permittee 
 16  from expedient.common.permissions.utils import permissions_save_override,\ 
 17      permissions_delete_override 
 18  from expedient.common.permissions.decorators import require_obj_permissions_for_method 
 19  from expedient.common.permissions.exceptions import PermissionDenied 
 20   
 21  logger = logging.getLogger("aggregate.models") 
22 23 -class Aggregate(Extendable):
24 ''' 25 Holds information about an aggregate. Needs to be extended by plugins. 26 27 @cvar information: Information about the aggregate. Used when displaying 28 information about the type. Should be overriden. 29 30 @ivar name: human-readable name of the Aggregate 31 @type name: L{str} 32 @ivar logo: Logo for the aggregate (an uploaded file). 33 @type logo: C{models.ImageField} 34 @ivar description: Description of this aggregate 35 @type description: Text Field 36 @ivar location: The location of the aggregate. 37 @type location: a string that is understandable by Google Maps. 38 @ivar available: Is the aggregate available for use? 39 @type available: C{bool} 40 @ivar slice_set: A read-only property that returns a queryset of 41 all slices allowed to use the aggregate (i.e. have the 42 "can_use_aggregate" permission for this aggregate). 43 @type slice_set: C{QuerySet} of C{Slice}s. 44 @ivar managers: A read-only property that returns a queryset of 45 all user allowed to edit the aggregate (i.e. have the 46 "can_edit_aggregate" permission for this aggregate). 47 @type managers: C{QuerySet} of C{User}s. 48 ''' 49 50 information = \ 51 """ 52 No information available. 53 """ 54 55 name = models.CharField( 56 max_length=200, unique=True, 57 help_text="Use a unique name for this aggregate.") 58 logo = models.ImageField( 59 'Logo', upload_to=settings.AGGREGATE_LOGOS_DIR, 60 editable=False, blank=True, null=True, 61 help_text="Select an optional logo.") 62 description = models.TextField(default="") 63 location = models.CharField( 64 "Geographic Location", max_length=200, default="", 65 help_text="This should be a location that can be found using " 66 "Google Maps") 67 available = models.BooleanField( 68 "Available", default=True, 69 help_text="Do you want to make this\ 70 aggregate available for others to use?") 71
72 - class Meta:
73 verbose_name = "Generic Aggregate"
74 75 save = permissions_save_override( 76 permittee_kw="user", 77 model_func=lambda: Aggregate, 78 create_perm="can_add_aggregate", 79 edit_perm="can_edit_aggregate", 80 delete_perm="can_edit_aggregate", 81 ) 82 delete = permissions_delete_override( 83 permittee_kw="user", 84 model_func=lambda: Aggregate, 85 delete_perm="can_edit_aggregate", 86 ) 87
88 - def __unicode__(self):
89 return u'Aggregate %s' % self.name
90
91 - def _get_managers(self):
92 """Gets the list of users who have the "can_edit_aggregate" permission 93 for this aggregate as a C{QuerySet} of C{User} objects. 94 """ 95 return Permittee.objects.filter_for_class_and_permission_name( 96 klass=User, 97 permission="can_edit_aggregate", 98 target_obj_or_class=self, 99 )
100 managers = property(_get_managers) 101
102 - def _get_slice_set(self):
103 """Gets the list of slices allowed to use the aggregate""" 104 from expedient.clearinghouse.slice.models import Slice 105 return Permittee.objects.filter_for_class_and_permission_name( 106 klass=Slice, 107 permission="can_use_aggregate", 108 target_obj_or_class=self, 109 )
110 slice_set = property(_get_slice_set) 111 112
113 - def check_status(self):
114 """Checks whether the aggregate is available or not. 115 116 @return: True if the aggregate is available, False otherwise. 117 """ 118 return self.available
119
120 - def get_logo_url(self):
121 try: 122 return self.logo.url 123 except Exception as e: 124 logger.debug("Exception getting logo url %s" % e) 125 return ""
126 127 @classmethod
128 - def get_url_name_prefix(cls):
129 """ 130 Get the prefix to append to the beginning of url names when 131 getting default urls. 132 133 By default this returns the application name. 134 """ 135 ct = ContentType.objects.get_for_model(cls) 136 return ct.app_label
137
138 - def get_edit_url(self):
139 """Get the url of where to go to edit the aggregate""" 140 return reverse( 141 "%s_aggregate_edit" % self.__class__.get_url_name_prefix(), 142 kwargs={'agg_id': self.id})
143
144 - def get_delete_url(self, next):
145 """ 146 Get the URL to use when deleting the project from the 147 Aggregate List. This function will first check if there is a URL 148 defined as <app_label>_aggregate_delete and return that if it 149 exists, attaching "?next=<C{next}>" to the end of the URL. 150 151 @param next: URL to redirect to after deleting object. 152 153 @return: URL to go to when requesting the aggregate be deleted. 154 """ 155 prefix = self.__class__.get_url_name_prefix() 156 try: 157 return reverse("%s_aggregate_delete" % prefix, 158 kwargs={'agg_id': self.id})+"?next=%s" % next 159 except NoReverseMatch: 160 return reverse("aggregate_delete", 161 kwargs={'agg_id': self.id})+"?next=%s" % next
162 163 @classmethod
164 - def get_aggregates_url(cls):
165 """Get the URL for aggregates of this type""" 166 ct = ContentType.objects.get_for_model(cls) 167 return reverse("aggregate_info", args=[ct.id])
168 169 @classmethod
170 - def get_create_url(cls):
171 """Get the URL to create aggregates of this type""" 172 prefix = cls.get_url_name_prefix() 173 return reverse("%s_aggregate_create" % prefix)
174
175 - def add_to_project(self, project, next):
176 """ 177 Gives the aggregate a chance to request additional information for a 178 project. This method should return a URL to redirect to where the 179 user can create or update the additional information the aggregate 180 needs. When done, the view at that URL should use the 181 C{give_permission} function to give the project 182 the "can_use_aggregate" permission:: 183 184 from expedient.common.permissions.shortcuts import \ 185 give_permission_to 186 187 give_permission_to("can_use_aggregate", self.as_leaf_class(), project) 188 189 and then it should redirect to C{next}. 190 191 If no extra information is needed, this function can return C{next}, 192 instead of a custom URL, but it still needs to give the project the 193 "can_use_aggregate" permission. 194 195 Unless overridden in a subclass, this function will look for a url 196 with name <app_name>_aggregate_project_add by reversing the name with 197 it parameters 'agg_id' and 'proj_id'. It will append '?next=<next>' to 198 the URL if found. Otherwise, it simply gives the permission to the 199 project and returns C{next}. 200 """ 201 202 logger.debug("adding aggregate to project") 203 204 must_have_permission("user", self.as_leaf_class(), "can_use_aggregate") 205 206 prefix = self.__class__.get_url_name_prefix() 207 try: 208 return reverse("%s_aggregate_project_add" % prefix, 209 kwargs={'agg_id': self.id, 210 'proj_id': project.id})+"?next=%s" % next 211 except NoReverseMatch: 212 logger.debug("Giving permission to use aggregate to %s" % project) 213 give_permission_to("can_use_aggregate", self.as_leaf_class(), project) 214 return next
215
216 - def remove_from_project(self, project, next):
217 """ 218 Similar to L{add_to_project} but does the reverse, deleting the 219 permission from the project using:: 220 221 from expedient.common.permissions.shortcuts import \ 222 delete_permission 223 224 delete_permission("can_use_aggregate", self.as_leaf_class(), project) 225 226 and then redirecting to C{next}. Additionally, if not overridden, 227 this function stops all slices in the project before removing the 228 aggregate. Subclasses should also stop slices. 229 """ 230 must_have_permission("user", self.as_leaf_class(), "can_use_aggregate") 231 232 prefix = self.__class__.get_url_name_prefix() 233 try: 234 return reverse("%s_aggregate_project_remove" % prefix, 235 kwargs={'agg_id': self.id, 236 'proj_id': project.id})+"?next=%s" % next 237 except NoReverseMatch: 238 # Stop all the slices in the project for this aggregate. 239 for slice in project.slice_set.all(): 240 try: 241 self.as_leaf_class().stop_slice(slice) 242 except: 243 pass 244 delete_permission("can_use_aggregate", self.as_leaf_class(), project) 245 return next
246
247 - def add_to_slice(self, slice, next):
248 """ 249 Works exactly the same as L{add_to_project} but for a slice. 250 """ 251 must_have_permission("user", self.as_leaf_class(), "can_use_aggregate") 252 must_have_permission("project", self.as_leaf_class(), "can_use_aggregate") 253 254 prefix = self.__class__.get_url_name_prefix() 255 try: 256 return reverse("%s_aggregate_slice_add" % prefix, 257 kwargs={'agg_id': self.id, 258 'slice_id': slice.id})+"?next=%s" % next 259 except NoReverseMatch: 260 give_permission_to("can_use_aggregate", self.as_leaf_class(), slice) 261 return next
262
263 - def remove_from_slice(self, slice, next):
264 """ 265 Works exactly the same as L{remove_from_project} but for a slice. 266 It stops the slice if not overridden. Subclasses should stop the 267 slice before removing the permission. 268 """ 269 must_have_permission("user", self.as_leaf_class(), "can_use_aggregate") 270 must_have_permission("project", self.as_leaf_class(), "can_use_aggregate") 271 272 prefix = self.__class__.get_url_name_prefix() 273 try: 274 return reverse("%s_aggregate_slice_remove" % prefix, 275 kwargs={'agg_id': self.id, 276 'slice_id': slice.id})+"?next=%s" % next 277 except NoReverseMatch: 278 try: 279 self.as_leaf_class().stop_slice(slice) 280 except: 281 pass 282 delete_permission("can_use_aggregate", self.as_leaf_class(), slice) 283 return next
284
285 - def add_to_user(self, user, next):
286 """ 287 Works exactly the same as L{add_to_project} but for a user. 288 """ 289 prefix = self.__class__.get_url_name_prefix() 290 try: 291 return reverse("%s_aggregate_user_add" % prefix, 292 kwargs={'agg_id': self.id, 293 'user_id': user.id})+"?next=%s" % next 294 except NoReverseMatch: 295 give_permission_to("can_use_aggregate", self.as_leaf_class(), user) 296 return next
297
298 - def remove_from_user(self, user, next):
299 """ 300 Works exactly the same as L{remove_from_project} but for a user. 301 Does not stop any slices. 302 """ 303 prefix = self.__class__.get_url_name_prefix() 304 try: 305 return reverse("%s_aggregate_user_remove" % prefix, 306 kwargs={'agg_id': self.id, 307 'user_id': user.id})+"?next=%s" % next 308 except NoReverseMatch: 309 delete_permission("can_use_aggregate", self.as_leaf_class(), user) 310 return next
311
312 - def start_slice(self, slice):
313 """Start the slice in the actual resources. 314 315 Subclasses overriding this method should call the parent class 316 to ensure permission checks. 317 """ 318 must_have_permission("user", self.as_leaf_class(), "can_use_aggregate") 319 must_have_permission("project", self.as_leaf_class(), "can_use_aggregate") 320 must_have_permission("slice", self.as_leaf_class(), "can_use_aggregate") 321 pass
322
323 - def stop_slice(self, slice):
324 """Take out the resource reservation from the aggregates. 325 326 Subclasses overriding this method should call the parent class 327 to ensure permission checks. 328 """ 329 user = get_permittee_from_threadlocals("user") 330 can_use = has_permission( 331 user, self.as_leaf_class(), "can_use_aggregate") 332 can_edit = has_permission( 333 user, self.as_leaf_class(), "can_edit_aggregate") 334 if not can_use and not can_edit: 335 raise PermissionDenied( 336 "can_use_aggregate", 337 self.as_leaf_class(), 338 user, allow_redirect=False) 339 pass
340