Revision 3e3743f2
b/snf-astakos-app/astakos/im/functions.py | ||
---|---|---|
67 | 67 |
from astakos.im.notifications import build_notification, NotificationError |
68 | 68 |
from astakos.im.models import ( |
69 | 69 |
AstakosUser, ProjectMembership, ProjectApplication, Project, |
70 |
sync_projects, PendingMembershipError, get_resource_names) |
|
71 |
from astakos.im.models import submit_application as models_submit_application |
|
70 |
sync_projects, PendingMembershipError, get_resource_names, new_chain) |
|
72 | 71 |
from astakos.im.project_notif import ( |
73 | 72 |
membership_change_notify, |
74 | 73 |
application_submit_notify, application_approve_notify, |
... | ... | |
624 | 623 |
def submit_application(kw, request_user=None): |
625 | 624 |
|
626 | 625 |
kw['applicant'] = request_user |
626 |
resource_policies = kw.pop('resource_policies', None) |
|
627 | 627 |
|
628 |
precursor = None |
|
628 | 629 |
precursor_id = kw.get('precursor_application', None) |
629 | 630 |
if precursor_id is not None: |
630 | 631 |
sfu = ProjectApplication.objects.select_for_update() |
631 | 632 |
precursor = sfu.get(id=precursor_id) |
632 | 633 |
kw['precursor_application'] = precursor |
633 | 634 |
|
634 |
if request_user and \ |
|
635 |
(not precursor.owner == request_user and \ |
|
636 |
not request_user.is_superuser): |
|
637 |
raise PermissionDenied(_(astakos_messages.NOT_ALLOWED)) |
|
635 |
if (request_user and |
|
636 |
(not precursor.owner == request_user and |
|
637 |
not request_user.is_superuser)): |
|
638 |
m = _(astakos_messages.NOT_ALLOWED) |
|
639 |
raise PermissionDenied(m) |
|
638 | 640 |
|
639 |
application = models_submit_application(**kw)
|
|
641 |
application = ProjectApplication(**kw)
|
|
640 | 642 |
|
643 |
if precursor is None: |
|
644 |
application.chain = new_chain() |
|
645 |
else: |
|
646 |
chain = precursor.chain |
|
647 |
application.chain = chain |
|
648 |
sfu = ProjectApplication.objects.select_for_update() |
|
649 |
pending = sfu.filter(chain=chain, state=ProjectApplication.PENDING) |
|
650 |
for app in pending: |
|
651 |
app.state = ProjectApplication.REPLACED |
|
652 |
app.save() |
|
653 |
|
|
654 |
application.save() |
|
655 |
application.resource_policies = resource_policies |
|
641 | 656 |
application_submit_notify(application) |
642 | 657 |
return application |
643 | 658 |
|
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
1231 | 1231 |
|
1232 | 1232 |
def user_visible_by_chain(self, flt): |
1233 | 1233 |
model = self.model |
1234 |
pending = self.filter(model.Q_PENDING).values_list('chain') |
|
1234 |
pending = self.filter(model.Q_PENDING | model.Q_DENIED).values_list('chain')
|
|
1235 | 1235 |
approved = self.filter(model.Q_APPROVED).values_list('chain') |
1236 | 1236 |
by_chain = dict(pending.annotate(models.Max('id'))) |
1237 | 1237 |
by_chain.update(approved.annotate(models.Max('id'))) |
... | ... | |
1326 | 1326 |
# Compiled queries |
1327 | 1327 |
Q_PENDING = Q(state=PENDING) |
1328 | 1328 |
Q_APPROVED = Q(state=APPROVED) |
1329 |
Q_DENIED = Q(state=DENIED) |
|
1329 | 1330 |
|
1330 | 1331 |
class Meta: |
1331 | 1332 |
unique_together = ("chain", "id") |
... | ... | |
1396 | 1397 |
uplimit = p.get('uplimit', 0) |
1397 | 1398 |
self.add_resource_policy(service, resource, uplimit) |
1398 | 1399 |
|
1399 |
def followers(self):
|
|
1400 |
followers = self.chained_applications()
|
|
1401 |
followers = followers.exclude(id=self.pk).filter(state=self.PENDING)
|
|
1402 |
followers = followers.order_by('id')
|
|
1403 |
return followers
|
|
1400 |
def pending_modifications(self):
|
|
1401 |
q = self.chained_applications()
|
|
1402 |
q = q.filter(~Q(id=self.id) & Q(state=self.PENDING))
|
|
1403 |
q = q.order_by('id')
|
|
1404 |
return q
|
|
1404 | 1405 |
|
1405 |
def last_follower(self):
|
|
1406 |
def last_pending(self):
|
|
1406 | 1407 |
try: |
1407 |
return self.followers().order_by('-id')[0]
|
|
1408 |
return self.pending_modifications().order_by('-id')[0]
|
|
1408 | 1409 |
except IndexError: |
1409 | 1410 |
return None |
1410 | 1411 |
|
1411 | 1412 |
def is_modification(self): |
1413 |
if self.state != self.PENDING: |
|
1414 |
return False |
|
1412 | 1415 |
parents = self.chained_applications().filter(id__lt=self.id) |
1413 | 1416 |
parents = parents.filter(state__in=[self.APPROVED]) |
1414 | 1417 |
return parents.count() > 0 |
... | ... | |
1417 | 1420 |
return ProjectApplication.objects.filter(chain=self.chain) |
1418 | 1421 |
|
1419 | 1422 |
def has_pending_modifications(self): |
1420 |
return bool(self.last_follower())
|
|
1423 |
return bool(self.last_pending())
|
|
1421 | 1424 |
|
1422 | 1425 |
def get_project(self): |
1423 | 1426 |
try: |
... | ... | |
1530 | 1533 |
def member_leave_policy_display(self): |
1531 | 1534 |
return PROJECT_MEMBER_LEAVE_POLICIES.get(str(self.member_leave_policy)) |
1532 | 1535 |
|
1533 |
def submit_application(**kw): |
|
1534 |
|
|
1535 |
resource_policies = kw.pop('resource_policies', None) |
|
1536 |
application = ProjectApplication(**kw) |
|
1537 |
|
|
1538 |
precursor = kw['precursor_application'] |
|
1539 |
|
|
1540 |
if precursor is None: |
|
1541 |
application.chain = new_chain() |
|
1542 |
else: |
|
1543 |
application.chain = precursor.chain |
|
1544 |
if precursor.state == ProjectApplication.PENDING: |
|
1545 |
precursor.state = ProjectApplication.REPLACED |
|
1546 |
precursor.save() |
|
1547 |
|
|
1548 |
application.save() |
|
1549 |
application.resource_policies = resource_policies |
|
1550 |
return application |
|
1551 |
|
|
1552 | 1536 |
class ProjectResourceGrant(models.Model): |
1553 | 1537 |
|
1554 | 1538 |
resource = models.ForeignKey(Resource) |
b/snf-astakos-app/astakos/im/tables.py | ||
---|---|---|
237 | 237 |
|
238 | 238 |
super(UserTable, self).__init__(*args, **kwargs) |
239 | 239 |
|
240 |
|
|
241 | 240 |
def project_name_append(application, column): |
242 | 241 |
if application.has_pending_modifications(): |
243 | 242 |
return mark_safe("<br /><i class='tiny'>%s</i>" % \ |
244 |
_('modifications pending'))
|
|
243 |
_('modifications pending')) |
|
245 | 244 |
return u'' |
246 | 245 |
|
247 | 246 |
# Table classes |
... | ... | |
332 | 331 |
confirm=confirms[i])) |
333 | 332 |
return context |
334 | 333 |
|
335 |
class ProjectApplicationMembersTable(UserTable):
|
|
334 |
class ProjectMembersTable(UserTable): |
|
336 | 335 |
name = tables.Column(accessor="person.last_name", verbose_name=_('Name')) |
337 | 336 |
status = tables.Column(accessor="state", verbose_name=_('Status')) |
338 | 337 |
project_action = RichLinkColumn(verbose_name=_('Action'), |
... | ... | |
342 | 341 |
|
343 | 342 |
def __init__(self, project, *args, **kwargs): |
344 | 343 |
self.project = project |
345 |
super(ProjectApplicationMembersTable, self).__init__(*args, **kwargs)
|
|
344 |
super(ProjectMembersTable, self).__init__(*args, **kwargs) |
|
346 | 345 |
if not self.user.owns_project(self.project): |
347 | 346 |
self.exclude = ('project_action', ) |
348 | 347 |
|
b/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html | ||
---|---|---|
11 | 11 |
<em> |
12 | 12 |
{% if user_owns_project %} |
13 | 13 |
[ PROJECT {% if object.is_modification %} MODIFICATION {% endif %}{{ object.state_display|upper }} |
14 |
{% if object.last_follower %} -
|
|
14 |
{% if object.has_pending_modifications %} -
|
|
15 | 15 |
<a |
16 |
href="{% url astakos.im.views.project_detail object.last_follower.pk %}">MODIFICATION PENDING</a>
|
|
16 |
href="{% url astakos.im.views.project_app object.last_pending.pk %}">MODIFICATION PENDING</a>
|
|
17 | 17 |
{% endif %}] |
18 | 18 |
{% else %} |
19 | 19 |
{% if user in approved_members %} |
... | ... | |
33 | 33 |
<span>{% if object.is_modification %} |
34 | 34 |
<span class="extratitle">MODIFICATION OF </span>{% endif %}{{ object.name|upper }}</span> |
35 | 35 |
{% if user_owns_project %} |
36 |
{% if object.last_follower %} |
|
37 |
<br /><a style="font-size:0.7em" href="{% url astakos.im.views.project_update object.pk %}">REQUEST MODIFICATION</a> |
|
38 |
{% else %} |
|
39 |
<br /><a style="font-size:0.7em" href="{% url astakos.im.views.project_update object.pk %}">MODIFY</a> |
|
40 |
{% endif %} |
|
36 |
<br /><a style="font-size:0.7em" href="{% url astakos.im.views.project_modify object.pk %}">MODIFY</a> |
|
41 | 37 |
{% else %} |
42 | 38 |
{% if member_status == -1 %} |
43 | 39 |
- |
... | ... | |
93 | 89 |
<dt>Precursor Application</dt> |
94 | 90 |
<dd> |
95 | 91 |
{% if object.precursor_application %} |
96 |
<a href="{% url project_detail object.precursor_application.id %}">{{object.precursor_application.id}}</a>
|
|
92 |
<a href="{% url project_app object.precursor_application.id %}">{{object.precursor_application.id}}</a>
|
|
97 | 93 |
{% endif %} |
98 | 94 |
|
99 | 95 |
</dd> |
100 | 96 |
<dt>Follower Application</dt> |
101 | 97 |
<dd> |
102 | 98 |
{% if object.follower %} |
103 |
<a href="{% url project_detail object.follower.id %}">{{object.follower.id}}</a>
|
|
99 |
<a href="{% url project_app object.follower.id %}">{{object.follower.id}}</a>
|
|
104 | 100 |
{% endif %} |
105 | 101 |
|
106 | 102 |
</dd> |
b/snf-astakos-app/astakos/im/urls.py | ||
---|---|---|
59 | 59 |
# url(r'^timeline/?$', 'timeline', {}, name='timeline'), |
60 | 60 |
|
61 | 61 |
url(r'^projects/add/?$', 'project_add', {}, name='project_add'), |
62 |
url(r'^projects/update/(?P<application_id>\d+)/?$', 'project_update', {}, name='project_update'), |
|
63 | 62 |
url(r'^projects/?$', 'project_list', {}, name='project_list'), |
64 | 63 |
url(r'^projects/search/?$', 'project_search', {}, name='project_search'), |
65 | 64 |
url(r'^projects/(?P<chain_id>\d+)/?$', 'project_detail', {}, name='project_detail'), |
... | ... | |
68 | 67 |
url(r'^projects/(?P<chain_id>\d+)/(?P<user_id>\d+)/accept/?$', 'project_accept_member', {}, name='project_accept_member'), |
69 | 68 |
url(r'^projects/(?P<chain_id>\d+)/(?P<user_id>\d+)/reject/?$', 'project_reject_member', {}, name='project_reject_member'), |
70 | 69 |
url(r'^projects/(?P<chain_id>\d+)/(?P<user_id>\d+)/remove/?$', 'project_remove_member', {}, name='project_remove_member'), |
70 |
url(r'^projects/app/(?P<application_id>\d+)/?$', 'project_app', {}, name='project_app'), |
|
71 |
url(r'^projects/app/(?P<application_id>\d+)/modify$', 'project_modify', {}, name='project_modify'), |
|
71 | 72 |
|
72 | 73 |
url(r'^projects/how_it_works/?$', 'how_it_works', {}, name='how_it_works'), |
73 | 74 |
url(r'^remove_auth_provider/(?P<pk>\d+)?$', 'remove_auth_provider', {}, name='remove_auth_provider'), |
b/snf-astakos-app/astakos/im/views.py | ||
---|---|---|
1095 | 1095 |
@require_http_methods(["GET", "POST"]) |
1096 | 1096 |
@signed_terms_required |
1097 | 1097 |
@login_required |
1098 |
def project_update(request, application_id):
|
|
1098 |
def project_modify(request, application_id):
|
|
1099 | 1099 |
resource_groups = RESOURCES_PRESENTATION_DATA.get('groups', {}) |
1100 | 1100 |
resource_catalog = () |
1101 | 1101 |
result = callpoint.list_resources() |
... | ... | |
1133 | 1133 |
@signed_terms_required |
1134 | 1134 |
@login_required |
1135 | 1135 |
@transaction.commit_on_success |
1136 |
def project_app(request, application_id): |
|
1137 |
return common_detail(request, application_id, is_chain=False) |
|
1138 |
|
|
1139 |
@require_http_methods(["GET", "POST"]) |
|
1140 |
@signed_terms_required |
|
1141 |
@login_required |
|
1142 |
@transaction.commit_on_success |
|
1136 | 1143 |
def project_detail(request, chain_id): |
1137 |
addmembers_form = AddProjectMembersForm()
|
|
1138 |
if request.method == 'POST': |
|
1139 |
addmembers_form = AddProjectMembersForm(
|
|
1140 |
request.POST,
|
|
1141 |
chain_id=int(chain_id),
|
|
1142 |
request_user=request.user)
|
|
1143 |
if addmembers_form.is_valid():
|
|
1144 |
try:
|
|
1145 |
rollback = False
|
|
1146 |
chain_id = int(chain_id)
|
|
1147 |
map(lambda u: enroll_member(
|
|
1148 |
chain_id,
|
|
1149 |
u,
|
|
1150 |
request_user=request.user),
|
|
1151 |
addmembers_form.valid_users)
|
|
1152 |
except (IOError, PermissionDenied), e:
|
|
1153 |
messages.error(request, e)
|
|
1154 |
except BaseException, e:
|
|
1155 |
rollback = True
|
|
1156 |
messages.error(request, e)
|
|
1157 |
finally:
|
|
1158 |
if rollback == True:
|
|
1159 |
transaction.rollback()
|
|
1160 |
else:
|
|
1161 |
transaction.commit()
|
|
1162 |
addmembers_form = AddProjectMembersForm()
|
|
1144 |
return common_detail(request, chain_id)
|
|
1145 |
|
|
1146 |
def addmembers(request, chain_id):
|
|
1147 |
addmembers_form = AddProjectMembersForm(
|
|
1148 |
request.POST,
|
|
1149 |
chain_id=int(chain_id),
|
|
1150 |
request_user=request.user)
|
|
1151 |
if addmembers_form.is_valid():
|
|
1152 |
try:
|
|
1153 |
rollback = False
|
|
1154 |
chain_id = int(chain_id)
|
|
1155 |
map(lambda u: enroll_member(
|
|
1156 |
chain_id,
|
|
1157 |
u,
|
|
1158 |
request_user=request.user),
|
|
1159 |
addmembers_form.valid_users)
|
|
1160 |
except (IOError, PermissionDenied), e:
|
|
1161 |
messages.error(request, e)
|
|
1162 |
except BaseException, e:
|
|
1163 |
rollback = True
|
|
1164 |
messages.error(request, e)
|
|
1165 |
finally:
|
|
1166 |
if rollback == True:
|
|
1167 |
transaction.rollback()
|
|
1168 |
else:
|
|
1169 |
transaction.commit()
|
|
1163 | 1170 |
|
1164 |
rollback = False |
|
1171 |
def common_detail(request, common_id, is_chain=True): |
|
1172 |
if is_chain: |
|
1173 |
if request.method == 'POST': |
|
1174 |
addmembers(request, common_id) |
|
1165 | 1175 |
|
1166 |
project, application = get_by_chain_or_404(chain_id) |
|
1167 |
if project: |
|
1168 |
members = project.projectmembership_set.select_related() |
|
1169 |
else: |
|
1170 |
members = ProjectMembership.objects.none() |
|
1176 |
addmembers_form = AddProjectMembersForm() |
|
1177 |
|
|
1178 |
project, application = get_by_chain_or_404(common_id) |
|
1179 |
if project: |
|
1180 |
members = project.projectmembership_set.select_related() |
|
1181 |
members_table = tables.ProjectMembersTable(project, |
|
1182 |
members, |
|
1183 |
user=request.user, |
|
1184 |
prefix="members_") |
|
1185 |
RequestConfig(request, paginate={"per_page": PAGINATE_BY} |
|
1186 |
).configure(members_table) |
|
1187 |
|
|
1188 |
else: |
|
1189 |
members_table = None |
|
1171 | 1190 |
|
1172 |
members_table = tables.ProjectApplicationMembersTable(project, |
|
1173 |
members, |
|
1174 |
user=request.user, |
|
1175 |
prefix="members_") |
|
1176 |
RequestConfig(request, paginate={"per_page": PAGINATE_BY}).configure(members_table) |
|
1191 |
else: # is application |
|
1192 |
application = get_object_or_404(ProjectApplication, pk=common_id) |
|
1193 |
members_table = None |
|
1194 |
addmembers_form = None |
|
1177 | 1195 |
|
1178 | 1196 |
modifications_table = None |
1179 | 1197 |
|
1180 |
following_applications = list(application.followers())
|
|
1198 |
following_applications = list(application.pending_modifications())
|
|
1181 | 1199 |
following_applications.reverse() |
1182 |
modifications_table = \
|
|
1200 |
modifications_table = (
|
|
1183 | 1201 |
tables.ProjectModificationApplicationsTable(following_applications, |
1184 | 1202 |
user=request.user, |
1185 |
prefix="modifications_") |
|
1203 |
prefix="modifications_"))
|
|
1186 | 1204 |
|
1187 | 1205 |
return object_detail( |
1188 | 1206 |
request, |
... | ... | |
1192 | 1210 |
extra_context={ |
1193 | 1211 |
'addmembers_form':addmembers_form, |
1194 | 1212 |
'members_table': members_table, |
1195 |
'user_owns_project': request.user.owns_project(project),
|
|
1213 |
'user_owns_project': request.user.owns_application(application),
|
|
1196 | 1214 |
'modifications_table': modifications_table, |
1197 | 1215 |
'member_status': application.user_status(request.user) |
1198 | 1216 |
}) |
Also available in: Unified diff