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