Revision 8fb8d0cf snf-astakos-app/astakos/im/models.py
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
148 | 148 |
else: |
149 | 149 |
metadata[component.name] = d |
150 | 150 |
|
151 |
|
|
152 | 151 |
def component_by_order(s): |
153 | 152 |
return s[1].get('order') |
154 | 153 |
|
... | ... | |
276 | 275 |
return '%ss' % self.display_name |
277 | 276 |
return self.display_name |
278 | 277 |
|
278 |
|
|
279 | 279 |
def get_resource_names(): |
280 | 280 |
_RESOURCE_NAMES = [] |
281 | 281 |
resources = Resource.objects.select_related('service').all() |
... | ... | |
320 | 320 |
Returns a uuid to username mapping for the uuids appearing in l. |
321 | 321 |
If l is None returns the mapping for all existing users. |
322 | 322 |
""" |
323 |
q = self.filter(uuid__in=l) if l != None else self
|
|
323 |
q = self.filter(uuid__in=l) if l is not None else self
|
|
324 | 324 |
return dict(q.values_list('uuid', 'username')) |
325 | 325 |
|
326 | 326 |
def displayname_catalog(self, l=None): |
... | ... | |
331 | 331 |
if l is not None: |
332 | 332 |
lmap = dict((x.lower(), x) for x in l) |
333 | 333 |
q = self.filter(username__in=lmap.keys()) |
334 |
values = ((lmap[n], u) for n, u in q.values_list('username', 'uuid')) |
|
334 |
values = ((lmap[n], u) |
|
335 |
for n, u in q.values_list('username', 'uuid')) |
|
335 | 336 |
else: |
336 | 337 |
q = self |
337 | 338 |
values = self.values_list('username', 'uuid') |
338 | 339 |
return dict(values) |
339 | 340 |
|
340 | 341 |
|
341 |
|
|
342 | 342 |
class AstakosUser(User): |
343 | 343 |
""" |
344 | 344 |
Extends ``django.contrib.auth.models.User`` by defining additional fields. |
345 | 345 |
""" |
346 |
affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
|
|
347 |
null=True) |
|
346 |
affiliation = models.CharField(_('Affiliation'), max_length=255, |
|
347 |
blank=True, null=True)
|
|
348 | 348 |
|
349 | 349 |
#for invitations |
350 | 350 |
user_level = astakos_settings.DEFAULT_USER_LEVEL |
351 | 351 |
level = models.IntegerField(_('Inviter level'), default=user_level) |
352 | 352 |
invitations = models.IntegerField( |
353 |
_('Invitations left'), default=astakos_settings.INVITATIONS_PER_LEVEL.get(user_level, 0)) |
|
354 |
|
|
355 |
auth_token = models.CharField(_('Authentication Token'), |
|
356 |
max_length=64, |
|
357 |
unique=True, |
|
358 |
null=True, |
|
359 |
blank=True, |
|
360 |
help_text = _('Renew your authentication ' |
|
361 |
'token. Make sure to set the new ' |
|
362 |
'token in any client you may be ' |
|
363 |
'using, to preserve its ' |
|
364 |
'functionality.')) |
|
353 |
_('Invitations left'), |
|
354 |
default=astakos_settings.INVITATIONS_PER_LEVEL.get(user_level, 0)) |
|
355 |
|
|
356 |
auth_token = models.CharField( |
|
357 |
_('Authentication Token'), |
|
358 |
max_length=64, |
|
359 |
unique=True, |
|
360 |
null=True, |
|
361 |
blank=True, |
|
362 |
help_text=_('Renew your authentication ' |
|
363 |
'token. Make sure to set the new ' |
|
364 |
'token in any client you may be ' |
|
365 |
'using, to preserve its ' |
|
366 |
'functionality.')) |
|
365 | 367 |
auth_token_created = models.DateTimeField(_('Token creation date'), |
366 | 368 |
null=True) |
367 | 369 |
auth_token_expires = models.DateTimeField( |
... | ... | |
428 | 430 |
Resource, null=True, through='AstakosUserQuota') |
429 | 431 |
|
430 | 432 |
disturbed_quota = models.BooleanField(_('Needs quotaholder syncing'), |
431 |
default=False, db_index=True)
|
|
433 |
default=False, db_index=True) |
|
432 | 434 |
|
433 | 435 |
objects = AstakosUserManager() |
434 | 436 |
forupdate = ForUpdateManager() |
... | ... | |
463 | 465 |
if self.has_perm(pname): |
464 | 466 |
return |
465 | 467 |
p, created = Permission.objects.get_or_create( |
466 |
codename=pname,
|
|
467 |
name=pname.capitalize(),
|
|
468 |
content_type=get_content_type())
|
|
468 |
codename=pname, |
|
469 |
name=pname.capitalize(), |
|
470 |
content_type=get_content_type()) |
|
469 | 471 |
self.user_permissions.add(p) |
470 | 472 |
|
471 | 473 |
def remove_permission(self, pname): |
... | ... | |
545 | 547 |
self.auth_token = new_token |
546 | 548 |
self.auth_token_created = datetime.now() |
547 | 549 |
self.auth_token_expires = self.auth_token_created + \ |
548 |
timedelta(hours=astakos_settings.AUTH_TOKEN_DURATION)
|
|
550 |
timedelta(hours=astakos_settings.AUTH_TOKEN_DURATION) |
|
549 | 551 |
if flush_sessions: |
550 | 552 |
self.flush_sessions(current_key) |
551 | 553 |
msg = 'Token renewed for %s' % self.log_display |
... | ... | |
605 | 607 |
msg += " (manually accepted)" |
606 | 608 |
else: |
607 | 609 |
msg += " (accepted policy: %s)" % \ |
608 |
self.accepted_policy
|
|
610 |
self.accepted_policy |
|
609 | 611 |
return msg |
610 | 612 |
|
611 | 613 |
@property |
... | ... | |
730 | 732 |
|
731 | 733 |
def get_activation_url(self, nxt=False): |
732 | 734 |
url = "%s?auth=%s" % (reverse('astakos.im.views.activate'), |
733 |
quote(self.verification_code))
|
|
735 |
quote(self.verification_code)) |
|
734 | 736 |
if nxt: |
735 | 737 |
url += "&next=%s" % quote(nxt) |
736 | 738 |
return url |
737 | 739 |
|
738 | 740 |
def get_password_reset_url(self, token_generator=default_token_generator): |
739 | 741 |
return reverse('astakos.im.views.target.local.password_reset_confirm', |
740 |
kwargs={'uidb36':int_to_base36(self.id),
|
|
741 |
'token':token_generator.make_token(self)})
|
|
742 |
kwargs={'uidb36': int_to_base36(self.id),
|
|
743 |
'token': token_generator.make_token(self)})
|
|
742 | 744 |
|
743 | 745 |
def get_inactive_message(self, provider_module, identifier=None): |
744 | 746 |
provider = self.get_auth_provider(provider_module, identifier) |
... | ... | |
758 | 760 |
message = msg_pending |
759 | 761 |
url = self.get_resend_activation_url() |
760 | 762 |
msg_extra = msg_pending_help + \ |
761 |
u' ' + \
|
|
762 |
'<a href="%s">%s?</a>' % (url, msg_resend)
|
|
763 |
u' ' + \ |
|
764 |
'<a href="%s">%s?</a>' % (url, msg_resend) |
|
763 | 765 |
else: |
764 | 766 |
if not self.moderated: |
765 | 767 |
message = msg_pending_mod |
... | ... | |
930 | 932 |
""" |
931 | 933 |
Available user authentication methods. |
932 | 934 |
""" |
933 |
affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
|
|
934 |
null=True, default=None) |
|
935 |
affiliation = models.CharField(_('Affiliation'), max_length=255, |
|
936 |
blank=True, null=True, default=None)
|
|
935 | 937 |
user = models.ForeignKey(AstakosUser, related_name='auth_providers') |
936 | 938 |
module = models.CharField(_('Provider'), max_length=255, blank=False, |
937 |
default='local')
|
|
939 |
default='local') |
|
938 | 940 |
identifier = models.CharField(_('Third-party identifier'), |
939 |
max_length=255, null=True,
|
|
940 |
blank=True)
|
|
941 |
max_length=255, null=True, |
|
942 |
blank=True) |
|
941 | 943 |
active = models.BooleanField(default=True) |
942 | 944 |
auth_backend = models.CharField(_('Backend'), max_length=255, blank=False, |
943 |
default='astakos') |
|
945 |
default='astakos')
|
|
944 | 946 |
info_data = models.TextField(default="", null=True, blank=True) |
945 | 947 |
created = models.DateTimeField('Creation date', auto_now_add=True) |
946 | 948 |
|
... | ... | |
959 | 961 |
except Exception, e: |
960 | 962 |
self.info = {} |
961 | 963 |
|
962 |
for key,value in self.info.iteritems(): |
|
964 |
for key, value in self.info.iteritems():
|
|
963 | 965 |
setattr(self, 'info_%s' % key, value) |
964 | 966 |
|
965 | 967 |
@property |
... | ... | |
977 | 979 |
|
978 | 980 |
extra_data['instance'] = self |
979 | 981 |
return auth.get_provider(self.module, self.user, |
980 |
self.identifier, **extra_data)
|
|
982 |
self.identifier, **extra_data) |
|
981 | 983 |
|
982 | 984 |
def __repr__(self): |
983 |
return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier) |
|
985 |
return '<AstakosUserAuthProvider %s:%s>' % ( |
|
986 |
self.module, self.identifier) |
|
984 | 987 |
|
985 | 988 |
def __unicode__(self): |
986 | 989 |
if self.identifier: |
... | ... | |
1055 | 1058 |
code = models.BigIntegerField(_('Invitation code'), db_index=True) |
1056 | 1059 |
is_consumed = models.BooleanField(_('Consumed?'), default=False) |
1057 | 1060 |
created = models.DateTimeField(_('Creation date'), auto_now_add=True) |
1058 |
consumed = models.DateTimeField(_('Consumption date'), null=True, blank=True) |
|
1061 |
consumed = models.DateTimeField(_('Consumption date'), |
|
1062 |
null=True, blank=True) |
|
1059 | 1063 |
|
1060 | 1064 |
def __init__(self, *args, **kwargs): |
1061 | 1065 |
super(Invitation, self).__init__(*args, **kwargs) |
... | ... | |
1099 | 1103 |
raise EmailChange.DoesNotExist |
1100 | 1104 |
# is there an active user with this address? |
1101 | 1105 |
try: |
1102 |
AstakosUser.objects.get(email__iexact=email_change.new_email_address) |
|
1106 |
AstakosUser.objects.get( |
|
1107 |
email__iexact=email_change.new_email_address) |
|
1103 | 1108 |
except AstakosUser.DoesNotExist: |
1104 | 1109 |
pass |
1105 | 1110 |
else: |
... | ... | |
1135 | 1140 |
|
1136 | 1141 |
def get_url(self): |
1137 | 1142 |
return reverse('email_change_confirm', |
1138 |
kwargs={'activation_key': self.activation_key}) |
|
1143 |
kwargs={'activation_key': self.activation_key})
|
|
1139 | 1144 |
|
1140 | 1145 |
def activation_key_expired(self): |
1141 |
expiration_date = timedelta(days=astakos_settings.EMAILCHANGE_ACTIVATION_DAYS) |
|
1146 |
expiration_date = timedelta( |
|
1147 |
days=astakos_settings.EMAILCHANGE_ACTIVATION_DAYS) |
|
1142 | 1148 |
return self.requested_at + expiration_date < datetime.now() |
1143 | 1149 |
|
1144 | 1150 |
|
... | ... | |
1173 | 1179 |
""" |
1174 | 1180 |
Model for registring successful third party user authentications |
1175 | 1181 |
""" |
1176 |
third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True) |
|
1182 |
third_party_identifier = models.CharField( |
|
1183 |
_('Third-party identifier'), max_length=255, null=True, blank=True) |
|
1177 | 1184 |
provider = models.CharField(_('Provider'), max_length=255, blank=True) |
1178 | 1185 |
email = models.EmailField(_('e-mail address'), blank=True, null=True) |
1179 | 1186 |
first_name = models.CharField(_('first name'), max_length=30, blank=True, |
... | ... | |
1182 | 1189 |
null=True) |
1183 | 1190 |
affiliation = models.CharField('Affiliation', max_length=255, blank=True, |
1184 | 1191 |
null=True) |
1185 |
username = models.CharField(_('username'), max_length=30, unique=True, |
|
1186 |
help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters")) |
|
1192 |
username = models.CharField( |
|
1193 |
_('username'), max_length=30, unique=True, |
|
1194 |
help_text=_("Required. 30 characters or fewer. " |
|
1195 |
"Letters, numbers and @/./+/-/_ characters")) |
|
1187 | 1196 |
token = models.CharField(_('Token'), max_length=255, null=True, blank=True) |
1188 | 1197 |
created = models.DateTimeField(auto_now_add=True, null=True, blank=True) |
1189 | 1198 |
info = models.TextField(default="", null=True, blank=True) |
... | ... | |
1211 | 1220 |
|
1212 | 1221 |
@property |
1213 | 1222 |
def realname(self): |
1214 |
return '%s %s' %(self.first_name, self.last_name) |
|
1223 |
return '%s %s' % (self.first_name, self.last_name)
|
|
1215 | 1224 |
|
1216 | 1225 |
@realname.setter |
1217 | 1226 |
def realname(self, value): |
... | ... | |
1226 | 1235 |
if not self.id: |
1227 | 1236 |
# set username |
1228 | 1237 |
while not self.username: |
1229 |
username = uuid.uuid4().hex[:30]
|
|
1238 |
username = uuid.uuid4().hex[:30] |
|
1230 | 1239 |
try: |
1231 |
AstakosUser.objects.get(username = username)
|
|
1240 |
AstakosUser.objects.get(username=username)
|
|
1232 | 1241 |
except AstakosUser.DoesNotExist, e: |
1233 | 1242 |
self.username = username |
1234 | 1243 |
super(PendingThirdPartyUser, self).save(**kwargs) |
... | ... | |
1239 | 1248 |
self.token = default_token_generator.make_token(self) |
1240 | 1249 |
|
1241 | 1250 |
def existing_user(self): |
1242 |
return AstakosUser.objects.filter(auth_providers__module=self.provider, |
|
1243 |
auth_providers__identifier=self.third_party_identifier) |
|
1251 |
return AstakosUser.objects.filter( |
|
1252 |
auth_providers__module=self.provider, |
|
1253 |
auth_providers__identifier=self.third_party_identifier) |
|
1244 | 1254 |
|
1245 | 1255 |
def get_provider(self, user): |
1246 | 1256 |
params = { |
... | ... | |
1250 | 1260 |
return auth.get_provider(self.provider, user, |
1251 | 1261 |
self.third_party_identifier, **params) |
1252 | 1262 |
|
1263 |
|
|
1253 | 1264 |
class SessionCatalog(models.Model): |
1254 | 1265 |
session_key = models.CharField(_('session key'), max_length=40) |
1255 | 1266 |
user = models.ForeignKey(AstakosUser, related_name='sessions', null=True) |
... | ... | |
1274 | 1285 |
def search_by_name(self, *search_strings): |
1275 | 1286 |
projects = Project.objects.search_by_name(*search_strings) |
1276 | 1287 |
chains = [p.id for p in projects] |
1277 |
apps = ProjectApplication.objects.search_by_name(*search_strings)
|
|
1288 |
apps = ProjectApplication.objects.search_by_name(*search_strings) |
|
1278 | 1289 |
apps = (app for app in apps if app.is_latest()) |
1279 | 1290 |
app_chains = [app.chain for app in apps if app.chain not in chains] |
1280 | 1291 |
return chains + app_chains |
... | ... | |
1309 | 1320 |
|
1310 | 1321 |
|
1311 | 1322 |
class Chain(models.Model): |
1312 |
chain = models.AutoField(primary_key=True)
|
|
1323 |
chain = models.AutoField(primary_key=True)
|
|
1313 | 1324 |
|
1314 | 1325 |
def __str__(self): |
1315 | 1326 |
return "%s" % (self.chain,) |
1316 | 1327 |
|
1317 | 1328 |
objects = ChainManager() |
1318 | 1329 |
|
1319 |
PENDING = 0
|
|
1320 |
DENIED = 3
|
|
1321 |
DISMISSED = 4
|
|
1322 |
CANCELLED = 5
|
|
1330 |
PENDING = 0 |
|
1331 |
DENIED = 3 |
|
1332 |
DISMISSED = 4 |
|
1333 |
CANCELLED = 5 |
|
1323 | 1334 |
|
1324 |
APPROVED = 10
|
|
1325 |
APPROVED_PENDING = 11
|
|
1326 |
SUSPENDED = 12
|
|
1327 |
SUSPENDED_PENDING = 13
|
|
1328 |
TERMINATED = 14
|
|
1335 |
APPROVED = 10 |
|
1336 |
APPROVED_PENDING = 11 |
|
1337 |
SUSPENDED = 12 |
|
1338 |
SUSPENDED_PENDING = 13 |
|
1339 |
TERMINATED = 14 |
|
1329 | 1340 |
TERMINATED_PENDING = 15 |
1330 | 1341 |
|
1331 | 1342 |
PENDING_STATES = [PENDING, |
... | ... | |
1353 | 1364 |
TERMINATED] |
1354 | 1365 |
|
1355 | 1366 |
STATE_DISPLAY = { |
1356 |
PENDING : _("Pending"), |
|
1357 |
DENIED : _("Denied"), |
|
1358 |
DISMISSED : _("Dismissed"), |
|
1359 |
CANCELLED : _("Cancelled"), |
|
1360 |
APPROVED : _("Active"), |
|
1361 |
APPROVED_PENDING : _("Active - Pending"), |
|
1362 |
SUSPENDED : _("Suspended"), |
|
1363 |
SUSPENDED_PENDING : _("Suspended - Pending"), |
|
1364 |
TERMINATED : _("Terminated"), |
|
1365 |
TERMINATED_PENDING : _("Terminated - Pending"), |
|
1366 |
} |
|
1367 |
|
|
1367 |
PENDING: _("Pending"), |
|
1368 |
DENIED: _("Denied"), |
|
1369 |
DISMISSED: _("Dismissed"), |
|
1370 |
CANCELLED: _("Cancelled"), |
|
1371 |
APPROVED: _("Active"), |
|
1372 |
APPROVED_PENDING: _("Active - Pending"), |
|
1373 |
SUSPENDED: _("Suspended"), |
|
1374 |
SUSPENDED_PENDING: _("Suspended - Pending"), |
|
1375 |
TERMINATED: _("Terminated"), |
|
1376 |
TERMINATED_PENDING: _("Terminated - Pending"), |
|
1377 |
} |
|
1368 | 1378 |
|
1369 | 1379 |
@classmethod |
1370 | 1380 |
def _chain_state(cls, project_state, app_state): |
... | ... | |
1420 | 1430 |
|
1421 | 1431 |
def user_visible_by_chain(self, flt): |
1422 | 1432 |
model = self.model |
1423 |
pending = self.filter(model.Q_PENDING | model.Q_DENIED).values_list('chain') |
|
1433 |
pending = self.filter( |
|
1434 |
model.Q_PENDING | model.Q_DENIED).values_list('chain') |
|
1424 | 1435 |
approved = self.filter(model.Q_APPROVED).values_list('chain') |
1425 | 1436 |
by_chain = dict(pending.annotate(models.Max('id'))) |
1426 | 1437 |
by_chain.update(approved.annotate(models.Max('id'))) |
... | ... | |
1434 | 1445 |
participates_filters = Q() |
1435 | 1446 |
else: |
1436 | 1447 |
participates_filters = Q(owner=user) | Q(applicant=user) | \ |
1437 |
Q(project__projectmembership__person=user)
|
|
1448 |
Q(project__projectmembership__person=user) |
|
1438 | 1449 |
|
1439 |
return self.user_visible_by_chain(participates_filters).order_by('issue_date').distinct() |
|
1450 |
return self.user_visible_by_chain( |
|
1451 |
participates_filters).order_by('issue_date').distinct() |
|
1440 | 1452 |
|
1441 | 1453 |
def search_by_name(self, *search_strings): |
1442 | 1454 |
q = Q() |
... | ... | |
1452 | 1464 |
|
1453 | 1465 |
|
1454 | 1466 |
class ProjectApplication(models.Model): |
1455 |
applicant = models.ForeignKey( |
|
1456 |
AstakosUser, |
|
1457 |
related_name='projects_applied', |
|
1458 |
db_index=True) |
|
1459 |
|
|
1460 |
PENDING = 0 |
|
1461 |
APPROVED = 1 |
|
1462 |
REPLACED = 2 |
|
1463 |
DENIED = 3 |
|
1464 |
DISMISSED = 4 |
|
1465 |
CANCELLED = 5 |
|
1466 |
|
|
1467 |
state = models.IntegerField(default=PENDING, |
|
1468 |
db_index=True) |
|
1469 |
|
|
1470 |
owner = models.ForeignKey( |
|
1471 |
AstakosUser, |
|
1472 |
related_name='projects_owned', |
|
1473 |
db_index=True) |
|
1474 |
|
|
1475 |
chain = models.ForeignKey(Chain, |
|
1476 |
related_name='chained_apps', |
|
1477 |
db_column='chain') |
|
1478 |
precursor_application = models.ForeignKey('ProjectApplication', |
|
1479 |
null=True, |
|
1480 |
blank=True) |
|
1481 |
|
|
1482 |
name = models.CharField(max_length=80) |
|
1483 |
homepage = models.URLField(max_length=255, null=True, |
|
1484 |
verify_exists=False) |
|
1485 |
description = models.TextField(null=True, blank=True) |
|
1486 |
start_date = models.DateTimeField(null=True, blank=True) |
|
1487 |
end_date = models.DateTimeField() |
|
1488 |
member_join_policy = models.IntegerField() |
|
1489 |
member_leave_policy = models.IntegerField() |
|
1490 |
limit_on_members_number = models.PositiveIntegerField(null=True) |
|
1491 |
resource_grants = models.ManyToManyField( |
|
1492 |
Resource, |
|
1493 |
null=True, |
|
1494 |
blank=True, |
|
1495 |
through='ProjectResourceGrant') |
|
1496 |
comments = models.TextField(null=True, blank=True) |
|
1497 |
issue_date = models.DateTimeField(auto_now_add=True) |
|
1498 |
response_date = models.DateTimeField(null=True, blank=True) |
|
1499 |
response = models.TextField(null=True, blank=True) |
|
1500 |
|
|
1501 |
objects = ProjectApplicationManager() |
|
1467 |
applicant = models.ForeignKey( |
|
1468 |
AstakosUser, |
|
1469 |
related_name='projects_applied', |
|
1470 |
db_index=True) |
|
1471 |
|
|
1472 |
PENDING = 0 |
|
1473 |
APPROVED = 1 |
|
1474 |
REPLACED = 2 |
|
1475 |
DENIED = 3 |
|
1476 |
DISMISSED = 4 |
|
1477 |
CANCELLED = 5 |
|
1478 |
|
|
1479 |
state = models.IntegerField(default=PENDING, |
|
1480 |
db_index=True) |
|
1481 |
owner = models.ForeignKey( |
|
1482 |
AstakosUser, |
|
1483 |
related_name='projects_owned', |
|
1484 |
db_index=True) |
|
1485 |
chain = models.ForeignKey(Chain, |
|
1486 |
related_name='chained_apps', |
|
1487 |
db_column='chain') |
|
1488 |
precursor_application = models.ForeignKey('ProjectApplication', |
|
1489 |
null=True, |
|
1490 |
blank=True) |
|
1491 |
name = models.CharField(max_length=80) |
|
1492 |
homepage = models.URLField(max_length=255, null=True, |
|
1493 |
verify_exists=False) |
|
1494 |
description = models.TextField(null=True, blank=True) |
|
1495 |
start_date = models.DateTimeField(null=True, blank=True) |
|
1496 |
end_date = models.DateTimeField() |
|
1497 |
member_join_policy = models.IntegerField() |
|
1498 |
member_leave_policy = models.IntegerField() |
|
1499 |
limit_on_members_number = models.PositiveIntegerField(null=True) |
|
1500 |
resource_grants = models.ManyToManyField( |
|
1501 |
Resource, |
|
1502 |
null=True, |
|
1503 |
blank=True, |
|
1504 |
through='ProjectResourceGrant') |
|
1505 |
comments = models.TextField(null=True, blank=True) |
|
1506 |
issue_date = models.DateTimeField(auto_now_add=True) |
|
1507 |
response_date = models.DateTimeField(null=True, blank=True) |
|
1508 |
response = models.TextField(null=True, blank=True) |
|
1509 |
|
|
1510 |
objects = ProjectApplicationManager() |
|
1502 | 1511 |
|
1503 | 1512 |
# Compiled queries |
1504 |
Q_PENDING = Q(state=PENDING)
|
|
1513 |
Q_PENDING = Q(state=PENDING) |
|
1505 | 1514 |
Q_APPROVED = Q(state=APPROVED) |
1506 |
Q_DENIED = Q(state=DENIED)
|
|
1515 |
Q_DENIED = Q(state=DENIED) |
|
1507 | 1516 |
|
1508 | 1517 |
class Meta: |
1509 | 1518 |
unique_together = ("chain", "id") |
... | ... | |
1513 | 1522 |
|
1514 | 1523 |
# TODO: Move to a more suitable place |
1515 | 1524 |
APPLICATION_STATE_DISPLAY = { |
1516 |
PENDING : _('Pending review'),
|
|
1517 |
APPROVED : _('Approved'),
|
|
1518 |
REPLACED : _('Replaced'),
|
|
1519 |
DENIED : _('Denied'),
|
|
1525 |
PENDING: _('Pending review'),
|
|
1526 |
APPROVED: _('Approved'),
|
|
1527 |
REPLACED: _('Replaced'),
|
|
1528 |
DENIED: _('Denied'),
|
|
1520 | 1529 |
DISMISSED: _('Dismissed'), |
1521 | 1530 |
CANCELLED: _('Cancelled') |
1522 | 1531 |
} |
... | ... | |
1631 | 1640 |
def cancel(self): |
1632 | 1641 |
if not self.can_cancel(): |
1633 | 1642 |
m = _("cannot cancel: application '%s' in state '%s'") % ( |
1634 |
self.id, self.state)
|
|
1643 |
self.id, self.state) |
|
1635 | 1644 |
raise AssertionError(m) |
1636 | 1645 |
|
1637 | 1646 |
self.state = self.CANCELLED |
... | ... | |
1643 | 1652 |
def dismiss(self): |
1644 | 1653 |
if not self.can_dismiss(): |
1645 | 1654 |
m = _("cannot dismiss: application '%s' in state '%s'") % ( |
1646 |
self.id, self.state)
|
|
1655 |
self.id, self.state) |
|
1647 | 1656 |
raise AssertionError(m) |
1648 | 1657 |
|
1649 | 1658 |
self.state = self.DISMISSED |
... | ... | |
1655 | 1664 |
def deny(self, reason): |
1656 | 1665 |
if not self.can_deny(): |
1657 | 1666 |
m = _("cannot deny: application '%s' in state '%s'") % ( |
1658 |
self.id, self.state)
|
|
1667 |
self.id, self.state) |
|
1659 | 1668 |
raise AssertionError(m) |
1660 | 1669 |
|
1661 | 1670 |
self.state = self.DENIED |
... | ... | |
1669 | 1678 |
def approve(self, reason): |
1670 | 1679 |
if not self.can_approve(): |
1671 | 1680 |
m = _("cannot approve: project '%s' in state '%s'") % ( |
1672 |
self.name, self.state)
|
|
1673 |
raise AssertionError(m) # invalid argument |
|
1681 |
self.name, self.state) |
|
1682 |
raise AssertionError(m) # invalid argument
|
|
1674 | 1683 |
|
1675 | 1684 |
now = datetime.now() |
1676 | 1685 |
self.state = self.APPROVED |
... | ... | |
1698 | 1707 |
policy = self.member_leave_policy |
1699 | 1708 |
return presentation.PROJECT_MEMBER_LEAVE_POLICIES.get(policy) |
1700 | 1709 |
|
1710 |
|
|
1701 | 1711 |
class ProjectResourceGrant(models.Model): |
1702 | 1712 |
|
1703 |
resource = models.ForeignKey(Resource)
|
|
1704 |
project_application = models.ForeignKey(ProjectApplication,
|
|
1705 |
null=True)
|
|
1706 |
project_capacity = intDecimalField(null=True)
|
|
1707 |
member_capacity = intDecimalField(default=0)
|
|
1713 |
resource = models.ForeignKey(Resource)
|
|
1714 |
project_application = models.ForeignKey(ProjectApplication,
|
|
1715 |
null=True) |
|
1716 |
project_capacity = intDecimalField(null=True)
|
|
1717 |
member_capacity = intDecimalField(default=0)
|
|
1708 | 1718 |
|
1709 | 1719 |
objects = ExtendedManager() |
1710 | 1720 |
|
... | ... | |
1750 | 1760 |
if value == 1: |
1751 | 1761 |
return '1 byte' |
1752 | 1762 |
else: |
1753 |
return '0' |
|
1763 |
return '0'
|
|
1754 | 1764 |
|
1755 | 1765 |
|
1756 | 1766 |
class ProjectManager(ForUpdateManager): |
... | ... | |
1769 | 1779 |
|
1770 | 1780 |
def expired_projects(self): |
1771 | 1781 |
q = (~Q(state=Project.TERMINATED) & |
1772 |
Q(application__end_date__lt=datetime.now()))
|
|
1782 |
Q(application__end_date__lt=datetime.now())) |
|
1773 | 1783 |
return self.filter(q) |
1774 | 1784 |
|
1775 | 1785 |
def search_by_name(self, *search_strings): |
... | ... | |
1781 | 1791 |
|
1782 | 1792 |
class Project(models.Model): |
1783 | 1793 |
|
1784 |
id = models.OneToOneField(Chain,
|
|
1785 |
related_name='chained_project',
|
|
1786 |
db_column='id',
|
|
1787 |
primary_key=True)
|
|
1794 |
id = models.OneToOneField(Chain,
|
|
1795 |
related_name='chained_project', |
|
1796 |
db_column='id', |
|
1797 |
primary_key=True) |
|
1788 | 1798 |
|
1789 |
application = models.OneToOneField(
|
|
1790 |
ProjectApplication,
|
|
1791 |
related_name='project')
|
|
1792 |
last_approval_date = models.DateTimeField(null=True)
|
|
1799 |
application = models.OneToOneField(
|
|
1800 |
ProjectApplication, |
|
1801 |
related_name='project') |
|
1802 |
last_approval_date = models.DateTimeField(null=True)
|
|
1793 | 1803 |
|
1794 |
members = models.ManyToManyField(
|
|
1795 |
AstakosUser,
|
|
1796 |
through='ProjectMembership')
|
|
1804 |
members = models.ManyToManyField(
|
|
1805 |
AstakosUser, |
|
1806 |
through='ProjectMembership') |
|
1797 | 1807 |
|
1798 |
deactivation_reason = models.CharField(max_length=255, null=True)
|
|
1799 |
deactivation_date = models.DateTimeField(null=True)
|
|
1808 |
deactivation_reason = models.CharField(max_length=255, null=True)
|
|
1809 |
deactivation_date = models.DateTimeField(null=True)
|
|
1800 | 1810 |
|
1801 |
creation_date = models.DateTimeField(auto_now_add=True)
|
|
1802 |
name = models.CharField(
|
|
1803 |
max_length=80,
|
|
1804 |
null=True,
|
|
1805 |
db_index=True,
|
|
1806 |
unique=True)
|
|
1811 |
creation_date = models.DateTimeField(auto_now_add=True)
|
|
1812 |
name = models.CharField(
|
|
1813 |
max_length=80, |
|
1814 |
null=True, |
|
1815 |
db_index=True, |
|
1816 |
unique=True) |
|
1807 | 1817 |
|
1808 |
APPROVED = 1
|
|
1809 |
SUSPENDED = 10
|
|
1810 |
TERMINATED = 100
|
|
1818 |
APPROVED = 1 |
|
1819 |
SUSPENDED = 10 |
|
1820 |
TERMINATED = 100 |
|
1811 | 1821 |
|
1812 |
state = models.IntegerField(default=APPROVED,
|
|
1813 |
db_index=True)
|
|
1822 |
state = models.IntegerField(default=APPROVED,
|
|
1823 |
db_index=True) |
|
1814 | 1824 |
|
1815 |
objects = ProjectManager()
|
|
1825 |
objects = ProjectManager()
|
|
1816 | 1826 |
|
1817 | 1827 |
# Compiled queries |
1818 |
Q_TERMINATED = Q(state=TERMINATED)
|
|
1819 |
Q_SUSPENDED = Q(state=SUSPENDED)
|
|
1828 |
Q_TERMINATED = Q(state=TERMINATED) |
|
1829 |
Q_SUSPENDED = Q(state=SUSPENDED) |
|
1820 | 1830 |
Q_DEACTIVATED = Q_TERMINATED | Q_SUSPENDED |
1821 | 1831 |
|
1822 | 1832 |
def __str__(self): |
... | ... | |
1829 | 1839 |
return _("<project %s '%s'>") % (self.id, self.application.name) |
1830 | 1840 |
|
1831 | 1841 |
STATE_DISPLAY = { |
1832 |
APPROVED : 'Active',
|
|
1833 |
SUSPENDED : 'Suspended',
|
|
1834 |
TERMINATED : 'Terminated'
|
|
1835 |
}
|
|
1842 |
APPROVED: 'Active',
|
|
1843 |
SUSPENDED: 'Suspended',
|
|
1844 |
TERMINATED: 'Terminated' |
|
1845 |
} |
|
1836 | 1846 |
|
1837 | 1847 |
def state_display(self): |
1838 | 1848 |
return self.STATE_DISPLAY.get(self.state, _('Unknown')) |
... | ... | |
1902 | 1912 |
return False |
1903 | 1913 |
return (len(self.approved_members) + adding > limit) |
1904 | 1914 |
|
1905 |
|
|
1906 | 1915 |
### Other |
1907 | 1916 |
|
1908 | 1917 |
def count_pending_memberships(self): |
... | ... | |
1930 | 1939 |
|
1931 | 1940 |
|
1932 | 1941 |
CHAIN_STATE = { |
1933 |
(Project.APPROVED, ProjectApplication.PENDING) : Chain.APPROVED_PENDING,
|
|
1934 |
(Project.APPROVED, ProjectApplication.APPROVED) : Chain.APPROVED,
|
|
1935 |
(Project.APPROVED, ProjectApplication.DENIED) : Chain.APPROVED,
|
|
1936 |
(Project.APPROVED, ProjectApplication.DISMISSED): Chain.APPROVED,
|
|
1937 |
(Project.APPROVED, ProjectApplication.CANCELLED): Chain.APPROVED,
|
|
1938 |
|
|
1939 |
(Project.SUSPENDED, ProjectApplication.PENDING) : Chain.SUSPENDED_PENDING,
|
|
1940 |
(Project.SUSPENDED, ProjectApplication.APPROVED) : Chain.SUSPENDED,
|
|
1941 |
(Project.SUSPENDED, ProjectApplication.DENIED) : Chain.SUSPENDED,
|
|
1942 |
(Project.SUSPENDED, ProjectApplication.DISMISSED): Chain.SUSPENDED,
|
|
1943 |
(Project.SUSPENDED, ProjectApplication.CANCELLED): Chain.SUSPENDED,
|
|
1944 |
|
|
1945 |
(Project.TERMINATED, ProjectApplication.PENDING) : Chain.TERMINATED_PENDING,
|
|
1946 |
(Project.TERMINATED, ProjectApplication.APPROVED) : Chain.TERMINATED,
|
|
1947 |
(Project.TERMINATED, ProjectApplication.DENIED) : Chain.TERMINATED,
|
|
1942 |
(Project.APPROVED, ProjectApplication.PENDING): Chain.APPROVED_PENDING,
|
|
1943 |
(Project.APPROVED, ProjectApplication.APPROVED): Chain.APPROVED,
|
|
1944 |
(Project.APPROVED, ProjectApplication.DENIED): Chain.APPROVED,
|
|
1945 |
(Project.APPROVED, ProjectApplication.DISMISSED): Chain.APPROVED, |
|
1946 |
(Project.APPROVED, ProjectApplication.CANCELLED): Chain.APPROVED, |
|
1947 |
|
|
1948 |
(Project.SUSPENDED, ProjectApplication.PENDING): Chain.SUSPENDED_PENDING,
|
|
1949 |
(Project.SUSPENDED, ProjectApplication.APPROVED): Chain.SUSPENDED,
|
|
1950 |
(Project.SUSPENDED, ProjectApplication.DENIED): Chain.SUSPENDED,
|
|
1951 |
(Project.SUSPENDED, ProjectApplication.DISMISSED): Chain.SUSPENDED, |
|
1952 |
(Project.SUSPENDED, ProjectApplication.CANCELLED): Chain.SUSPENDED, |
|
1953 |
|
|
1954 |
(Project.TERMINATED, ProjectApplication.PENDING): Chain.TERMINATED_PENDING, |
|
1955 |
(Project.TERMINATED, ProjectApplication.APPROVED): Chain.TERMINATED,
|
|
1956 |
(Project.TERMINATED, ProjectApplication.DENIED): Chain.TERMINATED,
|
|
1948 | 1957 |
(Project.TERMINATED, ProjectApplication.DISMISSED): Chain.TERMINATED, |
1949 | 1958 |
(Project.TERMINATED, ProjectApplication.CANCELLED): Chain.TERMINATED, |
1950 | 1959 |
|
1951 |
(None, ProjectApplication.PENDING) : Chain.PENDING,
|
|
1952 |
(None, ProjectApplication.DENIED) : Chain.DENIED,
|
|
1953 |
(None, ProjectApplication.DISMISSED): Chain.DISMISSED,
|
|
1954 |
(None, ProjectApplication.CANCELLED): Chain.CANCELLED,
|
|
1955 |
}
|
|
1960 |
(None, ProjectApplication.PENDING): Chain.PENDING,
|
|
1961 |
(None, ProjectApplication.DENIED): Chain.DENIED,
|
|
1962 |
(None, ProjectApplication.DISMISSED): Chain.DISMISSED, |
|
1963 |
(None, ProjectApplication.CANCELLED): Chain.CANCELLED, |
|
1964 |
} |
|
1956 | 1965 |
|
1957 | 1966 |
|
1958 | 1967 |
class ProjectMembershipManager(ForUpdateManager): |
... | ... | |
1971 | 1980 |
def suspended(self): |
1972 | 1981 |
return self.filter(state=ProjectMembership.USER_SUSPENDED) |
1973 | 1982 |
|
1983 |
|
|
1974 | 1984 |
class ProjectMembership(models.Model): |
1975 | 1985 |
|
1976 |
person = models.ForeignKey(AstakosUser)
|
|
1977 |
request_date = models.DateTimeField(auto_now_add=True)
|
|
1978 |
project = models.ForeignKey(Project)
|
|
1986 |
person = models.ForeignKey(AstakosUser)
|
|
1987 |
request_date = models.DateTimeField(auto_now_add=True)
|
|
1988 |
project = models.ForeignKey(Project)
|
|
1979 | 1989 |
|
1980 |
REQUESTED = 0
|
|
1981 |
ACCEPTED = 1
|
|
1982 |
LEAVE_REQUESTED = 5
|
|
1990 |
REQUESTED = 0
|
|
1991 |
ACCEPTED = 1
|
|
1992 |
LEAVE_REQUESTED = 5
|
|
1983 | 1993 |
# User deactivation |
1984 |
USER_SUSPENDED = 10
|
|
1994 |
USER_SUSPENDED = 10
|
|
1985 | 1995 |
|
1986 |
REMOVED = 200
|
|
1996 |
REMOVED = 200
|
|
1987 | 1997 |
|
1988 |
ASSOCIATED_STATES = set([REQUESTED,
|
|
1989 |
ACCEPTED,
|
|
1990 |
LEAVE_REQUESTED,
|
|
1991 |
USER_SUSPENDED,
|
|
1992 |
])
|
|
1998 |
ASSOCIATED_STATES = set([REQUESTED,
|
|
1999 |
ACCEPTED, |
|
2000 |
LEAVE_REQUESTED, |
|
2001 |
USER_SUSPENDED, |
|
2002 |
]) |
|
1993 | 2003 |
|
1994 |
ACCEPTED_STATES = set([ACCEPTED,
|
|
1995 |
LEAVE_REQUESTED,
|
|
1996 |
USER_SUSPENDED,
|
|
1997 |
])
|
|
2004 |
ACCEPTED_STATES = set([ACCEPTED,
|
|
2005 |
LEAVE_REQUESTED, |
|
2006 |
USER_SUSPENDED, |
|
2007 |
]) |
|
1998 | 2008 |
|
1999 |
ACTUALLY_ACCEPTED = set([ACCEPTED, LEAVE_REQUESTED])
|
|
2009 |
ACTUALLY_ACCEPTED = set([ACCEPTED, LEAVE_REQUESTED])
|
|
2000 | 2010 |
|
2001 |
state = models.IntegerField(default=REQUESTED,
|
|
2002 |
db_index=True)
|
|
2003 |
acceptance_date = models.DateTimeField(null=True, db_index=True)
|
|
2004 |
leave_request_date = models.DateTimeField(null=True)
|
|
2011 |
state = models.IntegerField(default=REQUESTED,
|
|
2012 |
db_index=True) |
|
2013 |
acceptance_date = models.DateTimeField(null=True, db_index=True)
|
|
2014 |
leave_request_date = models.DateTimeField(null=True)
|
|
2005 | 2015 |
|
2006 |
objects = ProjectMembershipManager()
|
|
2016 |
objects = ProjectMembershipManager()
|
|
2007 | 2017 |
|
2008 | 2018 |
# Compiled queries |
2009 | 2019 |
Q_ACCEPTED_STATES = ~Q(state=REQUESTED) & ~Q(state=REMOVED) |
2010 | 2020 |
Q_ACTUALLY_ACCEPTED = Q(state=ACCEPTED) | Q(state=LEAVE_REQUESTED) |
2011 | 2021 |
|
2012 | 2022 |
MEMBERSHIP_STATE_DISPLAY = { |
2013 |
REQUESTED : _('Requested'),
|
|
2014 |
ACCEPTED : _('Accepted'),
|
|
2015 |
LEAVE_REQUESTED : _('Leave Requested'),
|
|
2016 |
USER_SUSPENDED : _('Suspended'),
|
|
2017 |
REMOVED : _('Pending removal'),
|
|
2018 |
}
|
|
2023 |
REQUESTED: _('Requested'),
|
|
2024 |
ACCEPTED: _('Accepted'),
|
|
2025 |
LEAVE_REQUESTED: _('Leave Requested'), |
|
2026 |
USER_SUSPENDED: _('Suspended'),
|
|
2027 |
REMOVED: _('Pending removal'),
|
|
2028 |
} |
|
2019 | 2029 |
|
2020 | 2030 |
USER_FRIENDLY_STATE_DISPLAY = { |
2021 |
REQUESTED : _('Join requested'),
|
|
2022 |
ACCEPTED : _('Accepted member'),
|
|
2023 |
LEAVE_REQUESTED : _('Requested to leave'),
|
|
2024 |
USER_SUSPENDED : _('Suspended member'),
|
|
2025 |
REMOVED : _('Pending removal'),
|
|
2026 |
}
|
|
2031 |
REQUESTED: _('Join requested'),
|
|
2032 |
ACCEPTED: _('Accepted member'),
|
|
2033 |
LEAVE_REQUESTED: _('Requested to leave'), |
|
2034 |
USER_SUSPENDED: _('Suspended member'),
|
|
2035 |
REMOVED: _('Pending removal'),
|
|
2036 |
} |
|
2027 | 2037 |
|
2028 | 2038 |
def state_display(self): |
2029 | 2039 |
return self.MEMBERSHIP_STATE_DISPLAY.get(self.state, _('Unknown')) |
... | ... | |
2036 | 2046 |
#index_together = [["project", "state"]] |
2037 | 2047 |
|
2038 | 2048 |
def __str__(self): |
2039 |
return uenc(_("<'%s' membership in '%s'>") % (
|
|
2040 |
self.person.username, self.project)) |
|
2049 |
return uenc(_("<'%s' membership in '%s'>") % |
|
2050 |
(self.person.username, self.project))
|
|
2041 | 2051 |
|
2042 | 2052 |
__repr__ = __str__ |
2043 | 2053 |
|
... | ... | |
2050 | 2060 |
reason = ProjectMembershipHistory.reasons.get(reason, -1) |
2051 | 2061 |
|
2052 | 2062 |
history_item = ProjectMembershipHistory( |
2053 |
serial=self.id,
|
|
2054 |
person=self.person_id,
|
|
2055 |
project=self.project_id,
|
|
2056 |
date=date or datetime.now(),
|
|
2057 |
reason=reason)
|
|
2063 |
serial=self.id, |
|
2064 |
person=self.person_id, |
|
2065 |
project=self.project_id, |
|
2066 |
date=date or datetime.now(), |
|
2067 |
reason=reason) |
|
2058 | 2068 |
history_item.save() |
2059 | 2069 |
serial = history_item.id |
2060 | 2070 |
|
... | ... | |
2150 | 2160 |
|
2151 | 2161 |
|
2152 | 2162 |
class Serial(models.Model): |
2153 |
serial = models.AutoField(primary_key=True)
|
|
2163 |
serial = models.AutoField(primary_key=True)
|
|
2154 | 2164 |
|
2155 | 2165 |
|
2156 | 2166 |
class ProjectMembershipHistory(models.Model): |
2157 |
reasons_list = ['ACCEPT', 'REJECT', 'REMOVE'] |
|
2158 |
reasons = dict((k, v) for v, k in enumerate(reasons_list)) |
|
2167 |
reasons_list = ['ACCEPT', 'REJECT', 'REMOVE'] |
|
2168 |
reasons = dict((k, v) for v, k in enumerate(reasons_list)) |
|
2169 |
|
|
2170 |
person = models.BigIntegerField() |
|
2171 |
project = models.BigIntegerField() |
|
2172 |
date = models.DateTimeField(auto_now_add=True) |
|
2173 |
reason = models.IntegerField() |
|
2174 |
serial = models.BigIntegerField() |
|
2159 | 2175 |
|
2160 |
person = models.BigIntegerField() |
|
2161 |
project = models.BigIntegerField() |
|
2162 |
date = models.DateTimeField(auto_now_add=True) |
|
2163 |
reason = models.IntegerField() |
|
2164 |
serial = models.BigIntegerField() |
|
2165 | 2176 |
|
2166 | 2177 |
### SIGNALS ### |
2167 | 2178 |
################ |
... | ... | |
2178 | 2189 |
except BaseException, e: |
2179 | 2190 |
logger.exception(e) |
2180 | 2191 |
|
2192 |
|
|
2181 | 2193 |
def fix_superusers(): |
2182 | 2194 |
# Associate superusers with AstakosUser |
2183 | 2195 |
admins = User.objects.filter(is_superuser=True) |
2184 | 2196 |
for u in admins: |
2185 | 2197 |
create_astakos_user(u) |
2186 | 2198 |
|
2199 |
|
|
2187 | 2200 |
def user_post_save(sender, instance, created, **kwargs): |
2188 | 2201 |
if not created: |
2189 | 2202 |
return |
2190 | 2203 |
create_astakos_user(instance) |
2191 | 2204 |
post_save.connect(user_post_save, sender=User) |
2192 | 2205 |
|
2206 |
|
|
2193 | 2207 |
def astakosuser_post_save(sender, instance, created, **kwargs): |
2194 | 2208 |
pass |
2195 | 2209 |
|
2196 | 2210 |
post_save.connect(astakosuser_post_save, sender=AstakosUser) |
2197 | 2211 |
|
2212 |
|
|
2198 | 2213 |
def resource_post_save(sender, instance, created, **kwargs): |
2199 | 2214 |
pass |
2200 | 2215 |
|
2201 | 2216 |
post_save.connect(resource_post_save, sender=Resource) |
2202 | 2217 |
|
2218 |
|
|
2203 | 2219 |
def renew_token(sender, instance, **kwargs): |
2204 | 2220 |
if not instance.auth_token: |
2205 | 2221 |
instance.renew_token() |
Also available in: Unified diff