Commit 8df59879 authored by Giorgos Korfiatis's avatar Giorgos Korfiatis

astakos: Adapt to new project/app scheme

parent 29fb7398
This diff is collapsed.
......@@ -60,11 +60,6 @@ astakos_account_v1_0 += patterns(
astakos_account_v1_0 += patterns(
'astakos.api.projects',
url(r'^projects/?$', 'projects', name='api_projects'),
url(r'^projects/apps/?$', 'applications', name='api_applications'),
url(r'^projects/apps/(?P<app_id>\d+)/?$', 'application',
name='api_application'),
url(r'^projects/apps/(?P<app_id>\d+)/action/?$', 'application_action',
name='api_application_action'),
url(r'^projects/memberships/?$', 'memberships', name='api_memberships'),
url(r'^projects/memberships/(?P<memb_id>\d+)/?$', 'membership',
name='api_membership'),
......
......@@ -824,7 +824,8 @@ class ProjectApplicationForm(forms.ModelForm):
policies = {}
for d in self.resource_policies:
policies[d["name"]] = {
"project_capacity": None,
### TEMPORARY HACK !!!
"project_capacity": d["uplimit"],
"member_capacity": d["uplimit"]
}
......
This diff is collapsed.
......@@ -35,7 +35,7 @@ from optparse import make_option
from snf_django.management.commands import SynnefoCommand, CommandError
from astakos.im.models import Project
from astakos.im.models import Project, ProjectApplication
from django.db.models import Q
from snf_django.management import utils
from ._common import is_uuid, is_email
......@@ -45,6 +45,9 @@ class Command(SynnefoCommand):
help = """List projects and project status.
Project status can be one of:
Uninitialized an uninitialized project,
with no pending application
Pending an uninitialized project, pending review
Active an active project
......@@ -59,14 +62,11 @@ class Command(SynnefoCommand):
it can later be resumed
Terminated a terminated project; its name can be claimed
by a new project"""
by a new project
Deleted an uninitialized, deleted project"""
option_list = SynnefoCommand.option_list + (
make_option('--all',
action='store_true',
dest='all',
default=False,
help="List all projects (default)"),
make_option('--new',
action='store_true',
dest='new',
......@@ -83,11 +83,11 @@ class Command(SynnefoCommand):
default=False,
help=("Show only projects with a pending application "
"(equiv. --modified --new)")),
make_option('--skip',
make_option('--deleted',
action='store_true',
dest='skip',
dest='deleted',
default=False,
help="Skip cancelled and terminated projects"),
help="Also so cancelled/terminated projects"),
make_option('--name',
dest='name',
help='Filter projects by name'),
......@@ -105,63 +105,50 @@ class Command(SynnefoCommand):
name = options['name']
if name:
flt &= filter_by_name(name)
flt &= Q(realname=name)
chains = Project.objects.all_with_pending(flt)
if not options['deleted']:
flt &= ~Q(state__in=Project.SKIP_STATES)
if not options['all']:
if options['skip']:
pred = lambda c: (
c[0].overall_state() not in Project.SKIP_STATES
or c[1] is not None)
chains = filter_preds([pred], chains)
pending = Q(last_application__isnull=False,
last_application__state=ProjectApplication.PENDING)
preds = []
if options['new'] or options['pending']:
preds.append(
lambda c: c[0].overall_state() == Project.O_PENDING)
if options['modified'] or options['pending']:
preds.append(
lambda c: c[0].overall_state() != Project.O_PENDING
and c[1] is not None)
if options['pending']:
flt &= pending
else:
if options['new']:
flt &= pending & Q(state=Project.UNINITIALIZED)
if options['modified']:
flt &= pending & Q(state__in=Project.INITIALIZED_STATES)
if preds:
chains = filter_preds(preds, chains)
projects = Project.objects.\
select_related("last_application", "owner").filter(flt)
labels = ('ProjID', 'Name', 'Owner', 'Status', 'Pending AppID')
info = chain_info(chains)
info = project_info(projects)
utils.pprint_table(self.stdout, info, labels,
options["output_format"])
def filter_preds(preds, chains):
return [c for c in chains
if any(map(lambda f: f(c), preds))]
def filter_by_name(name):
return Q(application__name=name)
def filter_by_owner(s):
if is_email(s):
return Q(application__owner__email=s)
return Q(owner__email=s)
if is_uuid(s):
return Q(application__owner__uuid=s)
return Q(owner__uuid=s)
raise CommandError("Expecting either email or uuid.")
def chain_info(chains):
def project_info(projects):
l = []
for project, pending_app in chains:
for project in projects:
status = project.state_display()
pending_appid = pending_app.id if pending_app is not None else ""
application = project.application
app = project.last_application
pending_appid = app.id if app and app.state == app.PENDING else ""
t = (project.uuid,
application.name,
application.owner.email,
project.realname,
project.owner.email if project.owner else None,
status,
pending_appid,
)
......
......@@ -39,25 +39,20 @@ from snf_django.management.commands import SynnefoCommand
from snf_django.management import utils
from astakos.im.models import ProjectApplication, Project
from ._common import show_resource_value, style_options, check_style
from synnefo.util import units
class Command(SynnefoCommand):
args = "<id>"
help = "Show details for project (or application) <id>"
help = "Show details for project <id>"
option_list = SynnefoCommand.option_list + (
make_option('--app',
action='store_true',
dest='app',
default=False,
help="Show details of applications instead of projects"
),
make_option('--pending',
action='store_true',
dest='pending',
default=False,
help=("For a given project, show also pending "
"modifications (applications), if any")
"modification, if any")
),
make_option('--members',
action='store_true',
......@@ -80,23 +75,22 @@ class Command(SynnefoCommand):
show_pending = bool(options['pending'])
show_members = bool(options['members'])
search_apps = options['app']
show_quota = bool(options['list_quotas'])
self.output_format = options['output_format']
id_ = args[0]
if search_apps:
app = get_app(id_)
self.print_app(app)
else:
project, pending_app = get_chain_state(id_)
self.print_project(project, pending_app)
if True:
project = get_chain_state(id_)
self.print_project(project)
if show_members and project is not None:
self.stdout.write("\n")
fields, labels = members_fields(project)
self.pprint_table(fields, labels, title="Members")
if show_pending and pending_app is not None:
self.stdout.write("\n")
self.print_app(pending_app)
if show_pending:
app = project.last_application
if app and app.state == ProjectApplication.PENDING:
self.stdout.write("\n")
self.print_app(app)
def pprint_dict(self, d, vertical=True):
utils.pprint_table(self.stdout, [d.values()], d.keys(),
......@@ -109,44 +103,36 @@ class Command(SynnefoCommand):
def print_app(self, app):
app_info = app_fields(app)
self.pprint_dict(app_info)
self.print_resources(app)
self.print_app_resources(app)
def print_project(self, project, app):
if project is None:
self.print_app(app)
else:
self.pprint_dict(project_fields(project, app))
self.print_resources(project.application)
def print_project(self, project):
self.pprint_dict(project_fields(project))
self.print_resources(project)
def print_resources(self, app):
fields, labels = resource_fields(app, self.unit_style)
def print_resources(self, project):
policies = project.projectresourcequota_set.all()
fields, labels = resource_fields(policies, self.unit_style)
if fields:
self.stdout.write("\n")
self.pprint_table(fields, labels, title="Resource limits")
def get_app(app_id):
try:
app_id = int(app_id)
except ValueError:
raise CommandError("id should be an integer value.")
try:
return ProjectApplication.objects.get(id=app_id)
except ProjectApplication.DoesNotExist:
raise CommandError("Application with id %s not found." % app_id)
def print_app_resources(self, app):
policies = app.projectresourcegrant_set.all()
fields, labels = resource_fields(policies, None, self.unit_style)
if fields:
self.stdout.write("\n")
self.pprint_table(fields, labels, title="Resource limits")
def get_chain_state(project_id):
try:
chain = Project.objects.get(uuid=project_id)
return chain, chain.last_pending_application()
return Project.objects.get(uuid=project_id)
except Project.DoesNotExist:
raise CommandError("Project with id %s not found." % project_id)
def resource_fields(app, style):
def resource_fields(policies, style):
labels = ('name', 'description', 'max per member')
policies = app.projectresourcegrant_set.all()
collect = []
for policy in policies:
name = policy.resource.name
......@@ -158,64 +144,58 @@ def resource_fields(app, style):
def app_fields(app):
mem_limit = app.limit_on_members_number
mem_limit_show = mem_limit if mem_limit is not None else "unlimited"
d = OrderedDict([
('project id', app.chain.uuid),
('application id', app.id),
('name', app.name),
('status', app.state_display()),
('owner', app.owner),
('applicant', app.applicant),
('homepage', app.homepage),
('description', app.description),
('comments for review', app.comments),
('request issue date', app.issue_date),
('request start date', app.start_date),
('request end date', app.end_date),
('join policy', app.member_join_policy_display),
('leave policy', app.member_leave_policy_display),
('max members', mem_limit_show),
])
])
if app.name:
d['name'] = app.name
if app.owner:
d['owner'] = app.owner
if app.homepage:
d['homepage'] = app.homepage
if app.description:
d['description'] = app.description
if app.start_date:
d['request start date'] = app.start_date
if app.end_date:
d['request end date'] = app.end_date
if app.member_join_policy:
d['join policy'] = app.member_join_policy_display
if app.member_leave_policy:
d['leave policy'] = app.member_leave_policy_display
if app.limit_on_members_number:
d['max members'] = units.show(app.limit_on_members_number, None)
return d
def project_fields(project, pending_app):
app = project.application
def project_fields(project):
app = project.last_application
d = OrderedDict([
('project id', project.uuid),
('application id', app.id),
('name', app.name),
('name', project.realname),
('status', project.state_display()),
])
if pending_app is not None:
d.update([('pending application', pending_app.id)])
d.update([('owner', app.owner),
('applicant', app.applicant),
('homepage', app.homepage),
('description', app.description),
('comments for review', app.comments),
('request issue date', app.issue_date),
('request start date', app.start_date),
('creation date', project.creation_date),
('request end date', app.end_date),
])
('owner', project.owner),
('homepage', project.homepage),
('description', project.description),
('creation date', project.creation_date),
('request end date', project.end_date),
])
deact = project.last_deactivation()
if deact is not None:
d['deactivation date'] = deact.date
mem_limit = app.limit_on_members_number
mem_limit_show = mem_limit if mem_limit is not None else "unlimited"
d.update([
('join policy', app.member_join_policy_display),
('leave policy', app.member_leave_policy_display),
('max members', mem_limit_show),
('join policy', project.member_join_policy_display),
('leave policy', project.member_leave_policy_display),
('max members', units.show(project.limit_on_members_number, None)),
('total members', project.members_count()),
])
......
......@@ -162,27 +162,28 @@ def memberships(user):
for m in ms:
project = m.project
print_data.append((project.uuid,
project.application.name,
project.realname,
m.state_display(),
))
return print_data, labels
def ownerships(user):
chains = Project.objects.all_with_pending(Q(application__owner=user))
chains = Project.objects.select_related("last_application").\
filter(owner=user)
return chain_info(chains)
def chain_info(chains):
labels = ('project id', 'project name', 'status', 'pending app id')
l = []
for project, pending_app in chains:
for project in chains:
status = project.state_display()
pending_appid = pending_app.id if pending_app is not None else ""
application = project.application
app = project.last_application
pending_appid = app.id if app and app.state == app.PENDING else ""
t = (project.uuid,
application.name,
project.realname,
status,
pending_appid,
)
......
This diff is collapsed.
......@@ -93,7 +93,7 @@ def membership_request_notify(project, requested_user, action):
subject, template = MEMBERSHIP_REQUEST_DATA[action](project)
try:
build_notification(
SENDER, [project.application.owner.email], subject,
SENDER, [project.owner.email], subject,
template=template,
dictionary={'object': project, 'user': requested_user.email}
).send()
......@@ -131,7 +131,7 @@ def application_notify(application, action):
PROJECT_DATA = {
"terminate": lambda p: (
_(messages.PROJECT_TERMINATION_SUBJECT) % p.application.__dict__,
_(messages.PROJECT_TERMINATION_SUBJECT) % p.__dict__,
"im/projects/project_termination_notification.txt"),
"reinstate": lambda p: (
_(messages.PROJECT_REINSTATEMENT_SUBJECT) % p.__dict__,
......@@ -149,7 +149,7 @@ def project_notify(project, action):
subject, template = PROJECT_DATA[action](project)
try:
build_notification(
SENDER, [project.application.owner.email], subject,
SENDER, [project.owner.email], subject,
template=template,
dictionary={'object': project}
).send()
......
......@@ -34,7 +34,7 @@
from synnefo.util import units
from astakos.im.models import (
Resource, AstakosUserQuota, AstakosUser, Service,
Project, ProjectMembership, ProjectResourceGrant, ProjectApplication)
Project, ProjectMembership, ProjectResourceQuota, ProjectApplication)
import astakos.quotaholder_app.callpoint as qh
from astakos.quotaholder_app.exception import NoCapacityError
from django.db.models import Q
......@@ -195,26 +195,25 @@ def astakos_users_quotas(users, resource=None):
userids = [user.pk for user in users]
ACTUALLY_ACCEPTED = ProjectMembership.ACTUALLY_ACCEPTED
objs = ProjectMembership.objects.select_related(
'project', 'person', 'project__application')
'project', 'person')
memberships = objs.filter(
person__pk__in=userids,
state__in=ACTUALLY_ACCEPTED,
project__state=Project.NORMAL,
project__application__state=ProjectApplication.APPROVED)
)
apps = set(m.project.application_id for m in memberships)
projs = set(m.project_id for m in memberships)
objs = ProjectResourceGrant.objects.select_related()
grants = objs.filter(project_application__in=apps).filter(flt)
objs = ProjectResourceQuota.objects.select_related()
grants = objs.filter(project__in=projs).filter(flt)
for membership in memberships:
uuid = membership.person.uuid
userquotas = quotas.get(uuid, {})
application = membership.project.application
proj = membership.project
for grant in grants:
if grant.project_application_id != application.id:
if grant.project_id != proj.id:
continue
source = get_grant_source(grant)
......@@ -312,6 +311,10 @@ def qh_sync_project(project):
qh_sync_users(users)
def pick_limit_scheme(project, resource):
return resource.uplimit if project.is_base else resource.project_default
def qh_sync_new_resource(resource):
users = AstakosUser.objects.filter(
moderated=True, is_rejected=False).order_by('id').select_for_update()
......
......@@ -225,9 +225,9 @@ class UserTable(tables.Table):
def project_name_append(project, column):
pending_apps = column.table.pending_apps
app = pending_apps.get(project.id)
if app and app.id != project.application_id:
if project.state != project.UNINITIALIZED and \
project.last_application is not None and \
project.last_application.state == ProjectApplication.PENDING:
return mark_safe("<br /><i class='tiny'>%s</i>" %
_('modifications pending'))
return u''
......@@ -250,19 +250,16 @@ class UserProjectsTable(UserTable):
append=project_name_append,
args=(A('id'),),
orderable=False,
accessor='application.name')
issue_date = tables.DateColumn(verbose_name=_('Application'),
format=DEFAULT_DATE_FORMAT,
orderable=False,
accessor='application.issue_date')
start_date = tables.DateColumn(format=DEFAULT_DATE_FORMAT,
orderable=False,
accessor='application.start_date')
accessor='realname')
creation_date = tables.DateColumn(verbose_name=_('Application'),
format=DEFAULT_DATE_FORMAT,
orderable=False,
accessor='creation_date')
end_date = tables.DateColumn(verbose_name=_('Expiration'),
format=DEFAULT_DATE_FORMAT,
orderable=False,
accessor='application.end_date')
accessor='end_date')
members_count_f = tables.Column(verbose_name=_("Members"),
empty_values=(),
orderable=False)
......@@ -313,12 +310,11 @@ class UserProjectsTable(UserTable):
return mark_safe(str(members_count) + append)
class Meta:
sequence = ('name', 'membership_status', 'issue_date', 'end_date',
sequence = ('name', 'membership_status', 'creation_date', 'end_date',
'members_count_f', 'project_action')
attrs = {'id': 'projects-list', 'class': 'my-projects alt-style'}
template = "im/table_render.html"
empty_text = _('No projects')
exclude = ('start_date', )
def member_action_extra_context(membership, table, col):
......
......@@ -10,9 +10,9 @@
<a class="owner-action" href="{% url astakos.im.views.project_modify object.pk %}">MODIFY</a>
{% if owner_mode %}
{% with project.last_pending_application as last_pending %}
{% with object.last_pending_application as last_pending %}
{% if last_pending != None %}
{% if project.is_initialized %}
{% if object.is_initialized %}
- {% confirm_link "CANCEL PROJECT MODIFICATION" "project_modification_cancel" "project_app_cancel" last_pending.pk "" "OK" %}
{% else %}
- {% confirm_link "CANCEL PROJECT APPLICATION" "project_app_cancel" "project_app_cancel" last_pending.pk "" "OK" %}
......@@ -41,7 +41,7 @@
{% endif %}
{% if can_join_request %}
{% confirm_link "JOIN" "project_join" "project_join" project.pk %}
{% confirm_link "JOIN" "project_join" "project_join" object.pk %}
{% endif %}
{% if can_leave_request %}
......
<form action="{% url project_detail object.chain_id %}#members-table"
<form action="{% url project_detail object.id %}#members-table"
method="post" class="withlabels upperlabels" id="members-table" >
{% csrf_token %}
{% with addmembers_form as form %}
......
......@@ -3,14 +3,12 @@
{% load astakos_tags filters django_tables2 %}
{% block page.body %}
{% with object.chain as project %}
<div class="projects">
<h2>
<em>
{% if owner_mode or admin_mode %}
{% if project_view %}
PROJECT {{ project.state_display|upper }}
{% with project.last_pending_modification as last_pending %}
PROJECT {{ object.state_display|upper }}
{% with object.last_pending_modification as last_pending %}
{% if last_pending != None %} -
<a href="{% url astakos.im.views.project_app last_pending.pk %}">
MODIFICATION PENDING</a>
......@@ -22,33 +20,21 @@
{% endif %}
{% endif %}
{% endwith %}
{% else %}
<!-- application view -->
PROJECT {% if object.is_modification %} MODIFICATION {% endif %}
{{ object.state_display|upper }}
{% endif %}
{% else %}
<!-- third user -->
<!-- assert in project view -->
{% if project.is_deactivated %}
PROJECT {{ project.state_display|upper }} -
{% if object.is_deactivated %}
PROJECT {{ object.state_display|upper }} -
{% endif %}
{{ mem_display|upper }}
{% endif %}
</em>
<span>
{% if not project_view %}
<!-- owner mode only assumed -->
{% if object.is_modification %}
<span class="extratitle">MODIFICATION OF </span>
{% endif %}
{% endif %}
{{ object.name|upper }}
</span>
{% block project.actions %}
{% block object.actions %}
{% include "im/projects/_project_detail_actions.html" %}
{% endblock %}
</h2>
......@@ -57,7 +43,7 @@
<h3>PROJECT DETAILS</h3>
<dl class="alt-style">
<dt>Name</dt>
<dd>{{ object.name }}&nbsp;</dd>
<dd>{{ object.realname }}&nbsp;</dd>
<dt>Homepage url</dt>
<dd>
{% if object.homepage %}
......@@ -70,20 +56,13 @@
<dd>{{ object.description }}&nbsp;</dd>