Revision 362dadaa snf-astakos-app/astakos/im/models.py
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
512 | 512 |
def is_accepted(self): |
513 | 513 |
return self.moderated and not self.is_rejected |
514 | 514 |
|
515 |
def is_project_admin(self, application_id=None):
|
|
515 |
def is_project_admin(self): |
|
516 | 516 |
return self.uuid in astakos_settings.PROJECT_ADMINS |
517 | 517 |
|
518 | 518 |
@property |
... | ... | |
781 | 781 |
return application.owner == self |
782 | 782 |
|
783 | 783 |
def owns_project(self, project): |
784 |
return project.application.owner == self
|
|
784 |
return project.owner == self |
|
785 | 785 |
|
786 | 786 |
def is_associated(self, project): |
787 | 787 |
try: |
... | ... | |
1498 | 1498 |
self.display_member_capacity()) |
1499 | 1499 |
|
1500 | 1500 |
|
1501 |
def _distinct(f, l): |
|
1502 |
d = {} |
|
1503 |
last = None |
|
1504 |
for x in l: |
|
1505 |
group = f(x) |
|
1506 |
if group == last: |
|
1507 |
continue |
|
1508 |
last = group |
|
1509 |
d[group] = x |
|
1510 |
return d |
|
1511 |
|
|
1512 |
|
|
1513 |
def invert_dict(d): |
|
1514 |
return dict((v, k) for k, v in d.iteritems()) |
|
1515 |
|
|
1516 |
|
|
1517 | 1501 |
class ProjectManager(models.Manager): |
1518 |
|
|
1519 |
def all_with_pending(self, flt=None): |
|
1520 |
flt = Q() if flt is None else flt |
|
1521 |
projects = list(self.select_related( |
|
1522 |
'application', 'application__owner').filter(flt)) |
|
1523 |
|
|
1524 |
objs = ProjectApplication.objects.select_related('owner') |
|
1525 |
apps = objs.filter(state=ProjectApplication.PENDING, |
|
1526 |
chain__in=projects).order_by('chain', '-id') |
|
1527 |
app_d = _distinct(lambda app: app.chain_id, apps) |
|
1528 |
return [(project, app_d.get(project.pk)) for project in projects] |
|
1529 |
|
|
1530 | 1502 |
def expired_projects(self): |
1531 | 1503 |
model = self.model |
1532 |
q = ((model.o_state_q(model.O_ACTIVE) | |
|
1533 |
model.o_state_q(model.O_SUSPENDED)) & |
|
1534 |
Q(application__end_date__lt=datetime.now())) |
|
1504 |
q = (Q(state__in=[model.NORMAL, model.SUSPENDED]) & |
|
1505 |
Q(end_date__lt=datetime.now())) |
|
1535 | 1506 |
return self.filter(q) |
1536 | 1507 |
|
1537 | 1508 |
def user_accessible_projects(self, user): |
... | ... | |
1544 | 1515 |
else: |
1545 | 1516 |
membs = user.projectmembership_set.associated() |
1546 | 1517 |
memb_projects = membs.values_list("project", flat=True) |
1547 |
flt = (Q(application__owner=user) |
|
|
1548 |
Q(application__applicant=user) | |
|
1518 |
flt = (Q(owner=user) | |
|
1519 |
Q(last_application__applicant=user) |
|
|
1549 | 1520 |
Q(id__in=memb_projects)) |
1550 | 1521 |
|
1551 |
relevant = model.o_states_q(model.RELEVANT_STATES)
|
|
1522 |
relevant = ~Q(state=model.DELETED)
|
|
1552 | 1523 |
return self.filter(flt, relevant).order_by( |
1553 |
'application__issue_date').select_related( |
|
1554 |
'application', 'application__owner', 'application__applicant') |
|
1524 |
'creation_date').select_related('last_application', 'owner') |
|
1555 | 1525 |
|
1556 | 1526 |
def search_by_name(self, *search_strings): |
1557 | 1527 |
q = Q() |
... | ... | |
1559 | 1529 |
q = q | Q(name__icontains=s) |
1560 | 1530 |
return self.filter(q) |
1561 | 1531 |
|
1532 |
def initialized(self, flt=None): |
|
1533 |
q = Q(state__in=self.model.INITIALIZED_STATES) |
|
1534 |
if flt is not None: |
|
1535 |
q &= flt |
|
1536 |
return self.filter(q) |
|
1537 |
|
|
1562 | 1538 |
|
1563 | 1539 |
class Project(models.Model): |
1564 | 1540 |
|
... | ... | |
1582 | 1558 |
NORMAL = 1 |
1583 | 1559 |
SUSPENDED = 10 |
1584 | 1560 |
TERMINATED = 100 |
1561 |
DELETED = 1000 |
|
1562 |
|
|
1563 |
INITIALIZED_STATES = [NORMAL, |
|
1564 |
SUSPENDED, |
|
1565 |
TERMINATED, |
|
1566 |
] |
|
1567 |
|
|
1568 |
ALIVE_STATES = [NORMAL, |
|
1569 |
SUSPENDED, |
|
1570 |
] |
|
1571 |
|
|
1572 |
SKIP_STATES = [DELETED, |
|
1573 |
TERMINATED, |
|
1574 |
] |
|
1585 | 1575 |
|
1586 | 1576 |
DEACTIVATED_STATES = [SUSPENDED, TERMINATED] |
1587 | 1577 |
|
1588 |
state = models.IntegerField(default=NORMAL,
|
|
1578 |
state = models.IntegerField(default=UNINITIALIZED,
|
|
1589 | 1579 |
db_index=True) |
1590 | 1580 |
uuid = models.CharField(max_length=255, unique=True) |
1591 | 1581 |
|
... | ... | |
1612 | 1602 |
|
1613 | 1603 |
def __str__(self): |
1614 | 1604 |
return uenc(_("<project %s '%s'>") % |
1615 |
(self.id, udec(self.application.name)))
|
|
1605 |
(self.id, udec(self.realname)))
|
|
1616 | 1606 |
|
1617 | 1607 |
__repr__ = __str__ |
1618 | 1608 |
|
1619 | 1609 |
def __unicode__(self): |
1620 |
return _("<project %s '%s'>") % (self.id, self.application.name)
|
|
1610 |
return _("<project %s '%s'>") % (self.id, self.realname)
|
|
1621 | 1611 |
|
1612 |
O_UNINITIALIZED = -1 |
|
1622 | 1613 |
O_PENDING = 0 |
1623 | 1614 |
O_ACTIVE = 1 |
1615 |
O_ACTIVE_PENDING = 2 |
|
1624 | 1616 |
O_DENIED = 3 |
1625 | 1617 |
O_DISMISSED = 4 |
1626 | 1618 |
O_CANCELLED = 5 |
1627 | 1619 |
O_SUSPENDED = 10 |
1628 | 1620 |
O_TERMINATED = 100 |
1621 |
O_DELETED = 1000 |
|
1629 | 1622 |
|
1630 | 1623 |
O_STATE_DISPLAY = { |
1624 |
O_UNINITIALIZED: _("Uninitialized"), |
|
1631 | 1625 |
O_PENDING: _("Pending"), |
1632 | 1626 |
O_ACTIVE: _("Active"), |
1633 | 1627 |
O_DENIED: _("Denied"), |
... | ... | |
1635 | 1629 |
O_CANCELLED: _("Cancelled"), |
1636 | 1630 |
O_SUSPENDED: _("Suspended"), |
1637 | 1631 |
O_TERMINATED: _("Terminated"), |
1632 |
O_DELETED: _("Deleted"), |
|
1638 | 1633 |
} |
1639 | 1634 |
|
1640 |
OVERALL_STATE = { |
|
1641 |
(NORMAL, ProjectApplication.PENDING): O_PENDING, |
|
1642 |
(NORMAL, ProjectApplication.APPROVED): O_ACTIVE, |
|
1643 |
(NORMAL, ProjectApplication.DENIED): O_DENIED, |
|
1644 |
(NORMAL, ProjectApplication.DISMISSED): O_DISMISSED, |
|
1645 |
(NORMAL, ProjectApplication.CANCELLED): O_CANCELLED, |
|
1646 |
(SUSPENDED, ProjectApplication.APPROVED): O_SUSPENDED, |
|
1647 |
(TERMINATED, ProjectApplication.APPROVED): O_TERMINATED, |
|
1648 |
} |
|
1649 |
|
|
1650 |
OVERALL_STATE_INV = invert_dict(OVERALL_STATE) |
|
1651 |
|
|
1652 |
@classmethod |
|
1653 |
def o_state_q(cls, o_state): |
|
1654 |
p_state, a_state = cls.OVERALL_STATE_INV[o_state] |
|
1655 |
return Q(state=p_state, application__state=a_state) |
|
1656 |
|
|
1657 |
@classmethod |
|
1658 |
def o_states_q(cls, o_states): |
|
1659 |
return reduce(lambda x, y: x | y, map(cls.o_state_q, o_states), Q()) |
|
1660 |
|
|
1661 |
INITIALIZED_STATES = [O_ACTIVE, |
|
1662 |
O_SUSPENDED, |
|
1663 |
O_TERMINATED, |
|
1664 |
] |
|
1665 |
|
|
1666 |
RELEVANT_STATES = [O_PENDING, |
|
1667 |
O_DENIED, |
|
1668 |
O_ACTIVE, |
|
1669 |
O_SUSPENDED, |
|
1670 |
O_TERMINATED, |
|
1671 |
] |
|
1635 |
O_STATE_UNINITIALIZED = { |
|
1636 |
None: O_UNINITIALIZED, |
|
1637 |
ProjectApplication.PENDING: O_PENDING, |
|
1638 |
ProjectApplication.DENIED: O_DENIED, |
|
1639 |
} |
|
1640 |
O_STATE_DELETED = { |
|
1641 |
None: O_DELETED, |
|
1642 |
ProjectApplication.DISMISSED: O_DISMISSED, |
|
1643 |
ProjectApplication.CANCELLED: O_CANCELLED, |
|
1644 |
} |
|
1672 | 1645 |
|
1673 |
SKIP_STATES = [O_DISMISSED, |
|
1674 |
O_CANCELLED, |
|
1675 |
O_TERMINATED, |
|
1676 |
] |
|
1646 |
OVERALL_STATE = { |
|
1647 |
NORMAL: lambda app_state: Project.O_ACTIVE, |
|
1648 |
UNINITIALIZED: lambda app_state: Project.O_STATE_UNINITIALIZED.get( |
|
1649 |
app_state, None), |
|
1650 |
DELETED: lambda app_state: Project.O_STATE_DELETED.get( |
|
1651 |
app_state, None), |
|
1652 |
SUSPENDED: lambda app_state: Project.O_SUSPENDED, |
|
1653 |
TERMINATED: lambda app_state: Project.O_TERMINATED, |
|
1654 |
} |
|
1677 | 1655 |
|
1678 | 1656 |
@classmethod |
1679 | 1657 |
def _overall_state(cls, project_state, app_state): |
1680 |
return cls.OVERALL_STATE.get((project_state, app_state), None) |
|
1658 |
os = cls.OVERALL_STATE.get(project_state, None) |
|
1659 |
if os is None: |
|
1660 |
return None |
|
1661 |
return os(app_state) |
|
1681 | 1662 |
|
1682 | 1663 |
def overall_state(self): |
1683 |
return self._overall_state(self.state, self.application.state) |
|
1664 |
app_state = (self.last_application.state |
|
1665 |
if self.last_application else None) |
|
1666 |
return self._overall_state(self.state, app_state) |
|
1684 | 1667 |
|
1685 | 1668 |
def last_pending_application(self): |
1686 |
apps = self.chained_apps.filter( |
|
1687 |
state=ProjectApplication.PENDING).order_by('-id') |
|
1688 |
if apps: |
|
1689 |
return apps[0] |
|
1669 |
app = self.last_application |
|
1670 |
if app and app.state == ProjectApplication.PENDING: |
|
1671 |
return app |
|
1690 | 1672 |
return None |
1691 | 1673 |
|
1692 | 1674 |
def last_pending_modification(self): |
1693 | 1675 |
last_pending = self.last_pending_application() |
1694 |
if last_pending == self.application:
|
|
1695 |
return None
|
|
1696 |
return last_pending
|
|
1676 |
if self.state != Project.UNINITIALIZED:
|
|
1677 |
return last_pending
|
|
1678 |
return None
|
|
1697 | 1679 |
|
1698 | 1680 |
def state_display(self): |
1699 | 1681 |
return self.O_STATE_DISPLAY.get(self.overall_state(), _('Unknown')) |
1700 | 1682 |
|
1701 | 1683 |
def expiration_info(self): |
1702 | 1684 |
return (str(self.id), self.name, self.state_display(), |
1703 |
str(self.application.end_date))
|
|
1685 |
str(self.end_date)) |
|
1704 | 1686 |
|
1705 | 1687 |
def last_deactivation(self): |
1706 | 1688 |
objs = self.log.filter(to_state__in=self.DEACTIVATED_STATES) |
... | ... | |
1716 | 1698 |
return self.state != self.NORMAL |
1717 | 1699 |
|
1718 | 1700 |
def is_active(self): |
1719 |
return self.overall_state() == self.O_ACTIVE
|
|
1701 |
return self.state == self.NORMAL
|
|
1720 | 1702 |
|
1721 | 1703 |
def is_initialized(self): |
1722 |
return self.overall_state() in self.INITIALIZED_STATES
|
|
1704 |
return self.state in self.INITIALIZED_STATES
|
|
1723 | 1705 |
|
1724 | 1706 |
### Deactivation calls |
1725 | 1707 |
|
... | ... | |
1746 | 1728 |
def resume(self, actor=None, reason=None): |
1747 | 1729 |
self.set_state(self.NORMAL, actor=actor, reason=reason) |
1748 | 1730 |
if self.name is None: |
1749 |
self.name = self.application.name |
|
1731 |
self.name = self.realname |
|
1732 |
self.save() |
|
1733 |
|
|
1734 |
def activate(self, actor=None, reason=None): |
|
1735 |
assert self.state != self.DELETED, \ |
|
1736 |
"cannot activate: %s is deleted" % self |
|
1737 |
if self.state != self.NORMAL: |
|
1738 |
self.set_state(self.NORMAL, actor=actor, reason=reason) |
|
1739 |
if self.name != self.realname: |
|
1740 |
self.name = self.realname |
|
1750 | 1741 |
self.save() |
1751 | 1742 |
|
1743 |
def set_deleted(self, actor=None, reason=None): |
|
1744 |
self.set_state(self.DELETED, actor=actor, reason=reason) |
|
1745 |
|
|
1752 | 1746 |
### Logical checks |
1753 | 1747 |
|
1754 | 1748 |
@property |
1755 | 1749 |
def is_alive(self): |
1756 |
return self.overall_state() in [self.O_ACTIVE, self.O_SUSPENDED]
|
|
1750 |
return self.state in [self.NORMAL, self.SUSPENDED]
|
|
1757 | 1751 |
|
1758 | 1752 |
@property |
1759 | 1753 |
def is_terminated(self): |
... | ... | |
1764 | 1758 |
return self.is_deactivated(self.SUSPENDED) |
1765 | 1759 |
|
1766 | 1760 |
def violates_members_limit(self, adding=0): |
1767 |
application = self.application |
|
1768 |
limit = application.limit_on_members_number |
|
1769 |
if limit is None: |
|
1770 |
return False |
|
1761 |
limit = self.limit_on_members_number |
|
1771 | 1762 |
return (len(self.approved_members) + adding > limit) |
1772 | 1763 |
|
1773 | 1764 |
### Other |
... | ... | |
1787 | 1778 |
def approved_members(self): |
1788 | 1779 |
return [m.person for m in self.approved_memberships] |
1789 | 1780 |
|
1781 |
@property |
|
1782 |
def member_join_policy_display(self): |
|
1783 |
policy = self.member_join_policy |
|
1784 |
return presentation.PROJECT_MEMBER_JOIN_POLICIES.get(policy) |
|
1785 |
|
|
1786 |
@property |
|
1787 |
def member_leave_policy_display(self): |
|
1788 |
policy = self.member_leave_policy |
|
1789 |
return presentation.PROJECT_MEMBER_LEAVE_POLICIES.get(policy) |
|
1790 |
|
|
1790 | 1791 |
|
1791 | 1792 |
def create_project(**kwargs): |
1792 | 1793 |
if "uuid" not in kwargs: |
Also available in: Unified diff