Revision b22de10a snf-astakos-app/astakos/im/models.py
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
1017 | 1017 |
def __str__(self): |
1018 | 1018 |
return self.policy |
1019 | 1019 |
|
1020 |
class MemberRejectPolicy(models.Model): |
|
1021 |
policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True) |
|
1022 |
description = models.CharField(_('Description'), max_length=80) |
|
1023 |
|
|
1024 |
def __str__(self): |
|
1025 |
return self.policy |
|
1026 |
|
|
1020 | 1027 |
_auto_accept = False |
1021 | 1028 |
def get_auto_accept(): |
1022 | 1029 |
global _auto_accept |
... | ... | |
1047 | 1054 |
def save(self): |
1048 | 1055 |
self.validate_name() |
1049 | 1056 |
super(ProjectDefinition, self).save() |
1050 |
|
|
1051 |
def validate_name(self): |
|
1052 |
""" |
|
1053 |
Validate name uniqueness among all active projects. |
|
1054 |
""" |
|
1055 |
alive_projects = list(get_alive_projects()) |
|
1056 |
q = filter(lambda p: p.definition.name==self.name, alive_projects) |
|
1057 |
if q: |
|
1058 |
raise ValidationError({'name': [_(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)]}) |
|
1059 |
|
|
1057 |
|
|
1060 | 1058 |
@property |
1061 | 1059 |
def violated_resource_grants(self): |
1062 | 1060 |
return False |
... | ... | |
1086 | 1084 |
uplimit = p.get('uplimit', 0) |
1087 | 1085 |
update = p.get('update', True) |
1088 | 1086 |
self.add_resource_policy(service, resource, uplimit, update) |
1089 |
|
|
1090 |
def get_absolute_url(self): |
|
1091 |
return reverse('project_application_detail', args=(self.serial,)) |
|
1092 |
|
|
1093 | 1087 |
|
1094 | 1088 |
class ProjectResourceGrant(models.Model): |
1095 | 1089 |
objects = ExtendedManager() |
... | ... | |
1108 | 1102 |
unique=True, |
1109 | 1103 |
default=uuid.uuid4().hex[:30] |
1110 | 1104 |
) |
1111 |
applicant = models.ForeignKey(AstakosUser, related_name='my_project_applications') |
|
1112 |
owner = models.ForeignKey(AstakosUser, related_name='own_project_applications') |
|
1105 |
applicant = models.ForeignKey( |
|
1106 |
AstakosUser, |
|
1107 |
related_name='my_project_applications', |
|
1108 |
db_index=True) |
|
1109 |
owner = models.ForeignKey( |
|
1110 |
AstakosUser, |
|
1111 |
related_name='own_project_applications', |
|
1112 |
db_index=True |
|
1113 |
) |
|
1113 | 1114 |
comments = models.TextField(null=True, blank=True) |
1114 | 1115 |
definition = models.OneToOneField(ProjectDefinition) |
1115 | 1116 |
issue_date = models.DateTimeField() |
... | ... | |
1129 | 1130 |
application = models.OneToOneField(ProjectApplication, related_name='project') |
1130 | 1131 |
creation_date = models.DateTimeField() |
1131 | 1132 |
last_approval_date = models.DateTimeField(null=True) |
1133 |
termination_start_date = models.DateTimeField(null=True) |
|
1132 | 1134 |
termination_date = models.DateTimeField(null=True) |
1133 | 1135 |
members = models.ManyToManyField(AstakosUser, through='ProjectMembership') |
1134 |
last_synced_application = models.OneToOneField( |
|
1136 |
membership_dirty = models.BooleanField(default=False) |
|
1137 |
last_application_synced = models.OneToOneField( |
|
1135 | 1138 |
ProjectApplication, related_name='last_project', null=True, blank=True |
1136 | 1139 |
) |
1137 | 1140 |
|
1138 | 1141 |
@property |
1139 | 1142 |
def definition(self): |
1140 | 1143 |
return self.application.definition |
1141 |
|
|
1144 |
|
|
1145 |
@property |
|
1146 |
def violated_members_number_limit(self): |
|
1147 |
return len(self.approved_members) <= self.definition.limit_on_members_number |
|
1148 |
|
|
1142 | 1149 |
@property |
1143 | 1150 |
def is_valid(self): |
1144 | 1151 |
try: |
... | ... | |
1147 | 1154 |
return False |
1148 | 1155 |
else: |
1149 | 1156 |
return True |
1150 |
|
|
1157 |
|
|
1151 | 1158 |
@property |
1152 | 1159 |
def is_active(self): |
1153 | 1160 |
if not self.is_valid: |
... | ... | |
1158 | 1165 |
return False |
1159 | 1166 |
if self.definition.violated_resource_grants: |
1160 | 1167 |
return False |
1168 |
# if self.violated_members_number_limit: |
|
1169 |
# return False |
|
1161 | 1170 |
return True |
1162 | 1171 |
|
1163 | 1172 |
@property |
... | ... | |
1177 | 1186 |
if not self.last_approval_date: |
1178 | 1187 |
if not self.definition.violated_resource_grants: |
1179 | 1188 |
return False |
1189 |
# if not self.violated_members_number_limit: |
|
1190 |
# return False |
|
1180 | 1191 |
return True |
1181 | 1192 |
|
1182 | 1193 |
@property |
... | ... | |
1195 | 1206 |
return False |
1196 | 1207 |
|
1197 | 1208 |
@property |
1198 |
def approved_members(self): |
|
1199 |
return [m.person for m in self.members.filter(is_accepted=True)] |
|
1200 |
|
|
1201 |
def suspend(self): |
|
1202 |
self.last_approval_date = None |
|
1203 |
self.save() |
|
1209 |
def is_synchronized(self): |
|
1210 |
return self.last_application_synced == self.application and \ |
|
1211 |
not self.membership_dirty and \ |
|
1212 |
(not self.termination_start_date or termination_date) |
|
1204 | 1213 |
|
1205 |
def terminate(self): |
|
1206 |
self.terminaton_date = datetime.now() |
|
1207 |
self.save() |
|
1208 |
|
|
1209 |
def sync(self): |
|
1210 |
c, rejected = send_quota(self.approved_members) |
|
1214 |
@property |
|
1215 |
def approved_members(self): |
|
1216 |
return [m.person for m in self.projectmembership_set.filter(is_accepted=True)] |
|
1217 |
|
|
1218 |
def sync(self, specific_members=()): |
|
1219 |
if self.is_synchornized(): |
|
1220 |
return |
|
1221 |
members = specific_members or self.approved_members |
|
1222 |
c, rejected = send_quota(members) |
|
1211 | 1223 |
return rejected |
1212 | 1224 |
|
1213 | 1225 |
def add_member(self, user, request_user=None): |
... | ... | |
1229 | 1241 |
return |
1230 | 1242 |
if created: |
1231 | 1243 |
m.issue_date = datetime.now() |
1244 |
|
|
1232 | 1245 |
m.is_accepted = True |
1233 | 1246 |
m.decision_date = datetime.now() |
1234 | 1247 |
m.save() |
1248 |
|
|
1249 |
# set membership_dirty flag |
|
1250 |
self.membership_dirty = True |
|
1251 |
self.save() |
|
1252 |
|
|
1253 |
rejected = self.sync([user]) |
|
1254 |
if not rejected: |
|
1255 |
# if syncing was successful unset membership_dirty flag |
|
1256 |
self.membership_dirty = False |
|
1257 |
self.save() |
|
1258 |
|
|
1235 | 1259 |
notification = build_notification( |
1236 | 1260 |
settings.SERVER_EMAIL, |
1237 | 1261 |
[user.email], |
1238 | 1262 |
_('Your membership on project %(name)s has been accepted.') % project.definition.__dict__, |
1239 | 1263 |
_('Your membership on project %(name)s has been accepted.') % project.definition.__dict__ |
1240 | 1264 |
) |
1241 |
notification.send() |
|
1242 |
|
|
1265 |
|
|
1243 | 1266 |
def remove_member(self, user, request_user=None): |
1244 | 1267 |
if user.is_digit(): |
1245 | 1268 |
user = _lookup_object(AstakosUser, id=user) |
... | ... | |
1254 | 1277 |
m.is_accepted = False |
1255 | 1278 |
m.decision_date = datetime.now() |
1256 | 1279 |
m.save() |
1280 |
|
|
1281 |
# set membership_dirty flag |
|
1282 |
self.membership_dirty = True |
|
1283 |
self.save() |
|
1284 |
|
|
1285 |
rejected = self.sync([user]) |
|
1286 |
if not rejected: |
|
1287 |
# if syncing was successful unset membership_dirty flag |
|
1288 |
self.membership_dirty = False |
|
1289 |
self.save() |
|
1290 |
|
|
1257 | 1291 |
notification = build_notification( |
1258 | 1292 |
settings.SERVER_EMAIL, |
1259 | 1293 |
[user.email], |
... | ... | |
1261 | 1295 |
_('Your membership on project %(name)s has been removed.') % project.definition.__dict__ |
1262 | 1296 |
) |
1263 | 1297 |
notification.send() |
1298 |
|
|
1299 |
|
|
1300 |
def validate_name(self): |
|
1301 |
""" |
|
1302 |
Validate name uniqueness among all active projects. |
|
1303 |
""" |
|
1304 |
alive_projects = list(get_alive_projects()) |
|
1305 |
q = filter( |
|
1306 |
lambda p: p.definition.name == self.definition.name and \ |
|
1307 |
p.application.serial != self.application.serial, |
|
1308 |
alive_projects |
|
1309 |
) |
|
1310 |
if q: |
|
1311 |
raise ValidationError( |
|
1312 |
{'name': [_(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)]} |
|
1313 |
) |
|
1314 |
|
|
1315 |
@classmethod |
|
1316 |
def submit(definition, applicant, comments, precursor_application=None, commit=True): |
|
1317 |
if precursor_application and precursor_application.project.is_valid: |
|
1318 |
application = precursor_application.copy() |
|
1319 |
application.precursor_application = precursor_application |
|
1320 |
else: |
|
1321 |
application = ProjectApplication(owner=applicant) |
|
1322 |
application.definition = definition |
|
1323 |
application.applicant = applicant |
|
1324 |
application.comments = comments |
|
1325 |
application.issue_date = datetime.now() |
|
1326 |
if commit: |
|
1327 |
definition.save() |
|
1328 |
application.save() |
|
1329 |
if applicant.is_superuser(): |
|
1330 |
self.approve_application() |
|
1331 |
notification = build_notification( |
|
1332 |
settings.SERVER_EMAIL, |
|
1333 |
[i[1] for i in settings.ADMINS], |
|
1334 |
_(GROUP_CREATION_SUBJECT) % {'group':application.definition.name}, |
|
1335 |
_('An new project application identified by %(serial)s has been submitted.') % application.__dict__ |
|
1336 |
) |
|
1337 |
notification.send() |
|
1338 |
return application |
|
1339 |
|
|
1340 |
def approve(self, approval_user=None): |
|
1341 |
""" |
|
1342 |
If approval_user then during owner membership acceptance |
|
1343 |
it is checked whether the request_user is eligible. |
|
1344 |
""" |
|
1345 |
if not self.precursor_application: |
|
1346 |
kwargs = { |
|
1347 |
'application':self, |
|
1348 |
'creation_date':datetime.now(), |
|
1349 |
'last_approval_date':datetime.now(), |
|
1350 |
} |
|
1351 |
project = _create_object(Project, **kwargs) |
|
1352 |
project.add_member(self.owner, approval_user) |
|
1353 |
else: |
|
1354 |
project = self.precursor_application.project |
|
1355 |
last_approval_date = project.last_approval_date |
|
1356 |
if project.is_valid: |
|
1357 |
project.application = app |
|
1358 |
project.last_approval_date = datetime.now() |
|
1359 |
project.save() |
|
1360 |
else: |
|
1361 |
raise Exception(_(astakos_messages.INVALID_PROJECT) % project.__dict__) |
|
1362 |
|
|
1363 |
rejected = self.sync() |
|
1364 |
if rejected: |
|
1365 |
# revert to precursor |
|
1366 |
project.appication = app.precursor_application |
|
1367 |
if project.application: |
|
1368 |
project.last_approval_date = last_approval_date |
|
1369 |
project.save() |
|
1370 |
rejected = synchonize_project(project.serial) |
|
1371 |
if rejected: |
|
1372 |
raise Exception(_(astakos_messages.QH_SYNC_ERROR)) |
|
1373 |
else: |
|
1374 |
project.last_application_synced = app |
|
1375 |
project.save() |
|
1376 |
sender, recipients, subject, message |
|
1377 |
notification = build_notification( |
|
1378 |
settings.SERVER_EMAIL, |
|
1379 |
[project.owner.email], |
|
1380 |
_('Project application has been approved on %s alpha2 testing' % SITENAME), |
|
1381 |
_('Your application request %(serial)s has been apporved.') |
|
1382 |
) |
|
1383 |
notification.send() |
|
1384 |
|
|
1385 |
def terminate(self): |
|
1386 |
self.termination_start_date = datetime.now() |
|
1387 |
self.terminaton_date = None |
|
1388 |
self.save() |
|
1389 |
|
|
1390 |
rejected = self.sync() |
|
1391 |
if not rejected: |
|
1392 |
self.termination_start_date = None |
|
1393 |
self.terminaton_date = datetime.now() |
|
1394 |
self.save() |
|
1395 |
|
|
1396 |
notification = build_notification( |
|
1397 |
settings.SERVER_EMAIL, |
|
1398 |
[self.application.owner.email], |
|
1399 |
_('Project %(name)s has been terminated.') % self.definition.__dict__, |
|
1400 |
_('Project %(name)s has been terminated.') % self.definition.__dict__ |
|
1401 |
) |
|
1402 |
notification.send() |
|
1403 |
|
|
1404 |
def suspend(self): |
|
1405 |
self.last_approval_date = None |
|
1406 |
self.save() |
|
1407 |
notification = build_notification( |
|
1408 |
settings.SERVER_EMAIL, |
|
1409 |
[self.application.owner.email], |
|
1410 |
_('Project %(name)s has been suspended.') % self.definition.__dict__, |
|
1411 |
_('Project %(name)s has been suspended.') % self.definition.__dict__ |
|
1412 |
) |
|
1413 |
notification.send() |
|
1414 |
|
|
1264 | 1415 |
|
1265 | 1416 |
|
1266 | 1417 |
class ProjectMembership(models.Model): |
... | ... | |
1322 | 1473 |
def list_applications(): |
1323 | 1474 |
return ProjectApplication.objects.all() |
1324 | 1475 |
|
1325 |
def submit_application(definition, applicant, comments, precursor_application=None, commit=True): |
|
1326 |
if precursor_application: |
|
1327 |
application = precursor_application.copy() |
|
1328 |
application.precursor_application = precursor_application |
|
1329 |
else: |
|
1330 |
application = ProjectApplication(owner=applicant) |
|
1331 |
application.definition = definition |
|
1332 |
application.applicant = applicant |
|
1333 |
application.comments = comments |
|
1334 |
application.issue_date = datetime.now() |
|
1335 |
if commit: |
|
1336 |
definition.save() |
|
1337 |
application.save() |
|
1338 |
notification = build_notification( |
|
1339 |
settings.SERVER_EMAIL, |
|
1340 |
[i[1] for i in settings.ADMINS], |
|
1341 |
_(GROUP_CREATION_SUBJECT) % {'group':application.definition.name}, |
|
1342 |
_('An new project application identified by %(serial)s has been submitted.') % application.__dict__ |
|
1343 |
) |
|
1344 |
notification.send() |
|
1345 |
return application |
|
1346 |
|
|
1347 |
def approve_application(serial, request_user=None): |
|
1348 |
app = _lookup_object(ProjectApplication, serial=serial) |
|
1349 |
if not app.precursor_application: |
|
1350 |
kwargs = { |
|
1351 |
'application':app, |
|
1352 |
'creation_date':datetime.now(), |
|
1353 |
'last_approval_date':datetime.now(), |
|
1354 |
} |
|
1355 |
project = _create_object(Project, **kwargs) |
|
1356 |
project.add_member(app.owner, request_user) |
|
1357 |
else: |
|
1358 |
project = app.precursor_application.project |
|
1359 |
last_approval_date = project.last_approval_date |
|
1360 |
if project.is_valid: |
|
1361 |
project.application = app |
|
1362 |
project.last_approval_date = datetime.now() |
|
1363 |
project.save() |
|
1364 |
else: |
|
1365 |
raise Exception(_(astakos_messages.INVALID_PROJECT) % project.__dict__) |
|
1366 |
|
|
1367 |
rejected = synchonize_project(project.serial) |
|
1368 |
if rejected: |
|
1369 |
# revert to precursor |
|
1370 |
project.appication = app.precursor_application |
|
1371 |
if project.application: |
|
1372 |
project.last_approval_date = last_approval_date |
|
1373 |
project.save() |
|
1374 |
rejected = synchonize_project(project.serial) |
|
1375 |
if rejected: |
|
1376 |
raise Exception(_(astakos_messages.QH_SYNC_ERROR)) |
|
1377 |
else: |
|
1378 |
project.last_application_synced = app |
|
1379 |
project.save() |
|
1380 |
sender, recipients, subject, message |
|
1381 |
notification = build_notification( |
|
1382 |
settings.SERVER_EMAIL, |
|
1383 |
[project.owner.email], |
|
1384 |
_('Project application has been approved on %s alpha2 testing' % SITENAME), |
|
1385 |
_('Your application request %(serial)s has been apporved.') |
|
1386 |
) |
|
1387 |
notification.send() |
|
1388 |
|
|
1389 | 1476 |
|
1390 | 1477 |
def list_projects(filter_property=None): |
1391 | 1478 |
if filter_property: |
... | ... | |
1395 | 1482 |
) |
1396 | 1483 |
return Project.objects.all() |
1397 | 1484 |
|
1398 |
def suspend_project(serial): |
|
1399 |
project = _lookup_object(Project, serial=serial) |
|
1400 |
project.suspend() |
|
1401 |
notification = build_notification( |
|
1402 |
settings.SERVER_EMAIL, |
|
1403 |
[project.owner.email], |
|
1404 |
_('Project %(name)s has been suspended.') % project.definition.__dict__, |
|
1405 |
_('Project %(name)s has been suspended.') % project.definition.__dict__ |
|
1406 |
) |
|
1407 |
notification.send() |
|
1408 |
|
|
1409 |
def terminate_project(serial): |
|
1410 |
project = _lookup_object(Project, serial=serial) |
|
1411 |
project.termination() |
|
1412 |
notification = build_notification( |
|
1413 |
settings.SERVER_EMAIL, |
|
1414 |
[project.owner.email], |
|
1415 |
_('Project %(name)s has been terminated.') % project.definition.__dict__, |
|
1416 |
_('Project %(name)s has been terminated.') % project.definition.__dict__ |
|
1417 |
) |
|
1418 |
notification.send() |
|
1419 | 1485 |
|
1420 | 1486 |
def synchonize_project(serial): |
1421 | 1487 |
project = _lookup_object(Project, serial=serial) |
1422 |
if project.app != project.last_application_synced:
|
|
1423 |
return project.sync() |
|
1424 |
|
|
1488 |
return project.sync()
|
|
1489 |
|
|
1490 |
|
|
1425 | 1491 |
def create_astakos_user(u): |
1426 | 1492 |
try: |
1427 | 1493 |
AstakosUser.objects.get(user_ptr=u.pk) |
Also available in: Unified diff