commit 9b32e2fbe40b08c750870b638bd9d7ed17594feb
Author: Kostas Papadimitriou <kpap@grnet.gr>
Date:   Tue Jan 8 15:36:11 2013 +0200

    Projects list improvements

diff --git a/snf-astakos-app/astakos/im/models.py b/snf-astakos-app/astakos/im/models.py
index 951fb0a..3d8890c 100644
--- a/snf-astakos-app/astakos/im/models.py
+++ b/snf-astakos-app/astakos/im/models.py
@@ -1125,8 +1125,12 @@ class ProjectApplicationManager(ForUpdateManager):
         """
         Return projects accessed by specified user.
         """
-        return self.filter(Q(owner=user) | Q(applicant=user) | \
-                        Q(project__projectmembership__person=user)).order_by('pk').distinct()
+        participates_fitlers = Q(owner=user) | Q(applicant=user) | \
+                               Q(project__projectmembership__person=user)
+        state_filters = (Q(state=ProjectApplication.PENDING) & \
+                        Q(precursor_application__isnull=True)) | \
+                        Q(state=ProjectApplication.APPROVED)
+        return self.filter(participates_fitlers & state_filters).order_by('issue_date').distinct()
 
     def search_by_name(self, *search_strings):
         q = Q()
@@ -1252,6 +1256,21 @@ class ProjectApplication(models.Model):
         except ProjectApplication.DoesNotExist:
             return
 
+    def followers(self):
+        current = self
+        try:
+            while current.projectapplication:
+                yield current.follower
+                current = current.follower
+        except:
+            pass
+
+    def last_follower(self):
+        try:
+            return list(self.followers())[-1]
+        except IndexError:
+            return None
+
     def submit(self, resource_policies, applicant, comments,
                precursor_application=None):
 
diff --git a/snf-astakos-app/astakos/im/static/im/css/modules.css b/snf-astakos-app/astakos/im/static/im/css/modules.css
index 49c1e67..866e02c 100644
--- a/snf-astakos-app/astakos/im/static/im/css/modules.css
+++ b/snf-astakos-app/astakos/im/static/im/css/modules.css
@@ -557,3 +557,5 @@ form.withlabels.hidden-submit					{ margin-bottom:4em; }
     -webkit-transition: width 1s, color 0.2s; /* Safari and Chrome */
     -o-transition: width 1s, color 0.2s; /* Opera */
 }
