Commit 7d1e5e78 authored by Sofia Papagiannaki's avatar Sofia Papagiannaki
Browse files

Single model for ProjectApplication & Definition - Membership sync

parent 813c8205
......@@ -38,11 +38,9 @@ from django.utils.translation import ugettext as _
from astakos.im.models import AstakosUser
from astakos.im.util import get_invitation
from astakos.im.functions import (
send_activation, send_account_creation_notification, activate
)
send_activation, send_account_creation_notification, activate)
from astakos.im.settings import (
INVITATIONS_ENABLED, RE_USER_EMAIL_PATTERNS
)
INVITATIONS_ENABLED, RE_USER_EMAIL_PATTERNS)
from astakos.im import settings as astakos_settings
from astakos.im.forms import *
......
......@@ -42,7 +42,8 @@ from django.core.urlresolvers import reverse
from astakos.im.models import AstakosUser, GroupKind, Service, Resource
from astakos.im.api.faults import Fault, ItemNotFound, InternalServerError, BadRequest
from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
from astakos.im.settings import (
INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED, QUOTAHOLDER_URL)
import logging
logger = logging.getLogger(__name__)
......@@ -182,29 +183,30 @@ def get_menu(request, with_extra_links=False, with_signout=True):
url=absolute(request, reverse('invite')),
name="Invitations"))
append(item(
url=absolute(request, reverse('group_list')),
name="Projects",
# submenu=(item(
# url=absolute(request,
# reverse('group_list')),
# name="Overview"),
# item(
# url=absolute(request,
# reverse('group_create_list')),
# name="Create"),
# item(
# url=absolute(request,
# reverse('group_search')),
# name="Join"),
if QUOTAHOLDER_URL:
# append(item(
# url=absolute(request, reverse('group_list')),
# name="Projects",
# # submenu=(item(
# # url=absolute(request,
# # reverse('group_list')),
# # name="Overview"),
# # item(
# # url=absolute(request,
# # reverse('group_create_list')),
# # name="Create"),
# # item(
# # url=absolute(request,
# # reverse('group_search')),
# # name="Join"),
# # )
# )
# )
append(item(
url=absolute(request, reverse('project_list')),
name="New Projects",
)
)
)
append(item(
url=absolute(request, reverse('project_list')),
name="New Projects",
)
)
append(item(
url=absolute(request, reverse('resource_usage')),
name="Usage"))
......
......@@ -41,8 +41,7 @@ from django.utils import simplejson as json
from astakos.im.api.faults import (
Fault, Unauthorized, InternalServerError, BadRequest,
Forbidden
)
Forbidden)
from astakos.im.api import render_fault, _get_user_by_email, _get_user_by_username
from astakos.im.models import AstakosUser
from astakos.im.util import epoch
......
......@@ -39,18 +39,17 @@ from functools import wraps
from smtplib import SMTPException
from astakos.im.models import (
AstakosUser, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR,
Project, ProjectApplication, ProjectMembership, filter_queryset_by_property
)
from astakos.im.api.backends.base import BaseBackend, SuccessResult, FailureResult
AstakosUser,
# AstakosGroup, GroupKind,
Resource, Service, RESOURCE_SEPARATOR,
Project, ProjectApplication, ProjectMembership, filter_queryset_by_property)
from astakos.im.api.backends.base import (
BaseBackend, SuccessResult, FailureResult)
from astakos.im.api.backends.errors import (
ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist
)
# from astakos.im.api.backends.lib.notifications import EmailNotification
ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist)
from astakos.im.util import reserved_email, model_to_dict
from astakos.im.endpoints.qh import get_quota, send_quota
from astakos.im.settings import SITENAME
from astakos.im.endpoints.qh import get_quota
try:
from astakos.im.messages import astakos_messages
except:
......@@ -236,7 +235,7 @@ class DjangoBackend(BaseBackend):
@safe
def get_resource_usage(self, user_id):
user = self._lookup_user(user_id)
c, data = get_quota((user,))
data = get_quota((user,))
resources = []
append = resources.append
for t in data:
......@@ -306,17 +305,17 @@ class DjangoBackend(BaseBackend):
id__in=ids)
q.delete()
@safe
def create_group(self, **kwargs):
policies = kwargs.pop('policies', ())
permissions = kwargs.pop('permissions', ())
members = kwargs.pop('members', ())
owners = kwargs.pop('owners', ())
g = self._create_object(AstakosGroup, **kwargs)
g.permissions = permissions
g.policies = policies
# g.members = members
g.owners = owners
return self._details(g)
\ No newline at end of file
# @safe
# def create_group(self, **kwargs):
# policies = kwargs.pop('policies', ())
# permissions = kwargs.pop('permissions', ())
# members = kwargs.pop('members', ())
# owners = kwargs.pop('owners', ())
#
# g = self._create_object(AstakosGroup, **kwargs)
#
# g.permissions = permissions
# g.policies = policies
# # g.members = members
# g.owners = owners
# return self._details(g)
\ No newline at end of file
......@@ -143,9 +143,9 @@ class AstakosCallpoint():
rejected = b.remove_resources(service_id, ids)
return rejected
def create_groups(self, groups=()):
b = get_backend()
rejected = (b.create_group(**g) for g in groups)
return rejected
# def create_groups(self, groups=()):
# b = get_backend()
# rejected = (b.create_group(**g) for g in groups)
# return rejected
API_Callpoint = AstakosCallpoint
from synnefo.lib.commissioning.specificator import (
Specificator, Integer, Text, ListOf
)
Specificator, Integer, Text, ListOf)
class Name(Text):
......
......@@ -34,8 +34,7 @@
from astakos.im.settings import (
IM_MODULES, INVITATIONS_ENABLED, IM_STATIC_URL,
LOGIN_MESSAGES, SIGNUP_MESSAGES, PROFILE_MESSAGES,
GLOBAL_MESSAGES, PROFILE_EXTRA_LINKS
)
GLOBAL_MESSAGES, PROFILE_EXTRA_LINKS)
from astakos.im.api import get_menu
from astakos.im.util import get_query
from astakos.im.models import GroupKind
......
......@@ -40,8 +40,7 @@ from django.http import HttpRequest
from django.utils.translation import ugettext as _
from astakos.im.settings import (
COOKIE_NAME, COOKIE_DOMAIN, COOKIE_SECURE, LOGGING_LEVEL
)
COOKIE_NAME, COOKIE_DOMAIN, COOKIE_SECURE, LOGGING_LEVEL)
import astakos.im.messages as astakos_messages
......
......@@ -52,32 +52,41 @@ logger = logging.getLogger(__name__)
inf = float('inf')
_client = None
def get_client():
global _client
if _client:
return _client
if not QUOTAHOLDER_URL:
return
_client = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
def call(func_name):
"""Decorator function for Quotaholder client calls."""
def decorator(payload_func):
@wraps(payload_func)
def wrapper(entities=(), client=None, **kwargs):
def wrapper(entities=(), **kwargs):
if not entities:
return client, ()
return ()
if not QUOTAHOLDER_URL:
return client, ()
return ()
c = client or QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
c = get_client()
func = c.__dict__.get(func_name)
if not func:
return c, ()
return ()
data = payload_func(entities, client, **kwargs)
if not data:
return c, data
return data
funcname = func.__name__
kwargs = {'context': {}, funcname: data}
rejected = func(**kwargs)
msg = _('%s: %s - Rejected: %s' % (funcname, data, rejected,))
logger.log(LOGGING_LEVEL, msg)
return c, rejected
return rejected
return wrapper
return decorator
......@@ -153,20 +162,20 @@ def create_entities(entities, client=None, field=''):
def register_users(users, client=None):
users, copy = itertools.tee(users)
client, rejected = create_entities(entities=users,
rejected = create_entities(entities=users,
client=client,
field='email')
created = (e for e in copy if unicode(e) not in rejected)
return send_quota(created, client)
return send_quota(created)
def register_resources(resources, client=None):
resources, copy = itertools.tee(resources)
client, rejected = create_entities(entities=resources,
rejected = create_entities(entities=resources,
client=client,
field='service')
created = (e for e in copy if unicode(e) not in rejected)
return send_resource_quantities(created, client)
return send_resource_quantities(created)
from datetime import datetime
......
This diff is collapsed.
......@@ -41,10 +41,10 @@ from django.core.urlresolvers import reverse
from django.template import Context, loader
from django.contrib.auth import (
login as auth_login,
logout as auth_logout
)
logout as auth_logout)
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import PermissionDenied
from urllib import quote
from urlparse import urljoin
......@@ -57,8 +57,14 @@ from astakos.im.settings import (
VERIFICATION_EMAIL_SUBJECT, ACCOUNT_CREATION_SUBJECT,
GROUP_CREATION_SUBJECT, HELPDESK_NOTIFICATION_EMAIL_SUBJECT,
INVITATION_EMAIL_SUBJECT, GREETING_EMAIL_SUBJECT, FEEDBACK_EMAIL_SUBJECT,
EMAIL_CHANGE_EMAIL_SUBJECT
)
EMAIL_CHANGE_EMAIL_SUBJECT,
PROJECT_CREATION_SUBJECT, PROJECT_APPROVED_SUBJECT,
PROJECT_TERMINATION_SUBJECT, PROJECT_SUSPENSION_SUBJECT,
PROJECT_MEMBERSHIP_CHANGE_SUBJECT)
from astakos.im.notifications import build_notification, NotificationError
from astakos.im.models import (
ProjectMembership, ProjectApplication)
import astakos.im.messages as astakos_messages
logger = logging.getLogger(__name__)
......@@ -257,7 +263,8 @@ def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
logger.log(LOGGING_LEVEL, msg)
def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
def send_change_email(
ec, request, email_template_name='registration/email_change_email.txt'):
try:
url = reverse('email_change_confirm',
kwargs={'activation_key': ec.activation_key})
......@@ -279,8 +286,7 @@ def activate(
user,
email_template_name='im/welcome_email.txt',
helpdesk_email_template_name='im/helpdesk_notification.txt',
verify_email=False
):
verify_email=False):
"""
Activates the specific user and sends email.
......@@ -293,6 +299,12 @@ def activate(
send_helpdesk_notification(user, helpdesk_email_template_name)
send_greeting(user, email_template_name)
def invite(inviter, email, realname):
inv = Invitation(inviter=inviter, username=email, realname=realname)
inv.save()
send_invitation(inv)
inviter.invitations = max(0, self.invitations - 1)
inviter.save()
def switch_account_to_shibboleth(user, local_user,
greeting_template_name='im/welcome_email.txt'):
......@@ -355,3 +367,243 @@ class SendNotificationError(SendMailError):
def __init__(self):
self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
super(SendNotificationError, self).__init__()
### PROJECT VIEWS ###
def get_join_policy(str_policy):
try:
return MemberJoinPolicy.objects.get(policy=str_policy)
except:
return None
def get_leave_policy(str_policy):
try:
return MemberLeavePolicy.objects.get(policy=str_policy)
except:
return None
_auto_accept_join = False
def get_auto_accept_join_policy():
global _auto_accept_join
if _auto_accept_join is not False:
return _auto_accept_join
_auto_accept = get_join_policy('auto_accept')
return _auto_accept
_closed_join = False
def get_closed_join_policy():
global _closed_join
if _closed_join is not False:
return _closed_join
_closed_join = get_join_policy('closed')
return _closed_join
_auto_accept_leave = False
def get_auto_accept_leave_policy():
global _auto_accept_leave
if _auto_accept_leave is not False:
return _auto_accept_leave
_auto_accept_leave = get_leave_policy('auto_accept')
return _auto_accept_leave
_closed_leave = False
def get_closed_leave_policy():
global _closed_leave
if _closed_leave is not False:
return _closed_leave
_closed_leave = get_leave_policy('closed')
return _closed_leave
def get_project_by_application_id(project_application_id):
try:
return Project.objects.get(application__id=project_application_id)
except Project.DoesNotExist:
raise IOError(
_(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) % project_application_id)
def get_user_by_id(user_id):
try:
return AstakosUser.objects.get(user__id=user_id)
except AstakosUser.DoesNotExist:
raise IOError(_(astakos_messages.UNKNOWN_USER_ID) % user_id)
def create_membership(project_application_id, user_id):
try:
project = get_project_by_application_id(project_application_id)
m = ProjectMembership(
project=project,
person__id=user_id,
request_date=datetime.now())
except IntegrityError, e:
raise IOError(_(astakos_messages.MEMBERSHIP_REQUEST_EXISTS))
else:
m.save()
def get_membership(project, user):
if isinstace(project, int):
project = get_project_by_application_id(project)
if isinstace(user, int):
user = get_user_by_id(user)
try:
return ProjectMembership.objects.select_related().get(
project=project,
person=user)
except ProjectMembership.DoesNotExist:
raise IOError(_(astakos_messages.NOT_MEMBERSHIP_REQUEST))
def accept_membership(request, project, user, request_user=None):
"""
Raises:
django.core.exceptions.PermissionDenied
IOError
"""
membership = get_membership(project, user)
if request_user and \
(not membership.project.current_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) % membership.project.__dict__)
if len(self.project.approved_members) + 1 > \
self.project.definition.limit_on_members_number:
raise PermissionDenied(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
membership.accept()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[self.person.email],
_(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % membership.project.definition.__dict__,
template='im/projects/project_membership_change_notification.txt',
dictionary={'object':membership.project.current_application, 'action':'accepted'})
notification.send()
except NotificationError, e:
logger.error(e.messages)
return membership
def reject_membership(project, user, request_user=None):
"""
Raises:
django.core.exceptions.PermissionDenied
IOError
"""
membership = get_membership(project, user)
if request_user and \
(not membership.project.current_application.owner == request_user and \
not request_user.is_superuser):
raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
if not membership.project.is_alive:
raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
membership.reject()
try:
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.current_application, 'action':'rejected'})
notification.send()
except NotificationError, e:
logger.error(e.messages)
return membership
def remove_membership(project, user, request_user=None):
"""
Raises:
django.core.exceptions.PermissionDenied
IOError
"""
membership = get_membership(project, user)
if request_user and \
(not membership.project.current_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) % membership.project.__dict__)
membership.remove()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[self.person.email],
_(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % membership.project.definition.__dict__,
template='im/projects/project_membership_change_notification.txt',
dictionary={'object':membership.project.current_application, 'action':'removed'})
notification.send()
except NotificationError, e:
logger.error(e.messages)
return membership
def leave_project(project_application_id, user_id):
"""
Raises:
django.core.exceptions.PermissionDenied
IOError
"""
project = get_project_by_application_id(project_application_id)
leave_policy = project.current_application.definition.member_join_policy
if leave_policy == get_closed_leave():
raise PermissionDenied(_(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED))
membership = get_membership(project_application_id, user_id)
if leave_policy == get_auto_accept_leave():
membership.remove()
else:
membership.leave_request_date = datetime.now()
membership.save()
return membership
def join_project(project_application_id, user_id):
"""
Raises:
django.core.exceptions.PermissionDenied
IOError
"""
project = get_project_by_application_id(project_application_id)
join_policy = project.current_application.definition.member_join_policy
if join_policy == get_closed_join():
raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
membership = create_membership(project_application_id, user_id)
if join_policy == get_auto_accept_join():
membership.accept()
return membership
def submit_application(
application, resource_policies, applicant, comments, precursor_application=None):
application.submit(
resource_policies, applicant, comments, precursor_application)
try:
notification = build_notification(
settings.SERVER_EMAIL,
[i[1] for i in settings.ADMINS],
_(PROJECT_CREATION_SUBJECT) % application.__dict__,
template='im/projects/project_creation_notification.txt',
dictionary={'object':application})
notification.send()
except NotificationError, e:
logger.error(e.messages)
return application
def approve_application(application):
application.approve()
# rejected = application.project.sync()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[self.owner.email],
_(PROJECT_APPROVED_SUBJECT) % application.definition.__dict__,
template='im/projects/project_approval_notification.txt',
dictionary={'object':application})
notification.send()
except NotificationError, e:
logger.error(e.messages)
......@@ -40,11 +40,11 @@ from django.http import Http404
from astakos.im.models import ProjectApplication
@transaction.commit_manually
class Command(BaseCommand):
args = "<project application id>"
help = "Update project state"
@transaction.commit_manually
def handle(self, *args, **options):
if len(args) < 1:
raise CommandError("Please provide a group identifier")
......
......@@ -81,7 +81,7 @@ class Command(NoArgsCommand):
str(app.id),