commit 7538030899d80b1d9d9832cfcb3e075ad86b0e61
Author: Kostas Papadimitriou <kpap@grnet.gr>
Date:   Wed Apr 17 14:23:46 2013 +0300

    Use updated resource models in projects views

diff --git a/snf-astakos-app/astakos/im/forms.py b/snf-astakos-app/astakos/im/forms.py
index 7e01874..215054d 100644
--- a/snf-astakos-app/astakos/im/forms.py
+++ b/snf-astakos-app/astakos/im/forms.py
@@ -66,7 +66,7 @@ from astakos.im.settings import (
     MODERATION_ENABLED, PROJECT_MEMBER_JOIN_POLICIES,
     PROJECT_MEMBER_LEAVE_POLICIES, EMAILCHANGE_ENABLED,
     )
-from astakos.im.presentation import RESOURCES_PRESENTATION_DATA
+from astakos.im import presentation
 from astakos.im.widgets import DummyWidget, RecaptchaWidget
 from astakos.im.functions import (
     send_change_email, submit_application, accept_membership_checks)
@@ -878,7 +878,7 @@ class ProjectApplicationForm(forms.ModelForm):
                         d.update(dict(resource=prefix, uplimit=None))
                     append(d)
 
-        ordered_keys = RESOURCES_PRESENTATION_DATA['resources_order']
+        ordered_keys = presentation.RESOURCES['resources_order']
         policies = sorted(policies, key=lambda r:ordered_keys.index(r['str_repr']))
         return policies
 
diff --git a/snf-astakos-app/astakos/im/models.py b/snf-astakos-app/astakos/im/models.py
index aeb5173..bf49a5f 100644
--- a/snf-astakos-app/astakos/im/models.py
+++ b/snf-astakos-app/astakos/im/models.py
@@ -79,7 +79,7 @@ from synnefo.lib.db.managers import ForUpdateManager
 from astakos.quotaholder.api import QH_PRACTICALLY_INFINITE
 from synnefo.lib.db.intdecimalfield import intDecimalField
 from synnefo.util.text import uenc, udec
-from astakos.im.presentation import RESOURCES_PRESENTATION_DATA
+from astakos.im import presentation
 
 logger = logging.getLogger(__name__)
 
@@ -134,12 +134,12 @@ class Service(models.Model):
 _presentation_data = {}
 def get_presentation(resource):
     global _presentation_data
-    presentation = _presentation_data.get(resource, {})
-    if not presentation:
-        resource_presentation = RESOURCES_PRESENTATION_DATA.get('resources', {})
-        presentation = resource_presentation.get(resource, {})
-        _presentation_data[resource] = presentation
-    return presentation
+    resource_presentation = _presentation_data.get(resource, {})
+    if not resource_presentation:
+        resources_presentation = presentation.RESOURCES.get('resources', {})
+        resource_presentation = resources_presentation.get(resource, {})
+        _presentation_data[resource] = resource_presentation
+    return resource_presentation
 
 class Resource(models.Model):
     name = models.CharField(_('Name'), max_length=255, unique=True)
