commit 398a9604ae6ebe3045b42226ba46ab31a38ac4bf
Author: Giorgos Korfiatis <gkorf@grnet.gr>
Date:   Mon May 20 17:10:39 2013 +0300

    astakos: Include project membership info in commands
    
      `user-show --projects': Memberships and ownerships for a given user
                              in separate tables
      `project-show --members': Members of the given project

diff --git a/snf-astakos-app/astakos/im/management/commands/project-show.py b/snf-astakos-app/astakos/im/management/commands/project-show.py
index a958204..d831630 100644
--- a/snf-astakos-app/astakos/im/management/commands/project-show.py
+++ b/snf-astakos-app/astakos/im/management/commands/project-show.py
@@ -41,16 +41,8 @@ from astakos.im.models import Chain, ProjectApplication
 
 
 class Command(SynnefoCommand):
-    args = "<id or name>"
-    help = """
-    Show project details.
-
-    Command comes in two forms:
-        project-show <id>    Look up project by id
-
-        project-show <name>  Look up all projects whose name
-                             contains the given string
-"""
+    args = "<id>"
+    help = "Show details for project (or application) <id>"
 
     option_list = SynnefoCommand.option_list + (
         make_option('--app',
@@ -63,8 +55,14 @@ class Command(SynnefoCommand):
                     action='store_true',
                     dest='pending',
                     default=False,
-                    help=("For a given project, show also pending modifications"
-                          "(applications), if any")
+                    help=("For a given project, show also pending "
+                          "modifications (applications), if any")
+                    ),
+        make_option('--members',
+                    action='store_true',
+                    dest='members',
+                    default=False,
+                    help=("Show a list of project memberships")
                     ),
     )
 
@@ -73,73 +71,59 @@ class Command(SynnefoCommand):
             raise CommandError("Please provide project ID or name")
 
         show_pending = bool(options['pending'])
+        show_members = bool(options['members'])
         search_apps = options['app']
+        self.output_format = options['output_format']
 
-        name_or_id = args[0]
-        is_id = name_or_id.isdigit()
-        if is_id:
-            name_or_id = int(name_or_id)
+        id_ = args[0]
+        try:
+            id_ = int(id_)
+        except ValueError:
+            raise CommandError("id should be an integer value.")
 
         if search_apps:
-            infolist = app_info(name_or_id, is_id)
+            self.pprint_dict(app_info(id_))
         else:
-            chains = get_chains(name_or_id, is_id)
-            infolist = collect_info(chains, show_pending)
-
-        if not infolist:
-            kind = 'project application' if search_apps else 'project'
-            field = 'id' if is_id else 'name'
-            msg = "Unknown %s with %s '%s'" % (kind, field, name_or_id)
-            raise CommandError(msg)
-
-        first = True
-        for info in infolist:
-            if not first:
+            state, project, app = get_chain_state(id_)
+            self.pprint_dict(chain_fields(state, project, app))
+            if show_members and project is not None:
                 self.stdout.write("\n")
-            else:
-                first = False
-            utils.pprint_table(self.stdout, [info.values()], info.keys(),
-                               options["output_format"], vertical=True)
-
+                fields, labels = members_fields(project)
+                self.pprint_table(fields, labels)
+            if show_pending and state in Chain.PENDING_STATES:
+                self.stdout.write("\n")
+                self.pprint_dict(app_fields(app))
 
-def app_info(name_or_id, is_id):
-    try:
-        apps = ([ProjectApplication.objects.get(id=name_or_id)]
-                if is_id
-                else ProjectApplication.objects.search_by_name(name_or_id))
-        return [app_fields(app) for app in apps]
-    except ProjectApplication.DoesNotExist:
-            return []
+    def pprint_dict(self, d, vertical=True):
+        utils.pprint_table(self.stdout, [d.values()], d.keys(),
+                           self.output_format, vertical=vertical)
 
+    def pprint_table(self, tbl, labels):
+        utils.pprint_table(self.stdout, tbl, labels,
+                           self.output_format)
 
-def get_chains(name_or_id, is_id):
-    if is_id:
-        try:
-            return [Chain.objects.get(chain=name_or_id)]
-        except Chain.DoesNotExist:
-            return []
-    else:
-        return Chain.objects.search_by_name(name_or_id)
 
+def app_info(app_id):
+    try:
+        app = ProjectApplication.objects.get(id=app_id)
+        return app_fields(app)
+    except ProjectApplication.DoesNotExist:
+        raise CommandError("Application with id %s not found." % app_id)
 
-def collect_info(chains, pending):
-    states = [chain.full_state() for chain in chains]
 
