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

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.
This diff is collapsed.
......@@ -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),
app.state,
str(project_id),
app.definition.name,
app.name,
format_bool(is_active),
format_bool(is_alive),
format_bool(is_suspended),
......
......@@ -39,8 +39,7 @@ from django.views.generic.create_update import lookup_object
from django.http import Http404
from astakos.im.models import (
ProjectApplication, Project, PENDING
)
ProjectApplication, Project)
@transaction.commit_on_success
class Command(BaseCommand):
......
# Copyright 2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import socket
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
from astakos.im.models import AstakosUser
from astakos.im.api.callpoint import AstakosCallpoint
def filter_custom_options(options):
base_dests = list(
getattr(o, 'dest', None) for o in BaseCommand.option_list)
return dict((k, v) for k, v in options.iteritems() if k not in base_dests)
class Command(BaseCommand):
args = "<user ID>"
help = "Remove a user"
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Invalid number of arguments")
id = args[0]
if not id.isdigit():
raise CommandError('ID must me an integer')
try:
user = AstakosUser.objects.get(id=int(id))
except:
msg = "Unknown user with id '%s'" % id
raise CommandError(msg)
else:
user.delete()
self.stdout.write('User deleted successfully\n')
\ No newline at end of file
......@@ -145,5 +145,9 @@ MEMBERSHIP_REQUEST_EXISTS = 'There is alreary such a membership
NO_APPLICANT = 'Project application requires an applicant. None found.'
ADD_PROJECT_MEMBERS_Q_HELP = 'Add comma separated user emails, eg. user1@user.com, user2@user.com'
MISSING_IDENTIFIER = 'Missing identifier.'
UNKNOWN_USER_ID = 'There is no user identified by %s.'
UNKNOWN_PROJECT_APPLICATION_ID = 'There is no project application identified by %s.'
UNKNOWN_IDENTIFIER = 'Unknown identidier.'
PENDING_MEMBERSHIP_LEAVE = 'Your request is pending acception.'
\ No newline at end of file
PENDING_MEMBERSHIP_LEAVE = 'Your request is pending acception.'
USER_JOINED_PROJECT = '%(realname)s has been successfully joined the project.'
USER_LEFT_PROJECT = '%(realname)s has been successfully left the project.'
......@@ -8,7 +8,7 @@ class Migration(DataMigration):
def forwards(self, orm):
for user in orm.AstakosUser.objects.all():
if user.provider != 'local':
if user.provider and user.provider != 'local':
orm.AstakosUserAuthProvider.objects.create(user=user,
module=user.provider,
identifier=user.third_party_identifier)
......
This diff is collapsed.
......@@ -46,17 +46,14 @@ import astakos.im.messages as astakos_messages
logger = logging.getLogger(__name__)
def build_notification(
sender, recipients, subject, message=None, template=None, dictionary=None
):
sender, recipients, subject, message=None, template=None, dictionary=None):
return EmailNotification(
sender, recipients, subject, message, template, dictionary
)
sender, recipients, subject, message, template, dictionary)
class Notification(object):
def __init__(
self, sender, recipients, subject,
message=None, template=None, dictionary=None
):
message=None, template=None, dictionary=None):
if not message and not template:
raise InputError('message and template cannot be both None.')
dictionary = dictionary or {}
......@@ -75,8 +72,7 @@ class EmailNotification(Notification):
self.subject,
self.message,
self.sender,
self.recipients
)
self.recipients)
except (SMTPException, socket.error), e:
logger.exception(e)
raise NotificationError()
......
......@@ -43,8 +43,7 @@ installed_apps = [
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
# 'djcelery',
'debug_toolbar',
# 'debug_toolbar',
]
context_processors = [
......@@ -69,7 +68,7 @@ middlware_classes = [
'synnefo.lib.middleware.LoggingConfigMiddleware',
'synnefo.lib.middleware.SecureMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
]
loggers = {
......@@ -92,9 +91,6 @@ CUSTOM_USER_MODEL = 'astakos.im.AstakosUser'
#SOUTH_TESTS_MIGRATE = False
import djcelery
djcelery.setup_loader()
BROKER_URL = ''
INTERNAL_IPS = ('127.0.0.1',)
# INTERNAL_IPS = ('127.0.0.1',)
......@@ -36,8 +36,7 @@ from django.utils.translation import ugettext as _
from django.utils.http import urlencode
from django.contrib.auth import authenticate
from django.http import (
HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
)
HttpResponse, HttpResponseBadRequest, HttpResponseForbidden)
from django.core.exceptions import ValidationError
from django.views.decorators.http import require_http_methods
......
......@@ -44,8 +44,8 @@ from django.shortcuts import get_object_or_404
from urlparse import urlunsplit, urlsplit
from astakos.im.util import prepare_response, get_context
from astakos.im.views import requires_anonymous, render_response, \
requires_auth_provider
from astakos.im.views import (
requires_anonymous, render_response, requires_auth_provider)
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
from astakos.im.models import AstakosUser, PendingThirdPartyUser
from astakos.im.forms import LoginForm
......@@ -74,8 +74,8 @@ class Tokens:
def login(
request,
template='im/third_party_check_local.html',
extra_context=None
):
extra_context=None):
extra_context = extra_context or {}
tokens = request.META
......
......@@ -44,8 +44,8 @@ from django.shortcuts import get_object_or_404
from urlparse import urlunsplit, urlsplit
from astakos.im.util import prepare_response, get_context
from astakos.im.views import requires_anonymous, render_response, \
requires_auth_provider
from astakos.im.views import (
requires_anonymous, render_response, requires_auth_provider)
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
from astakos.im.models import AstakosUser, PendingThirdPartyUser
from astakos.im.forms import LoginForm
......@@ -89,8 +89,7 @@ def login(request):
def authenticated(
request,
template='im/third_party_check_local.html',
extra_context={}
):
extra_context={}):
if not 'request_token' in request.session:
messages.error(request, 'Twitter handshake failed')
......
......@@ -39,8 +39,7 @@ from functools import wraps
from astakos.im.endpoints.qh import send_quota
from astakos.im.endpoints.aquarium.producer import (
report_credits_event,
report_user_event
)
report_user_event)
from astakos.im.endpoints.aquarium.client import AquariumClient
import logging
......
......@@ -15,7 +15,7 @@ Maximum participant number: {{object.definition.limit_on_members_number}}
Precursor: {{object.precursor_application.id}}
Policies:
{% for p in object.definition.projectresourcegrant_set.all %}
{{p.resource}}: uplimit:{% if p.member_limit %}{{p.member_limit}}{% else %}inf{% endif %}
{{p.resource}}: uplimit:{% if p.member_capacity %}{{p.member_capacity}}{% else %}inf{% endif %}
{% endfor %}
Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο:
......@@ -38,7 +38,7 @@ Maximum participant number: {{object.definition.limit_on_members_number}}
Precursor: {{object.precursor_application.id}}
Policies:
{% for p in object.definition.projectresourcegrant_set.all %}
{{p.resource}}: uplimit:{% if p.member_limit %}{{p.member_limit}}{% else %}inf{% endif %}
{{p.resource}}: uplimit:{% if p.member_capacity %}{{p.member_capacity}}{% else %}inf{% endif %}
{% endfor %}
For approving it you can use the command line tool:
......
......@@ -24,7 +24,7 @@
{% endif %}
{% endif %}
</em>
<span>{{ object.definition.name|upper }}</span>
<span>{{ object.name|upper }}</span>
</h2>
<!--
......@@ -37,7 +37,7 @@
</h3>
<div class="data">
<p class="restricted">{{ object.definition.description|safe }}</p>
<p class="restricted">{{ object.description|safe }}</p>
<dl class="alt-style">
</dl>
</div>
......@@ -58,31 +58,31 @@
<h3>DETAILS</h3>
<dl class="alt-style">
<dt>Name</dt>
<dd>{{ object.definition.name }}&nbsp;</dd>
<dd>{{ object.name }}&nbsp;</dd>
<dt>Description</dt>
<dd>{{ object.definition.description|safe }}&nbsp;</dd>
<dd>{{ object.description|safe }}&nbsp;</dd>
<dt>Homepage url</dt>
<dd>
{% if object.definition.homepage%}
<a href="{{ object.definition.homepage }}">{{ object.definition.homepage }}</a>
{% if object.homepage%}
<a href="{{ object.homepage }}">{{ object.homepage }}</a>
{% else %}
Not set yet
{% endif %}
</dd>
<dt>Member join policy</dt>
<dd>
{{ object.definition.member_join_policy }}
{{ object.member_join_policy }}
</dd>
<dt>Member leave policy</dt>
<dd>
{{ object.definition.member_leave_policy }}
{{ object.member_leave_policy }}
</dd>
<dt>Issue date:</dt>
<dd>{{object.issue_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>Start date:</dt>
<dd>{{object.definition.start_date|date:"d/m/Y"}}&nbsp;</dd>
<dd>{{object.start_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>End Date</dt>
<dd>{{object.definition.end_date|date:"d/m/Y"}}&nbsp;</dd>
<dd>{{object.end_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>Status</dt>
<dd>{{ object.state }}</dd>
<dt>Owner</dt>
......@@ -94,7 +94,7 @@
&nbsp;
</dd>
<dt>Max participants</dt>
<dd>{% if object.definition.limit_on_members_number%}{{object.definition.limit_on_members_number}}{% else %}&nbsp;{% endif %}</dd>
<dd>{% if object.limit_on_members_number%}{{object.limit_on_members_number}}{% else %}&nbsp;{% endif %}</dd>
<dt>Precursor Application</dt>
<dd>
{% if object.precursor_application %}
......@@ -113,14 +113,14 @@
</div>
<div class="full-dotted">
<h3>RESOURCES</h3>
{% if object.definition.projectresourcegrant_set.all %}
{% if object.projectresourcegrant_set.all %}
<dl class="alt-style">
{% for rp in object.definition.projectresourcegrant_set.all %}
{% for rp in object.projectresourcegrant_set.all %}
<dt>
{{rp.resource}}
</dt>
<dd>
{{rp.member_limit}}
{{rp.member_capacity}}
<dd/>
<!--
<dt>
......@@ -236,10 +236,10 @@
{% endif %}
<div class="full-dotted">
<p>
<a href="{% url group_all %}">back to All Projects &gt;</a></li>
<a href="{% url project_all %}">back to All Projects &gt;</a></li>
</p>
<p>
<a href="{% url group_list %}">back to My Projects &gt;</a>
<a href="{% url project_list %}">back to My Projects &gt;</a>
</p>
</ul>
</div>
......
......@@ -216,7 +216,7 @@ def resource_grants(project_definition):
grants = grants.values_list(
'resource__name',
'resource__service__name',
'member_limit'