diff --git a/snf-astakos-app/astakos/im/presentation.py b/snf-astakos-app/astakos/im/presentation.py
index 2c5a969..ce180a2 100644
--- a/snf-astakos-app/astakos/im/presentation.py
+++ b/snf-astakos-app/astakos/im/presentation.py
@@ -31,7 +31,9 @@
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
-RESOURCES_PRESENTATION_DATA = {
+from astakos.im import settings
+
+RESOURCES = {
     'groups': {
         'compute': {
             'help_text': ('Compute resources '
@@ -55,7 +57,7 @@ RESOURCES_PRESENTATION_DATA = {
         },
     },
     'resources': {
-        'pithos+.diskspace': {
+        'pithos.diskspace': {
             'help_text': ('This is the space on Pithos for storing files '
                           'and VM Images. '),
             'help_text_input_each': ('This is the total amount of space on '
@@ -65,6 +67,7 @@ RESOURCES_PRESENTATION_DATA = {
             'report_desc': 'Storage Space',
             'placeholder': 'eg. 10GB',
             'verbose_name': 'Storage Space',
+            'group': 'storage'
         },
         'cyclades.disk': {
             'help_text': ('This is the System Disk that the VMs have that '
@@ -77,7 +80,8 @@ RESOURCES_PRESENTATION_DATA = {
             'is_abbreviation': False,
             'report_desc': 'System Disk',
             'placeholder': 'eg. 5GB, 2GB etc',
-            'verbose_name': 'System Disk'
+            'verbose_name': 'System Disk',
+            'group': 'compute'
         },
         'cyclades.ram': {
             'help_text': 'RAM used by VMs ',
@@ -87,7 +91,9 @@ RESOURCES_PRESENTATION_DATA = {
             'is_abbreviation': True,
             'report_desc': 'RAM',
             'placeholder': 'eg. 4GB',
-            'verbose_name': 'ram'
+            'verbose_name': 'ram',
+            'group': 'compute'
+
         },
         'cyclades.cpu': {
             'help_text': 'CPUs used by VMs ',
@@ -97,7 +103,9 @@ RESOURCES_PRESENTATION_DATA = {
             'is_abbreviation': True,
             'report_desc': 'CPUs',
             'placeholder': 'eg. 1',
-            'verbose_name': 'cpu'
+            'verbose_name': 'cpu',
+            'group': 'compute'
+
         },
         'cyclades.vm': {
             'help_text': ('These are the VMs one can create on the '
@@ -109,6 +117,8 @@ RESOURCES_PRESENTATION_DATA = {
             'report_desc': 'Virtual Machines',
             'placeholder': 'eg. 2',
             'verbose_name': 'vm',
+            'group': 'compute'
+
         },
         'cyclades.network.private': {
             'help_text': ('These are the Private Networks one can create on '
@@ -119,11 +129,13 @@ RESOURCES_PRESENTATION_DATA = {
             'is_abbreviation': False,
             'report_desc': 'Private Networks',
             'placeholder': 'eg. 1',
-            'verbose_name': 'Private Network'
+            'verbose_name': 'Private Network',
+            'group': 'network'
+
         }
     },
     'groups_order': ['storage', 'compute', 'network'],
-    'resources_order': ['pithos+.diskspace',
+    'resources_order': ['pithos.diskspace',
                         'cyclades.disk',
                         'cyclades.cpu',
                         'cyclades.ram',
diff --git a/snf-astakos-app/astakos/im/resources.py b/snf-astakos-app/astakos/im/resources.py
index a9905ca..ec79628 100644
--- a/snf-astakos-app/astakos/im/resources.py
+++ b/snf-astakos-app/astakos/im/resources.py
@@ -54,7 +54,7 @@ def add_resource(service, resource, uplimit):
         old_uplimit = None
 
     r.uplimit = uplimit
-    r.service = s
+    r.service = service
     for key, value in resource.iteritems():
         setattr(r, key, value)
 
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html
index b3da6d9..39e857d 100644
--- a/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html
+++ b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html
@@ -113,12 +113,16 @@ exactly your request.</p>
 	    	</span>    
     	</legend>
     	<ul class="clearfix">
-    	    {% with object|resource_groups as groups %}
+            {% with object|resource_groups as groups %}
             {% for g, group_info in resource_groups.items %}
                 {% if g %}
 	    		<li>
-	    		    <a href="#{{ g }}" id="{{'group_'|add:g}}" {% if g in groups %}class="selected"{% endif %}><img src="/static/im/images/create-{{ g }}.png" alt="vm"/></a>
-	    			<input type="hidden" name="proxy_{{ 'is_selected_'|add:g }}"  id="proxy_{{ 'id_is_selected_'|add:g }}" {% if g in groups %}value="1"{% else %}value="0"{% endif %}>
+                <a href="#{{ g }}" 
+                    id="{{'group_'|add:g}}" 
+                    {% if g in groups %}class="selected"{% endif %}>
+                    <img src="/static/im/images/create-{{ g }}.png" alt="vm"/></a>
+                <input type="hidden" name="proxy_{{ 'is_selected_'|add:g }}" 
+                id="proxy_{{ 'id_is_selected_'|add:g }}" {% if g in groups %}value="1"{% else %}value="0"{% endif %}>
 	    			<input type="hidden" name="{{ 'is_selected_'|add:g }}"  id="{{ 'id_is_selected_'|add:g }}" {% if g in groups %}value="1"{% else %}value="0"{% endif %}>
 	                <p class="msg">{{ group_info.help_text }}</p>
 	    		</li>
@@ -132,7 +136,7 @@ exactly your request.</p>
     <div class="not-visible">
     	{% for gname, resources in resource_catalog %}
      	    <div class="group {{'group_'|add:gname}}" id="{{ gname }}">
-	    		<a href="#icons" class="delete">X remove resource</a>	
+                <a href="#icons" class="delete">X remove resource</a>	
 	    		{% for rdata in resources %}
                 {% with rdata.str_repr as rname %}
 	    		<fieldset class="quota">
@@ -167,8 +171,7 @@ exactly your request.</p>
 										{% else %}
 											value = "{% get_grant_value rname form %}"
 										{% endif %}
-			       						autocomplete="off"			
-							> 
+                                        autocomplete="off"> 
 			       			<span class="extra-img">&nbsp;</span>
 			         		<span class="info"><em>more info</em><span>{{ rdata.help_text_input_each }}</span></span>
 			    		</p>
diff --git a/snf-astakos-app/astakos/im/views.py b/snf-astakos-app/astakos/im/views.py
index 32db4c4..4938e00 100644
--- a/snf-astakos-app/astakos/im/views.py
+++ b/snf-astakos-app/astakos/im/views.py
@@ -109,7 +109,7 @@ from astakos.im.settings import (
     PAGINATE_BY_ALL,
     ACTIVATION_REDIRECT_URL,
     MODERATION_ENABLED)
-from astakos.im.presentation import RESOURCES_PRESENTATION_DATA
+from astakos.im import presentation
 from astakos.im.api import get_services_dict
 from astakos.im import settings as astakos_settings
 from astakos.im.api.callpoint import AstakosCallpoint
@@ -1042,10 +1042,74 @@ def _update_object(request, model=None, object_id=None, slug=None,
             populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname))
         return response
 
+
+def _resources_catalog(request):
+    """
+    `resource_catalog` contains a list of tuples. Each tuple contains the group
+    key the resource is assigned to and resources list of dicts that contain
+    resource information.
+    `resource_groups` contains information about the groups
+    """
+    # presentation data
+    resource_groups = presentation.RESOURCES.get('groups', {})
+    resource_catalog = ()
+
+    # resources in database
+    result = callpoint.list_resources()
+    if not result.is_success:
+        messages.error(request, 'Unable to retrieve system resources: %s' %
+                                result.reason)
+    else:
+        # initialize resource_catalog to contain all group/resource information
+        for r in result.data:
+            if not r.get('group') in resource_groups:
+                resource_groups[r.get('group')] = {'icon': 'unknown'}
+
+        resource_keys = [r.get('str_repr') for r in result.data]
+        resource_catalog = [[g, filter(lambda r: r.get('group', '') == g,
+                                       result.data)] for g in resource_groups]
+
+    # order groups, also include unknown groups
+    groups_order = presentation.RESOURCES.get('groups_order')
+    for g in resource_groups.keys():
+        if not g in groups_order:
+            groups_order.append(g)
+
+    # order resources, also include unknown resources
+    resources_order = presentation.RESOURCES.get('resources_order')
+    for r in resource_keys:
+        if not r in resources_order:
+            resources_order.append(r)
+
+    # sort catalog groups
+    resource_catalog = sorted(resource_catalog,
+                              key=lambda g: groups_order.index(g[0]))
+
+    # sort groups
+    def groupindex(g):
+        return groups_order.index(g[0])
+    resource_groups_list = sorted([(k, v) for k, v in resource_groups.items()],
+                                  key=groupindex)
+    resource_groups = OrderedDict(resource_groups_list)
+
+    # sort resources
+    def resourceindex(r):
+        return resources_order.index(r['str_repr'])
+    for index, group in enumerate(resource_catalog):
+        resource_catalog[index][1] = sorted(resource_catalog[index][1],
+                                            key=resourceindex)
+        if len(resource_catalog[index][1]) == 0:
+            resource_catalog.pop(index)
+            for gindex, g in enumerate(resource_groups):
+                if g[0] == group[0]:
+                    resource_groups.pop(gindex)
+
+    return resource_catalog, resource_groups
+
+
 @require_http_methods(["GET", "POST"])
 @valid_astakos_user_required
 def project_add(request):
-
     user = request.user
     reached, limit = reached_pending_application_limit(user.id)
     if not user.is_project_admin() and reached:
@@ -1055,42 +1119,17 @@ def project_add(request):
         next = restrict_next(next, domain=COOKIE_DOMAIN)
         return redirect(next)
 
-    resource_groups = RESOURCES_PRESENTATION_DATA.get('groups', {})
-    resource_catalog = ()
-    result = callpoint.list_resources()
-    details_fields = [
-        "name", "homepage", "description","start_date","end_date", "comments"]
-    membership_fields =[
-        "member_join_policy", "member_leave_policy", "limit_on_members_number"]
-    if not result.is_success:
-        messages.error(
-            request,
-            'Unable to retrieve system resources: %s' % result.reason
-    )
-    else:
-        resource_catalog = [
-            [g, filter(lambda r: r.get('group', '') == g, result.data)] \
-                for g in resource_groups]
-
-    # order resources
-    groups_order = RESOURCES_PRESENTATION_DATA.get('groups_order')
-    resources_order = RESOURCES_PRESENTATION_DATA.get('resources_order')
-    resource_catalog = sorted(resource_catalog, key=lambda g:groups_order.index(g[0]))
-
-    resource_groups_list = sorted([(k,v) for k,v in resource_groups.items()],
-                                  key=lambda f:groups_order.index(f[0]))
-    resource_groups = OrderedDict(resource_groups_list)
-    for index, group in enumerate(resource_catalog):
-        resource_catalog[index][1] = sorted(resource_catalog[index][1],
-                                            key=lambda r: resources_order.index(r['str_repr']))
-
-
+    details_fields = ["name", "homepage", "description", "start_date",
+                      "end_date", "comments"]
+    membership_fields = ["member_join_policy", "member_leave_policy",
+                         "limit_on_members_number"]
+    resource_catalog, resource_groups = _resources_catalog(request)
     extra_context = {
-        'resource_catalog':resource_catalog,
-        'resource_groups':resource_groups,
-        'show_form':True,
-        'details_fields':details_fields,
-        'membership_fields':membership_fields}
+        'resource_catalog': resource_catalog,
+        'resource_groups': resource_groups,
+        'show_form': True,
+        'details_fields': details_fields,
+        'membership_fields': membership_fields}
 
     response = None
     with ExceptionHandler(request):
@@ -1186,41 +1225,31 @@ def project_modify(request, application_id):
         next = restrict_next(next, domain=COOKIE_DOMAIN)
         return redirect(next)
 
-    resource_groups = RESOURCES_PRESENTATION_DATA.get('groups', {})
-    resource_catalog = ()
-    result = callpoint.list_resources()
-    details_fields = [
-        "name", "homepage", "description","start_date","end_date", "comments"]
-    membership_fields =[
-        "member_join_policy", "member_leave_policy", "limit_on_members_number"]
-    if not result.is_success:
-        messages.error(
-            request,
-            'Unable to retrieve system resources: %s' % result.reason
-    )
-    else:
-        resource_catalog = [
-            (g, filter(lambda r: r.get('group', '') == g, result.data)) \
-                for g in resource_groups]
+    details_fields = ["name", "homepage", "description", "start_date",
+                      "end_date", "comments"]
+    membership_fields = ["member_join_policy", "member_leave_policy",
+                         "limit_on_members_number"]
+    resource_catalog, resource_groups = _resources_catalog(request)
     extra_context = {
-        'resource_catalog':resource_catalog,
-        'resource_groups':resource_groups,
-        'show_form':True,
-        'details_fields':details_fields,
+        'resource_catalog': resource_catalog,
+        'resource_groups': resource_groups,
+        'show_form': True,
+        'details_fields': details_fields,
         'update_form': True,
-        'membership_fields':membership_fields}
+        'membership_fields': membership_fields
+    }
 
     response = None
     with ExceptionHandler(request):
-        response =_update_object(
+        response = _update_object(
             request,
             object_id=application_id,
             template_name='im/projects/projectapplication_form.html',
-            extra_context=extra_context, post_save_redirect=reverse('project_list'),
+            extra_context=extra_context,
+            post_save_redirect=reverse('project_list'),
             form_class=ProjectApplicationForm,
-            msg = _("The %(verbose_name)s has been received and "
-                    "is under consideration."),
-            )
+            msg=_("The %(verbose_name)s has been received and is under "
+                  "consideration."))
 
     if response is not None:
         return response