+
+i.tiny { font-size: 0.8em; color: #999;}
diff --git a/snf-astakos-app/astakos/im/tables.py b/snf-astakos-app/astakos/im/tables.py
index 692cf25..ef00372 100644
--- a/snf-astakos-app/astakos/im/tables.py
+++ b/snf-astakos-app/astakos/im/tables.py
@@ -37,36 +37,39 @@ from django.utils.translation import ugettext as _
 from django.utils.safestring import mark_safe
 from django.template import Context, Template
 from django.template.loader import render_to_string
+from django.core.exceptions import PermissionDenied
 
 from django_tables2 import A
 import django_tables2 as tables
 
 from astakos.im.models import *
 from astakos.im.templatetags.filters import truncatename
+from astakos.im.functions import do_join_project_checks
 
 DEFAULT_DATE_FORMAT = "d/m/Y"
 
 
-MEMBER_STATUS_DISPLAY = {
-    100: _('Owner'),
-      0: _('Requested'),
-      1: _('Pending'),
-      2: _('Accepted'),
-      3: _('Removing'),
-      4: _('Removed'),
-     -1: _('Unregistered'),
-}
-
-class TruncatedLinkColumn(tables.LinkColumn):
+class LinkColumn(tables.LinkColumn):
 
     def __init__(self, *args, **kwargs):
-        self.truncate_chars = kwargs.pop('truncate_chars', 10)
-        super(TruncatedLinkColumn, self).__init__(*args, **kwargs)
-
+        self.coerce = kwargs.pop('coerce', None)
+        self.append = kwargs.pop('append', None)
+        super(LinkColumn, self).__init__(*args, **kwargs)
+
+    def render(self, value, record, bound_column):
+        link = super(LinkColumn, self).render(value, record, bound_column)
+        extra = ''
+        if self.append:
+            if callable(self.append):
+                extra = self.append(record, bound_column)
+            else:
+                extra = self.append
+        return mark_safe(link + extra)
 
     def render_link(self, uri, text, attrs=None):
-        text = truncatename(text, self.truncate_chars)
-        return super(TruncatedLinkColumn, self).render_link(uri, text, attrs)
+        if self.coerce:
+            text = self.coerce(text)
+        return super(LinkColumn, self).render_link(uri, text, attrs)
 
 
 # Helper columns
@@ -226,17 +229,30 @@ class UserTable(tables.Table):
         super(UserTable, self).__init__(*args, **kwargs)
 
 
+def project_name_append(record, column):
+    try:
+        if record.projectapplication and column.table.user.owns_project(record):
+            if record.state == ProjectApplication.APPROVED:
+                return mark_safe("<br /><i class='tiny'>%s</i>" % _('modifications pending'))
+            else:
+                return u''
+        else:
+            return u''
+    except:
+        return u''
+
 # Table classes
 class UserProjectApplicationsTable(UserTable):
     caption = _('My projects')
 
-    name = TruncatedLinkColumn('astakos.im.views.project_detail',
-                                                truncate_chars=25,
-                                                args=(A('pk'),))
-    issue_date = tables.DateColumn(format=DEFAULT_DATE_FORMAT)
+    name = LinkColumn('astakos.im.views.project_detail',
+                      coerce=lambda x: truncatename(x, 25),
+                      append=project_name_append,
+                      args=(A('pk'),))
+    issue_date = tables.DateColumn(verbose_name=_('Applied at'), format=DEFAULT_DATE_FORMAT)
     start_date = tables.DateColumn(format=DEFAULT_DATE_FORMAT)
-    end_date = tables.DateColumn(format=DEFAULT_DATE_FORMAT)
-    members_count = tables.Column(verbose_name=_("Enrolled"), default=0,
+    end_date = tables.DateColumn(verbose_name=_('Expires at'), format=DEFAULT_DATE_FORMAT)
+    members_count = tables.Column(verbose_name=_("Members"), default=0,
                                   sortable=False)
     membership_status = tables.Column(verbose_name=_("Status"), empty_values=(),
                                       sortable=False)
@@ -246,19 +262,28 @@ class UserProjectApplicationsTable(UserTable):
 
 
     def render_membership_status(self, record, *args, **kwargs):
-        status = record.user_status(self.user)
-        if status == 100:
-            return record.state
+        if self.user.owns_project(record):
+            return record.state_display()
         else:
-            return MEMBER_STATUS_DISPLAY.get(status, 'Unknown')
+            status = record.user_status(self.user)
+            return record.user_status_display(self.user)
 
     class Meta:
         model = ProjectApplication
-        fields = ('name', 'membership_status', 'issue_date', 'start_date','end_date', 'members_count')
+        fields = ('name', 'membership_status', 'issue_date', 'end_date', 'members_count')
         attrs = {'id': 'projects-list', 'class': 'my-projects alt-style'}
         template = "im/table_render.html"
         empty_text = _('No projects')
 
+class ProjectModificationApplicationsTable(UserProjectApplicationsTable):
+    name = LinkColumn('astakos.im.views.project_detail',
+                      verbose_name=_('Action'),
+                      coerce= lambda x: 'review',
+                      args=(A('pk'),))
+    class Meta:
+        attrs = {'id': 'projects-list', 'class': 'my-projects alt-style'}
+        fields = ('issue_date', 'membership_status')
+        exclude = ('start_date', 'end_date', 'members_count', 'project_action')
 
 def member_action_extra_context(membership, table, col):
 
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html b/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html
index 217576b..c22a190 100644
--- a/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html
+++ b/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html
@@ -109,11 +109,11 @@ href="{% url astakos.im.views.project_leave object.pk %}">LEAVE</a>
 		 	<dd>{% if object.limit_on_members_number%}{{object.limit_on_members_number}}{% else %}&nbsp;{% endif %}</dd>
 		 	<dt>Member join policy</dt>
             <dd>
-                {{ join_policies|lookup:object.member_join_policy }}
+                {{ join_policies|lookup:object.member_join_policy|title }}
             </dd>
 		 	<dt>Member leave policy</dt>
             <dd>
-                {{ leave_policies|lookup:object.member_leave_policy }}
+                {{ leave_policies|lookup:object.member_leave_policy|title }}
             </dd>
 		 </dl>
 	 </div>
@@ -170,6 +170,14 @@ href="{% url astakos.im.views.project_leave object.pk %}">LEAVE</a>
             </form>
         </div>
         {% endif %}
+        {% comment %}
+        {% if modifications_table %}
+         <div class="full-dotted">
+             <h3>MODIFICATION REQUESTS</h3>
+             {% render_table modifications_table %}
+         </div>
+         {% endif %}
+         {% endcomment %}
     {% endif %}
     <div class="full-dotted">
         <p>
diff --git a/snf-astakos-app/astakos/im/views.py b/snf-astakos-app/astakos/im/views.py
index 29ce0c0..e5ed518 100644
--- a/snf-astakos-app/astakos/im/views.py
+++ b/snf-astakos-app/astakos/im/views.py
@@ -1136,6 +1136,15 @@ def project_detail(request, application_id):
                                                           prefix="members_")
     RequestConfig(request, paginate={"per_page": PAGINATE_BY}).configure(members_table)
 
+    modifications_table = None
+    if application.follower:
+        following_applications = list(application.followers())
+        following_applications.reverse()
+        modifications_table = \
+            tables.ProjectModificationApplicationsTable(following_applications,
+                                                       user=request.user,
+                                                       prefix="modifications_")
+
     return object_detail(
         request,
         queryset=ProjectApplication.objects.select_related(),
