from django.conf import settings
from django.utils.importlib import import_module
from django.core.validators import email_re
+from django.core.exceptions import PermissionDenied
+from django.views.generic.create_update import lookup_object
+from django.core.exceptions import ObjectDoesNotExist
from astakos.im.settings import (
DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
AUTH_TOKEN_DURATION, BILLING_FIELDS,
EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL,
- GROUP_CREATION_SUBJECT
+ SITENAME, SERVICES,
+ PROJECT_CREATION_SUBJECT, PROJECT_APPROVED_SUBJECT,
+ PROJECT_TERMINATION_SUBJECT, PROJECT_SUSPENSION_SUBJECT,
+ PROJECT_MEMBERSHIP_CHANGE_SUBJECT
)
from astakos.im.endpoints.qh import (
register_users, send_quota, register_resources
logger = logging.getLogger(__name__)
DEFAULT_CONTENT_TYPE = None
-try:
- content_type = ContentType.objects.get(app_label='im', model='astakosuser')
-except:
- content_type = DEFAULT_CONTENT_TYPE
+_content_type = None
+
+PENDING, APPROVED, REPLACED, UNKNOWN = 'Pending', 'Approved', 'Replaced', 'Unknown'
+
+def get_content_type():
+ global _content_type
+ if _content_type is not None:
+ return _content_type
+
+ try:
+ content_type = ContentType.objects.get(app_label='im', model='astakosuser')
+ except:
+ content_type = DEFAULT_CONTENT_TYPE
+ _content_type = content_type
+ return content_type
RESOURCE_SEPARATOR = '.'
class Resource(models.Model):
- name = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
+ name = models.CharField(_('Name'), max_length=255)
meta = models.ManyToManyField(ResourceMetadata)
service = models.ForeignKey(Service)
desc = models.TextField(_('Description'), null=True)
unit = models.CharField(_('Name'), null=True, max_length=255)
group = models.CharField(_('Group'), null=True, max_length=255)
+
+ class Meta:
+ unique_together = ("name", "service")
def __str__(self):
return '%s%s%s' % (self.service, RESOURCE_SEPARATOR, self.name)
_('Creation date'),
default=datetime.now()
)
- issue_date = models.DateTimeField('Start date', null=True)
+ issue_date = models.DateTimeField(
+ _('Start date'),
+ null=True
+ )
expiration_date = models.DateTimeField(
_('Expiration date'),
null=True
self.owner = l
map(self.approve_member, l)
-
+_default_quota = {}
+def get_default_quota():
+ global _default_quota
+ if _default_quota:
+ return _default_quota
+ for s, data in SERVICES.iteritems():
+ map(
+ lambda d:_default_quota.update(
+ {'%s%s%s' % (s, RESOURCE_SEPARATOR, d.get('name')):d.get('uplimit', 0)}
+ ),
+ data.get('resources', {})
+ )
+ return _default_quota
class AstakosUserManager(UserManager):
def add_permission(self, pname):
if self.has_perm(pname):
return
- p, created = Permission.objects.get_or_create(codename=pname,
- name=pname.capitalize(),
- content_type=content_type)
+ p, created = Permission.objects.get_or_create(
+ codename=pname,
+ name=pname.capitalize(),
+ content_type=get_content_type())
self.user_permissions.add(p)
def remove_permission(self, pname):
if self.has_perm(pname):
return
p = Permission.objects.get(codename=pname,
- content_type=content_type)
+ content_type=get_content_type())
self.user_permissions.remove(p)
@property
def quota(self):
"""Returns a dict with the sum of quota limits per resource"""
d = defaultdict(int)
+ default_quota = get_default_quota()
+ d.update(default_quota)
for q in self.policies:
d[q.resource] += q.uplimit or inf
- for m in self.extended_groups:
- if not m.is_approved:
+ for m in self.projectmembership_set.select_related().all():
+ if not m.acceptance_date:
continue
- g = m.group
- if not g.is_enabled:
+ p = m.project
+ if not p.is_active:
continue
- for r, uplimit in g.quota.iteritems():
- d[r] += uplimit or inf
+ grants = p.application.definition.projectresourcegrant_set.all()
+ for g in grants:
+ d[str(g.resource)] += g.member_limit or inf
# TODO set default for remaining
return d
session_key = models.CharField(_('session key'), max_length=40)
user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
-class MemberAcceptPolicy(models.Model):
+class MemberJoinPolicy(models.Model):
policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
description = models.CharField(_('Description'), max_length=80)
def __str__(self):
return self.policy
-try:
- auto_accept = MemberAcceptPolicy.objects.get(policy='auto_accept')
-except:
- auto_accept = None
+class MemberLeavePolicy(models.Model):
+ policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
+ description = models.CharField(_('Description'), max_length=80)
+
+ def __str__(self):
+ return self.policy
+
+_auto_accept_join = False
+def get_auto_accept_join():
+ global _auto_accept_join
+ if _auto_accept_join is not False:
+ return _auto_accept_join
+ try:
+ auto_accept = MemberJoinPolicy.objects.get(policy='auto_accept')
+ except:
+ auto_accept = None
+ _auto_accept_join = auto_accept
+ return auto_accept
+
+_closed_join = False
+def get_closed_join():
+ global _closed_join
+ if _closed_join is not False:
+ return _closed_join
+ try:
+ closed = MemberJoinPolicy.objects.get(policy='closed')
+ except:
+ closed = None
+ _closed_join = closed
+ return closed
+
+_auto_accept_leave = False
+def get_auto_accept_leave():
+ global _auto_accept_leave
+ if _auto_accept_leave is not False:
+ return _auto_accept_leave
+ try:
+ auto_accept = MemberLeavePolicy.objects.get(policy='auto_accept')
+ except:
+ auto_accept = None
+ _auto_accept_leave = auto_accept
+ return auto_accept
+
+_closed_leave = False
+def get_closed_leave():
+ global _closed_leave
+ if _closed_leave is not False:
+ return _closed_leave
+ try:
+ closed = MemberLeavePolicy.objects.get(policy='closed')
+ except:
+ closed = None
+ _closed_leave = closed
+ return closeds
class ProjectDefinition(models.Model):
name = models.CharField(max_length=80)
description = models.TextField(null=True)
start_date = models.DateTimeField()
end_date = models.DateTimeField()
- member_accept_policy = models.ForeignKey(MemberAcceptPolicy)
+ member_join_policy = models.ForeignKey(MemberJoinPolicy)
+ member_leave_policy = models.ForeignKey(MemberLeavePolicy)
limit_on_members_number = models.PositiveIntegerField(null=True,blank=True)
resource_grants = models.ManyToManyField(
Resource,
through='ProjectResourceGrant'
)
- def save(self):
- self.validate_name()
- super(ProjectDefinition, self).save()
-
- def validate_name(self):
- """
- Validate name uniqueness among all active projects.
- """
- alive_projects = list(get_alive_projects())
- q = filter(lambda p: p.definition.name==self.name, alive_projects)
- if q:
- raise ValidationError({'name': [_(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)]})
-
@property
def violated_resource_grants(self):
return False
update = p.get('update', True)
self.add_resource_policy(service, resource, uplimit, update)
- def get_absolute_url(self):
- return reverse('project_application_detail', args=(self.serial,))
+ def validate_name(self):
+ """
+ Validate name uniqueness among all active projects.
+ """
+ alive_projects = list(get_alive_projects())
+ q = filter(
+ lambda p: p.definition.name == self.name and \
+ p.application.id != self.projectapplication.id,
+ alive_projects
+ )
+ if q:
+ raise ValidationError(
+ _(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)
+ )
class ProjectResourceGrant(models.Model):
class Meta:
unique_together = ("resource", "project_definition")
+
class ProjectApplication(models.Model):
- serial = models.CharField(
- primary_key=True,
- max_length=30,
- unique=True,
- default=uuid.uuid4().hex[:30]
+ states_list = [PENDING, APPROVED, REPLACED, UNKNOWN]
+ states = dict((k, v) for k, v in enumerate(states_list))
+
+ applicant = models.ForeignKey(
+ AstakosUser,
+ related_name='my_project_applications',
+ db_index=True)
+ owner = models.ForeignKey(
+ AstakosUser,
+ related_name='own_project_applications',
+ db_index=True
)
- applicant = models.ForeignKey(AstakosUser, related_name='my_project_applications')
- owner = models.ForeignKey(AstakosUser, related_name='own_project_applications')
comments = models.TextField(null=True, blank=True)
definition = models.OneToOneField(ProjectDefinition)
issue_date = models.DateTimeField()
precursor_application = models.OneToOneField('ProjectApplication',
null=True,
- blank=True
+ blank=True,
+ db_index=True
)
+ state = models.CharField(max_length=80, default=UNKNOWN)
+
+ @property
+ def follower(self):
+ try:
+ return ProjectApplication.objects.get(precursor_application=self)
+ except ProjectApplication.DoesNotExist:
+ return
+
+ def save(self):
+ self.definition.save()
+ self.definition = self.definition
+ super(ProjectApplication, self).save()
+
+
+ @staticmethod
+ def submit(definition, resource_policies, applicant, comments,
+ precursor_application=None, commit=True):
+
+ application = ProjectApplication()
+ if precursor_application:
+ application.precursor_application = precursor_application
+ application.owner = precursor_application.owner
+ else:
+ application.owner = applicant
+
+ application.definition = definition
+ application.definition.resource_policies = resource_policies
+ application.applicant = applicant
+ application.comments = comments
+ application.issue_date = datetime.now()
+ application.state = PENDING
+
+ if commit:
+ application.save()
+ application.definition.resource_policies = resource_policies
+ # better implementation ???
+ if precursor_application:
+ try:
+ precursor = ProjectApplication.objects.get(id=precursor_application_id)
+ except:
+ pass
+ application.precursor_application = precursor
+ application.save()
+
+ notification = build_notification(
+ settings.SERVER_EMAIL,
+ [i[1] for i in settings.ADMINS],
+ _(PROJECT_CREATION_SUBJECT) % application.definition.__dict__,
+ template='im/projects/project_creation_notification.txt',
+ dictionary={'object':application}
+ )
+ notification.send()
+ return application
+
+ def approve(self, approval_user=None):
+ """
+ If approval_user then during owner membership acceptance
+ it is checked whether the request_user is eligible.
+
+ Raises:
+ ValidationError: if there is other alive project with the same name
+
+ """
+ try:
+ self.definition.validate_name()
+ except ValidationError, e:
+ raise PermissionDenied(e.messages[0])
+ if self.state != PENDING:
+ raise PermissionDenied(_(PROJECT_ALREADY_ACTIVE))
+
+ try:
+ precursor = self.precursor_application
+ project = precursor.project
+ project.application = self
+ prev_approval_date = project.last_approval_date
+ project.last_approval_date = datetime.now()
+ project.save()
+
+ p = precursor
+ while p:
+ p.state = REPLACED
+ p.save()
+ p = p.precursor_application
+
+ except:
+ kwargs = {
+ 'application':self,
+ 'creation_date':datetime.now(),
+ 'last_approval_date':datetime.now(),
+ }
+ project = _create_object(Project, **kwargs)
+ project.accept_member(self.owner, approval_user)
+ precursor = None
+
+ self.state = APPROVED
+ self.save()
+
+ notification = build_notification(
+ settings.SERVER_EMAIL,
+ [self.owner.email],
+ _(PROJECT_APPROVED_SUBJECT) % self.definition.__dict__,
+ template='im/projects/project_approval_notification.txt',
+ dictionary={'object':self}
+ )
+ notification.send()
+
+ rejected = self.project.sync()
+ if rejected:
+ # revert to precursor
+ if precursor:
+ project.application = precursor
+ project.last_approval_date = prev_approval_date
+ project.save()
+
+ rejected = project.sync()
+ if rejected:
+ raise Exception(_(astakos_messages.QH_SYNC_ERROR))
+ else:
+ project.last_application_synced = self
+ project.save()
+
class Project(models.Model):
- serial = models.CharField(
- _('username'),
- primary_key=True,
- max_length=30,
- unique=True,
- default=uuid.uuid4().hex[:30]
- )
application = models.OneToOneField(ProjectApplication, related_name='project')
creation_date = models.DateTimeField()
- last_approval_date = models.DateTimeField()
- termination_date = models.DateTimeField()
+ last_approval_date = models.DateTimeField(null=True)
+ termination_start_date = models.DateTimeField(null=True)
+ termination_date = models.DateTimeField(null=True)
members = models.ManyToManyField(AstakosUser, through='ProjectMembership')
- last_synced_application = models.OneToOneField(
+ membership_dirty = models.BooleanField(default=False)
+ last_application_synced = models.OneToOneField(
ProjectApplication, related_name='last_project', null=True, blank=True
)
+
@property
def definition(self):
return self.application.definition
-
+
@property
- def is_valid(self):
- try:
- self.application.definition.validate_name()
- except ValidationError:
- return False
- else:
- return True
-
+ def violated_members_number_limit(self):
+ return len(self.approved_members) <= self.definition.limit_on_members_number
+
@property
def is_active(self):
- if not self.is_valid:
- return False
if not self.last_approval_date:
return False
if self.termination_date:
return False
if self.definition.violated_resource_grants:
return False
+# if self.violated_members_number_limit:
+# return False
return True
@property
def is_terminated(self):
- if not self.is_valid:
- return False
if not self.termination_date:
return False
return True
@property
def is_suspended(self):
- if not self.is_valid:
- return False
- if not self.termination_date:
+ if self.termination_date:
return False
- if not self.last_approval_date:
+ if self.last_approval_date:
if not self.definition.violated_resource_grants:
return False
+# if not self.violated_members_number_limit:
+# return False
return True
@property
return False
@property
+ def is_synchronized(self):
+ return self.last_application_synced == self.application and \
+ not self.membership_dirty and \
+ (not self.termination_start_date or termination_date)
+
+ @property
def approved_members(self):
- return [m.person for m in self.members.filter(is_accepted=True)]
+ return [m.person for m in self.projectmembership_set.filter(~Q(acceptance_date=None))]
+
+ def sync(self, specific_members=()):
+ if self.is_synchronized:
+ return
+ members = specific_members or self.approved_members
+ c, rejected = send_quota(self.approved_members)
+ return rejected
- def suspend(self):
- self.last_approval_date = None
- self.save()
+ def accept_member(self, user, request_user=None):
+ """
+ Raises:
+ django.exceptions.PermissionDenied
+ astakos.im.models.AstakosUser.DoesNotExist
+ """
+ if isinstance(user, int):
+ try:
+ user = lookup_object(AstakosUser, user, None, None)
+ except Http404:
+ raise AstakosUser.DoesNotExist()
+ m, created = ProjectMembership.objects.get_or_create(
+ person=user, project=self
+ )
+ m.accept(delete_on_failure=created, request_user=None)
+
+ def reject_member(self, user, request_user=None):
+ """
+ Raises:
+ django.exceptions.PermissionDenied
+ astakos.im.models.AstakosUser.DoesNotExist
+ astakos.im.models.ProjectMembership.DoesNotExist
+ """
+ if isinstance(user, int):
+ try:
+ user = lookup_object(AstakosUser, user, None, None)
+ except Http404:
+ raise AstakosUser.DoesNotExist()
+ m = ProjectMembership.objects.get(person=user, project=self)
+ m.reject()
+
+ def remove_member(self, user, request_user=None):
+ """
+ Raises:
+ django.exceptions.PermissionDenied
+ astakos.im.models.AstakosUser.DoesNotExist
+ astakos.im.models.ProjectMembership.DoesNotExist
+ """
+ if isinstance(user, int):
+ try:
+ user = lookup_object(AstakosUser, user, None, None)
+ except Http404:
+ raise AstakosUser.DoesNotExist()
+ m = ProjectMembership.objects.get(person=user, project=self)
+ m.remove()
def terminate(self):
- self.terminaton_date = datetime.now()
+ self.termination_start_date = datetime.now()
+ self.terminaton_date = None
self.save()
-
- def sync(self):
- c, rejected = send_quota(self.approved_members)
- return rejected
+
+ rejected = self.sync()
+ if not rejected:
+ self.termination_start_date = None
+ self.terminaton_date = datetime.now()
+ self.save()
+
+ notification = build_notification(
+ settings.SERVER_EMAIL,
+ [self.application.owner.email],
+ _(PROJECT_TERMINATION_SUBJECT) % self.definition.__dict__,
+ template='im/projects/project_termination_notification.txt',
+ dictionary={'object':self.application}
+ )
+ notification.send()
+
+ def suspend(self):
+ self.last_approval_date = None
+ self.save()
+ self.sync()
+ notification = build_notification(
+ settings.SERVER_EMAIL,
+ [self.application.owner.email],
+ _(PROJECT_SUSPENSION_SUBJECT) % self.definition.__dict__,
+ template='im/projects/project_suspension_notification.txt',
+ dictionary={'object':self.application}
+ )
+ notification.send()
class ProjectMembership(models.Model):
person = models.ForeignKey(AstakosUser)
project = models.ForeignKey(Project)
- issue_date = models.DateField(default=datetime.now())
- decision_date = models.DateField(null=True, db_index=True)
- is_accepted = models.BooleanField(
- _('Whether the membership application is accepted'),
- default=False
- )
+ request_date = models.DateField(default=datetime.now())
+ acceptance_date = models.DateField(null=True, db_index=True)
+ leave_request_date = models.DateField(null=True)
class Meta:
unique_together = ("person", "project")
+ def accept(self, delete_on_failure=False, request_user=None):
+ """
+ Raises:
+ django.exception.PermissionDenied
+ astakos.im.notifications.NotificationError
+ """
+ try:
+ if request_user and \
+ (not self.project.application.owner == request_user and \
+ not request_user.is_superuser):
+ raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
+ if not self.project.is_alive:
+ raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % self.project.__dict__)
+ if self.project.definition.member_join_policy == 'closed':
+ raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
+ if len(self.project.approved_members) + 1 > self.project.definition.limit_on_members_number:
+ raise PermissionDenied(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
+ except PermissionDenied, e:
+ if delete_on_failure:
+ m.delete()
+ raise
+ if self.acceptance_date:
+ return
+ self.acceptance_date = datetime.now()
+ self.save()
+ notification = build_notification(
+ settings.SERVER_EMAIL,
+ [self.person.email],
+ _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % self.project.definition.__dict__,
+ template='im/projects/project_membership_change_notification.txt',
+ dictionary={'object':self.project.application, 'action':'accepted'}
+ ).send()
+ self.sync()
+
+ def reject(self, request_user=None):
+ """
+ Raises:
+ django.exception.PermissionDenied,
+ astakos.im.notifications.NotificationError
+ """
+ if request_user and \
+ (not self.project.application.owner == request_user and \
+ not request_user.is_superuser):
+ raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
+ if not self.project.is_alive:
+ raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
+ history_item = ProjectMembershipHistory(
+ person=self.person,
+ project=self.project,
+ request_date=self.request_date,
+ rejection_date=datetime.now()
+ )
+ self.delete()
+ history_item.save()
+ notification = build_notification(
+ settings.SERVER_EMAIL,
+ [self.person.email],
+ _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % self.project.definition.__dict__,
+ template='im/projects/project_membership_change_notification.txt',
+ dictionary={'object':self.project.application, 'action':'rejected'}
+ ).send()
+
+ def remove(self, request_user=None):
+ """
+ Raises:
+ django.exception.PermissionDenied
+ astakos.im.notifications.NotificationError
+ """
+ if request_user and \
+ (not self.project.application.owner == request_user and \
+ not request_user.is_superuser):
+ raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
+ if not self.project.is_alive:
+ raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % self.project.__dict__)
+ history_item = ProjectMembershipHistory(
+ id=self.id,
+ person=self.person,
+ project=self.project,
+ request_date=self.request_date,
+ removal_date=datetime.now()
+ )
+ self.delete()
+ history_item.save()
+ notification = build_notification(
+ settings.SERVER_EMAIL,
+ [self.person.email],
+ _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % self.project.definition.__dict__,
+ template='im/projects/project_membership_change_notification.txt',
+ dictionary={'object':self.project.application, 'action':'removed'}
+ ).send()
+ self.sync()
+
+ def leave(self):
+ leave_policy = self.project.application.definition.member_leave_policy
+ if leave_policy == get_auto_accept_leave():
+ self.remove()
+ else:
+ self.leave_request_date = datetime.now()
+ self.save()
+
+ def sync(self):
+ # set membership_dirty flag
+ self.project.membership_dirty = True
+ self.project.save()
+
+ rejected = self.project.sync(specific_members=[self.person])
+ if not rejected:
+ # if syncing was successful unset membership_dirty flag
+ self.membership_dirty = False
+ self.project.save()
+
+
+class ProjectMembershipHistory(models.Model):
+ person = models.ForeignKey(AstakosUser)
+ project = models.ForeignKey(Project)
+ request_date = models.DateField(default=datetime.now())
+ removal_date = models.DateField(null=True)
+ rejection_date = models.DateField(null=True)
+
+
def filter_queryset_by_property(q, property):
"""
Incorporate list comprehension for filtering querysets by property
'is_active'
)
-def _lookup_object(model, **kwargs):
- """
- Returns an object of the specific model matching the given lookup
- parameters.
- """
- if not kwargs:
- raise MissingIdentifier
- try:
- return model.objects.get(**kwargs)
- except model.DoesNotExist:
- raise ItemNotExists(model._meta.verbose_name, **kwargs)
- except model.MultipleObjectsReturned:
- raise MultipleItemsExist(model._meta.verbose_name, **kwargs)
-
def _create_object(model, **kwargs):
o = model.objects.create(**kwargs)
o.save()
return o
-def _update_object(model, id, save=True, **kwargs):
- o = self._lookup_object(model, id=id)
- if kwargs:
- o.__dict__.update(kwargs)
- if save:
- o.save()
- return o
-
-def list_applications():
- return ProjectAppication.objects.all()
-
-def submit_application(definition, applicant, comments, precursor_application=None, commit=True):
- if precursor_application:
- application = precursor_application.copy()
- application.precursor_application = precursor_application
- else:
- application = ProjectApplication(owner=applicant)
- application.definition = definition
- application.applicant = applicant
- application.comments = comments
- application.issue_date = datetime.now()
- if commit:
- definition.save()
- application.save()
- notification = build_notification(
- settings.SERVER_EMAIL,
- [i[1] for i in settings.ADMINS],
- _(GROUP_CREATION_SUBJECT) % {'group':application.definition.name},
- _('An new project application identified by %(serial)s has been submitted.') % application.__dict__
- )
- notification.send()
- return application
-
-def approve_application(serial):
- app = _lookup_object(ProjectAppication, serial=serial)
- notify = False
- if not app.precursor_application:
- kwargs = {
- 'application':app,
- 'creation_date':datetime.now(),
- 'last_approval_date':datetime.now(),
- }
- project = _create_object(Project, **kwargs)
- else:
- project = app.precursor_application.project
- last_approval_date = project.last_approval_date
- if project.is_valid:
- project.application = app
- project.last_approval_date = datetime.now()
- project.save()
- else:
- raise Exception(_(astakos_messages.INVALID_PROJECT) % project.__dict__)
-
- rejected = synchonize_project(project.serial)
- if rejected:
- # revert to precursor
- project.appication = app.precursor_application
- if project.application:
- project.last_approval_date = last_approval_date
- project.save()
- rejected = synchonize_project(project.serial)
- if rejected:
- raise Exception(_(astakos_messages.QH_SYNC_ERROR))
- else:
- project.last_application_synced = app
- project.save()
- sender, recipients, subject, message
- notification = build_notification(
- settings.SERVER_EMAIL,
- [project.owner.email],
- _('Project application has been approved on %s alpha2 testing' % SITENAME),
- _('Your application request %(serial)s has been apporved.')
- )
- notification.send()
-
-
-def list_projects(filter_property=None):
- if filter_property:
- return filter_queryset_by_property(
- Project.objects.all(),
- filter_property
- )
- return Project.objects.all()
-
-def add_project_member(serial, user_id, request_user):
- project = _lookup_object(Project, serial=serial)
- user = _lookup_object(AstakosUser, id=user_id)
- if not project.owner == request_user:
- raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
-
- if not project.is_alive:
- raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
- if len(project.members) + 1 > project.limit_on_members_number:
- raise Exception(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
- m = self._lookup_object(ProjectMembership, person=user, project=project)
- if m.is_accepted:
- return
- m.is_accepted = True
- m.decision_date = datetime.now()
- m.save()
- notification = build_notification(
- settings.SERVER_EMAIL,
- [user.email],
- _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__,
- _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__,
- )
- notification.send()
-
-def remove_project_member(serial, user_id, request_user):
- project = _lookup_object(Project, serial=serial)
- if not project.is_alive:
- raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
- if not project.owner == request_user:
- raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
- user = self.lookup_user(user_id)
- m = _lookup_object(ProjectMembership, person=user, project=project)
- if not m.is_accepted:
- return
- m.is_accepted = False
- m.decision_date = datetime.now()
- m.save()
- notification = build_notification(
- settings.SERVER_EMAIL,
- [user.email],
- _('Your membership on project %(name)s has been removed.') % project.definition.__dict__,
- _('Your membership on project %(name)s has been removed.') % project.definition.__dict__
- )
- notification.send()
-
-def suspend_project(serial):
- project = _lookup_object(Project, serial=serial)
- project.suspend()
- notification = build_notification(
- settings.SERVER_EMAIL,
- [project.owner.email],
- _('Project %(name)s has been suspended.') % project.definition.__dict__,
- _('Project %(name)s has been suspended.') % project.definition.__dict__
- )
- notification.send()
-
-def terminate_project(serial):
- project = _lookup_object(Project, serial=serial)
- project.termination()
- notification = build_notification(
- settings.SERVER_EMAIL,
- [project.owner.email],
- _('Project %(name)s has been terminated.') % project.definition.__dict__,
- _('Project %(name)s has been terminated.') % project.definition.__dict__
- )
- notification.send()
-def synchonize_project(serial):
- project = _lookup_object(Project, serial=serial)
- if project.app != project.last_application_synced:
- return project.sync()
-
def create_astakos_user(u):
try:
AstakosUser.objects.get(user_ptr=u.pk)
admins = User.objects.filter(is_superuser=True)
for u in admins:
create_astakos_user(u)
+post_syncdb.connect(fix_superusers)
def user_post_save(sender, instance, created, **kwargs):
if not created:
return
create_astakos_user(instance)
-
-
-def set_default_group(user):
- try:
- default = AstakosGroup.objects.get(name='default')
- Membership(
- group=default, person=user, date_joined=datetime.now()).save()
- except AstakosGroup.DoesNotExist, e:
- logger.exception(e)
+post_save.connect(user_post_save, sender=User)
def astakosuser_pre_save(sender, instance, **kwargs):
l = filter(lambda f: get(db_instance, f) != get(instance, f),
BILLING_FIELDS)
instance.aquarium_report = True if l else False
+pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
+
+def set_default_group(user):
+ try:
+ default = AstakosGroup.objects.get(name='default')
+ Membership(
+ group=default, person=user, date_joined=datetime.now()).save()
+ except AstakosGroup.DoesNotExist, e:
+ logger.exception(e)
def astakosuser_post_save(sender, instance, created, **kwargs):
set_default_group(instance)
# TODO handle socket.error & IOError
register_users((instance,))
+post_save.connect(astakosuser_post_save, sender=AstakosUser)
def resource_post_save(sender, instance, created, **kwargs):
if not created:
return
register_resources((instance,))
+post_save.connect(resource_post_save, sender=Resource)
+
+
+def on_quota_disturbed(sender, users, **kwargs):
+# print '>>>', locals()
+ if not users:
+ return
+ send_quota(users)
+
+quota_disturbed = Signal(providing_args=["users"])
+quota_disturbed.connect(on_quota_disturbed)
def send_quota_disturbed(sender, instance, **kwargs):
if not instance.is_enabled:
return
quota_disturbed.send(sender=sender, users=users)
-
-
-def on_quota_disturbed(sender, users, **kwargs):
-# print '>>>', locals()
- if not users:
- return
- send_quota(users)
-
-def renew_token(sender, instance, **kwargs):
- if not instance.auth_token:
- instance.renew_token()
-
-post_syncdb.connect(fix_superusers)
-post_save.connect(user_post_save, sender=User)
-pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
-post_save.connect(astakosuser_post_save, sender=AstakosUser)
-post_save.connect(resource_post_save, sender=Resource)
-
-quota_disturbed = Signal(providing_args=["users"])
-quota_disturbed.connect(on_quota_disturbed)
-
post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
post_delete.connect(send_quota_disturbed, sender=Membership)
post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
+
+def renew_token(sender, instance, **kwargs):
+ if not instance.auth_token:
+ instance.renew_token()
pre_save.connect(renew_token, sender=AstakosUser)
pre_save.connect(renew_token, sender=Service)
+
+
+def check_closed_join_membership_policy(sender, instance, **kwargs):
+ if instance.id:
+ return
+ join_policy = instance.project.application.definition.member_join_policy
+ if join_policy == get_closed_join():
+ raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
+pre_save.connect(check_closed_join_membership_policy, sender=ProjectMembership)
+
+
+def check_auto_accept_join_membership_policy(sender, instance, created, **kwargs):
+ if not created:
+ return
+ join_policy = instance.project.application.definition.member_join_policy
+ if join_policy == get_auto_accept_join():
+ instance.accept()
+post_save.connect(check_auto_accept_join_membership_policy, sender=ProjectMembership)
\ No newline at end of file