-    infolist = []
-    for state in states:
-        infolist += (chain_fields(state, pending))
-    return infolist
+def get_chain_state(project_id):
+    try:
+        chain = Chain.objects.get(chain=project_id)
+        return chain.full_state()
+    except Chain.DoesNotExist:
+        raise CommandError("Project with id %s not found." % project_id)
 
 
-def chain_fields((s, project, app), request=False):
-    l = []
-    if project:
-        l = [project_fields(s, project, app)]
-        if request and s in Chain.PENDING_STATES:
-            l.append(app_fields(app))
+def chain_fields(state, project, app):
+    if project is not None:
+        return project_fields(state, project, app)
     else:
-        l = [app_fields(app)]
-    return l
+        return app_fields(app)
 
 
 def app_fields(app):
@@ -206,18 +190,18 @@ def project_fields(s, project, last_app):
             ('total members', project.members_count()),
     ])
 
-    memberships = project.projectmembership_set
-    accepted = [str(m.person) for m in memberships.any_accepted()]
-    requested = [str(m.person) for m in memberships.requested()]
-    suspended = [str(m.person) for m in memberships.suspended()]
-
-    if accepted:
-        d['accepted members'] = ', '.join(accepted)
+    return d
 
-    if suspended:
-        d['suspended members'] = ', '.join(suspended)
 
-    if requested:
-        d['membership requests'] = ', '.join(requested)
+def members_fields(project):
+    labels = ('member uuid', 'email', 'status')
+    objs = project.projectmembership_set.select_related('person')
+    memberships = objs.all().order_by('state', 'person__email')
+    collect = []
+    for m in memberships:
+        user = m.person
+        collect.append((user.uuid,
+                       user.email,
+                       m.state_display()))
 
-    return d
+    return collect, labels
diff --git a/snf-astakos-app/astakos/im/management/commands/user-show.py b/snf-astakos-app/astakos/im/management/commands/user-show.py
index f71fa56..ee67a4d 100644
--- a/snf-astakos-app/astakos/im/management/commands/user-show.py
+++ b/snf-astakos-app/astakos/im/management/commands/user-show.py
@@ -34,7 +34,7 @@
 from django.core.management.base import CommandError
 from optparse import make_option
 
-from astakos.im.models import AstakosUser, get_latest_terms
+from astakos.im.models import AstakosUser, get_latest_terms, Chain
 from astakos.im.quotas import list_user_quotas
 
 from synnefo.lib.ordereddict import OrderedDict
@@ -56,6 +56,11 @@ class Command(SynnefoCommand):
                     dest='list_quotas',
                     default=False,
                     help="Also list user quota"),
+        make_option('--projects',
+                    action='store_true',
+                    dest='list_projects',
+                    default=False,
+                    help="Also list project memberships"),
     )
 
     def handle(self, *args, **options):
@@ -122,3 +127,71 @@ class Command(SynnefoCommand):
                 print_data, labels = show_quotas(quotas, initial)
                 utils.pprint_table(self.stdout, print_data, labels,
                                    options["output_format"])
+
+            if options["list_projects"]:
+                print_data, labels = ownerships(user)
+                if print_data:
+                    self.stdout.write("\n")
+                    utils.pprint_table(self.stdout, print_data, labels,
+                                       options["output_format"],
+                                       title="Owned Projects")
+
+                print_data, labels = memberships(user)
+                if print_data:
+                    self.stdout.write("\n")
+                    utils.pprint_table(self.stdout, print_data, labels,
+                                       options["output_format"],
+                                       title="Project Memberships")
+
+
+def memberships(user):
+    ms = user.projectmembership_set.all()
+    print_data = []
+    labels = ('project id', 'project name', 'status')
+
+    for m in ms:
+        project = m.project
+        print_data.append((project.id,
+                           project.application.name,
+                           m.state_display(),
+                           ))
+    return print_data, labels
+
+
+def ownerships(user):
+    chain_dict = Chain.objects.all_full_state()
+    chain_dict = filter_by(is_owner(user), chain_dict)
+    return chain_info(chain_dict)
+
+
+def is_owner(user):
+    def f(state, project, app):
+        return user == app.owner
+    return f
+
+
+def filter_by(f, chain_dict):
+    d = {}
+    for chain, tpl in chain_dict.iteritems():
+        if f(*tpl):
+            d[chain] = tpl
+    return d
+
+
+def chain_info(chain_dict):
+    labels = ('project id', 'project name', 'status', 'pending app id')
+    l = []
+    for chain, (state, project, app) in chain_dict.iteritems():
+        status = Chain.state_display(state)
+        if state in Chain.PENDING_STATES:
+            appid = str(app.id)
+        else:
+            appid = ""
+
+        t = (chain,
+             project.application.name if project else app.name,
+             status,
+             appid,
+             )
+        l.append(t)
+    return l, labels
